def construct_def_box(self): """Construct the function's definition in a box""" fact = self.get_def() rect = ThinRectangle().surround(fact, stretch=True) self.play(m.Write(fact)) self.play(m.ShowCreation(rect)) self.def_box = m.VDict([("function", fact), ("box", rect)]) self.def_box.generate_target().scale(self.def_scale_ratio).to_corner( m.UL) self.play(m.MoveToTarget(self.def_box))
def construct_def_box(self) -> None: """Show the creation of the OCaml definiton, with a box""" # Create the function definition and its rectangular box self.def_box = m.VDict([("function", self.get_def())]) self.def_box.add([ ("box", ThinRectangle(height=1.5).surround(self.def_box["function"])) ]) # Animate the creations self.play(m.Write(self.def_box["function"])) self.play(m.ShowCreation(self.def_box["box"])) # Move and scale down to up-left corner of the scene self.def_box.generate_target() self.def_box.target.scale(self.def_scale_ratio).to_corner(m.UL) self.play(m.MoveToTarget(self.def_box))
def add( self, name: str, val_orig: m.Mobject, highlight: m.Mobject = None, extra_animations: List[m.Animation] = None, ) -> None: """Add an name-value association to the context and return animations name -- the name part of the association val_orig -- a Mobject that is the value part of the association highlight -- a Mobject that should be highlighted before other animations are played, defaults to val_orig extra_animations -- animations to be played along the others during the last step This creates a copy of `val_orig` and sets its target to the position of the value in the context, the returned animations will make the copy move to this position. """ self.scene.play(m.Indicate(highlight if highlight else val_orig)) association = m.VDict([ ("name", m.Tex(name, color=m.GRAY)), ("eq", m.Tex("=", color=m.GRAY)), ("val", val_orig.copy()), ]) association["name"].move_to(self.entries[-1], aligned_edge=m.LEFT) association["eq"].next_to(association["name"], m.RIGHT) association["val"].generate_target().next_to(association["eq"], m.RIGHT).set_color(m.GRAY) self.scene.play( m.ApplyMethod(self.entries.shift, m.UP * 0.5), m.FadeInFrom(association["name"], direction=m.DOWN), m.FadeInFrom(association["eq"], direction=m.DOWN), m.MoveToTarget(association["val"]), *(extra_animations if extra_animations else []), ) self.entries.add(association) self.scene.remove(association) # The previous line created a copy
def construct_call(self, val: int) -> None: """Show how a call to the function with the given argument is evaluated""" call = m.VDict([ ("name", m.Tex("\\verb|fact|").shift(m.LEFT * 5 + m.DOWN * 0.7)), ("val", m.Tex(f"\\verb|{val}|")), ]) call["val"].next_to(call["name"], m.RIGHT, aligned_edge=m.UP) self.play(m.FadeIn(call)) # Show the first call def_instance = deepcopy(self.def_box["function"]).remove("fn") def_instance.generate_target().scale(1 / self.def_scale_ratio).next_to( call, m.RIGHT * 5) lines = m.VGroup( ThinLine( call.get_corner(m.RIGHT) + m.RIGHT * 0.2, def_instance.target.get_corner(m.LEFT) + m.LEFT * 0.2, color=m.BLUE, ), ThinLine( def_instance.target.get_corner(m.DL) + m.LEFT * 0.2, def_instance.target.get_corner(m.UL) + m.LEFT * 0.2, color=m.BLUE, ), ) self.wait() self.play(m.Indicate(self.def_box)) self.play( m.MoveToTarget(def_instance), m.ShowCreation(lines), ) self.wait() _, res_mobject = self.eval_call(def_instance, val, call["val"]) self.play(m.FadeOut(call), m.FadeOut(lines), m.ApplyMethod(res_mobject.center))
def construct_call(self, val: int) -> None: """Show how a call to the OCaml function is evaluated val: the argument given to square_of_pred """ call = m.VDict([ ("name", m.Tex("\\verb|square_of_pred|").shift(m.LEFT * 2)), ("val", m.Tex(f"\\verb|{val}|")), ]) call["val"].next_to(call["name"], m.RIGHT, aligned_edge=m.UP) self.play(m.FadeIn(call)) # Shift the call and show instanciated definition of the function def_instance = deepcopy(self.def_box["function"]).remove("fn") def_instance.generate_target().scale(1 / self.def_scale_ratio).next_to( call, m.RIGHT * 5) lines = m.VGroup( ThinLine( call.get_corner(m.RIGHT) + m.RIGHT * 0.2, def_instance.target.get_corner(m.LEFT) + m.LEFT * 0.2, color=m.BLUE, ), ThinLine( def_instance.target.get_corner(m.DL) + m.LEFT * 0.2, def_instance.target.get_corner(m.UL) + m.LEFT * 0.2, color=m.BLUE, ), ) self.wait() self.play(m.Indicate(self.def_box)) self.play( m.MoveToTarget(def_instance), m.ShowCreation(lines), ) self.wait() # Show context context = CallContext(def_instance, self) # add x=val to context context.add("x", call["val"]) # Replace x by its value self.wait() context.replace_occurrence(-1, def_instance["pred"]["def"]["val"]["x"]) self.wait() # Evaluate pred_x replace_expr(self, def_instance["pred"]["def"]["val"], f"\\verb|{val-1}|") self.wait() # add pred_x=val-1 to context context.add( "pred\\_x", def_instance["pred"]["def"]["val"], highlight=def_instance["pred"], extra_animations=[ m.FadeOutAndShift(def_instance["pred"], direction=m.UP), m.ApplyMethod(def_instance["res"].next_to, lines[1], m.RIGHT), ], ) self.wait() # Replace pred_x by its value context.replace_occurrence(-1, def_instance["res"]["op2"]) context.replace_occurrence(-1, def_instance["res"]["op1"]) # Evaluate the result self.wait() replace_expr( self, def_instance["res"], f"\\verb|{(val-1) * (val-1)}|", aligned_edge=m.LEFT, ) # Wrap up self.wait() self.play( m.FadeOut(context.entries), m.FadeOut(call), m.ApplyMethod(def_instance["res"].center), m.Uncreate(lines), )
def eval_call(self, call: m.Mobject, val: int, val_mobject: m.Mobject) -> Tuple[int, m.Mobject]: """Show the evaluation of one function call, recursively call: the manim object representing the call, expected to be oneline val: the value of `n` for that call Returns the value and the corresponding Mobject of the result of the call """ context = CallContext(call, self) context.add("n", val_mobject) self.wait() # Evaluate the if's condition context.replace_occurrence(-1, call["if"]["cond"]["n"]) self.wait() replace_expr(self, call["if"]["cond"], f"\\verb|{str(val == 0).lower()}|") # Replace the expression with the correct if branch self.wait() self.play(m.Indicate(call["if"]["cond"])) if val == 0: bad = "rec" good = "base" else: good = "rec" bad = "base" strike_through = ThinLine( call[bad].get_corner(m.DL) + m.DL * 0.1, call[bad].get_corner(m.UR) + m.UR * 0.1, ) rect = ThinRectangle().surround(call[good], stretch=True) self.play(m.ShowCreation(strike_through), m.ShowCreation(rect)) self.wait() self.play( *map(m.FadeOut, [strike_through, rect, call["if"], call[bad], call["else"]])) self.play( m.ApplyMethod(call[good].next_to, call.get_corner(m.LEFT), m.RIGHT * 0.1)) if val == 0: # end of recursion self.play(*map(m.FadeOut, context.entries)) return 1, call["base"] # Evaluate the expression containing the recursive call up to the call, starting # by the right-hand operand of the multiplication self.wait() self.play(m.Indicate(call["rec"]["call"])) context.replace_occurrence(-1, call["rec"]["call"]["arg"]["n"]) self.wait(m.DEFAULT_WAIT_TIME / 2) replace_expr(self, call["rec"]["call"]["arg"], f"\\verb|{val - 1}|", aligned_edge=m.LEFT) # Evaluate the recursive call rect = ThinRectangle(color=m.BLUE).surround(call["rec"]["call"], stretch=True) def_instance = deepcopy(self.def_box["function"]).remove("fn") def_instance.generate_target().scale(1 / self.def_scale_ratio).next_to( rect, m.RIGHT * 5) lines = m.VGroup( ThinLine( rect.get_corner(m.RIGHT), def_instance.target.get_corner(m.LEFT) + m.LEFT * 0.2, color=m.BLUE, ), ThinLine( def_instance.target.get_corner(m.DL) + m.LEFT * 0.2, def_instance.target.get_corner(m.UL) + m.LEFT * 0.2, color=m.BLUE, ), ) self.play(m.ShowCreation(rect)) self.wait(m.DEFAULT_WAIT_TIME / 2) self.play(m.Indicate(self.def_box)) self.play(m.MoveToTarget(def_instance), m.ShowCreation(lines)) self.wait() shift_vector = (call["rec"]["call"]["arg"].get_center() - val_mobject.get_center()) self.play( m.ApplyMethod(self.camera.frame.shift, shift_vector), m.ApplyMethod(self.def_box.shift, shift_vector), ) recursive_call_res, recursive_call_res_mobject = self.eval_call( def_instance, val - 1, call["rec"]["call"]["arg"]) self.play( m.ApplyMethod(self.camera.frame.shift, -shift_vector), m.ApplyMethod(self.def_box.shift, -shift_vector), ) self.play( m.FadeOut(lines), m.FadeOut(rect), m.FadeOut(call["rec"]["call"]), m.ApplyMethod(recursive_call_res_mobject.next_to, call["rec"]["*"], m.RIGHT), ) self.wait() call["rec"]["call"] = recursive_call_res_mobject # Evaluate the left-hand operand of the multiplication context.replace_occurrence(-1, call["rec"]["n"]) res = val * recursive_call_res replace_expr(self, call["rec"], f"\\verb|{res}|", aligned_edge=m.LEFT) self.play(*map(m.FadeOut, context.entries)) return res, call["rec"]