diff --git a/example_scenes.py b/example_scenes.py index 666faa42..e22e94c9 100644 --- a/example_scenes.py +++ b/example_scenes.py @@ -67,48 +67,6 @@ class OpeningManimExample(Scene): self.wait(2) -class InteractiveDevlopment(Scene): - def construct(self): - circle = Circle() - circle.set_fill(BLUE, opacity=0.5) - circle.set_stroke(BLUE_E, width=4) - square = Square() - - self.play(ShowCreation(square)) - self.wait() - - # This opens an iPython termnial where you can keep writing - # lines as if they were part of this construct method - self.embed() - # Try copying and pasting some of the lines below into - # the interactive shell - self.play(ReplacementTransform(square, circle)) - self.wait() - self.play(circle.stretch, 4, 0) - self.play(Rotate(circle, 90 * DEGREES)) - self.play(circle.shift, 2 * RIGHT, circle.scale, 0.25) - - text = Text(""" - In general, using the interactive shell - is very helpful when developing new scenes - """) - self.play(Write(text)) - - # In the interactive shell, you can just type - # play, add, remove, clear, wait, save_state and restore, - # instead of self.play, self.add, self.remove, etc. - - # To interact with the window, type touch(). You can then - # scroll in the window, or zoom by holding down 'z' while scrolling, - # and change camera perspective by holding down 'd' while moving - # the mouse. Press 'r' to reset to the standard camera position. - # Press 'q' to stop interacting with the window and go back to - # typing new commands into the shell. - - # In principle you can customize a scene - always(circle.move_to, self.mouse_point) - - class AnimatingMethods(Scene): def construct(self): grid = Tex(r"\pi").get_grid(10, 10, height=4) @@ -121,7 +79,10 @@ class AnimatingMethods(Scene): # to the left, but the following line animates that motion. self.play(grid.shift, 2 * LEFT) # The same applies for any method, including those setting colors. + self.play(grid.set_color, YELLOW) + self.wait() self.play(grid.set_submobject_colors_by_gradient, BLUE, GREEN) + self.wait() self.play(grid.set_height, TAU - MED_SMALL_BUFF) self.wait() @@ -361,6 +322,166 @@ class UpdatersExample(Scene): self.wait(4 * PI) +class GraphExample(Scene): + def construct(self): + axes = Axes((-3, 10), (-1, 8)) + axes.add_coordinate_labels() + + self.play(Write(axes, lag_ratio=0.01, run_time=1)) + + # Axes.get_graph will return the graph of a function + sin_graph = axes.get_graph( + lambda x: 2 * math.sin(x), + color=BLUE, + ) + # By default, it draws it so as to somewhat smoothly interpolate + # between sampled points (x, f(x)). If the graph is meant to have + # a corner, though, you can set use_smoothing to False + relu_graph = axes.get_graph( + lambda x: max(x, 0), + use_smoothing=False, + color=YELLOW, + ) + # For discontinuous functions, you can specify the point of + # discontinuity so that it does not try to draw over the gap. + step_graph = axes.get_graph( + lambda x: 2.0 if x > 3 else 1.0, + discontinuities=[3], + color=GREEN, + ) + + # Axes.get_graph_label takes in either a string or a mobject. + # If it's a string, it treats it as a LaTeX expression. By default + # it places the label next to the graph near the right side, and + # has it match the color of the graph + sin_label = axes.get_graph_label(sin_graph, "\\sin(x)") + relu_label = axes.get_graph_label(relu_graph, Text("ReLU")) + step_label = axes.get_graph_label(step_graph, Text("Step"), x=4) + + self.play( + ShowCreation(sin_graph), + FadeIn(sin_label, RIGHT), + ) + self.wait(2) + self.play( + ReplacementTransform(sin_graph, relu_graph), + FadeTransform(sin_label, relu_label), + ) + self.wait() + self.play( + ReplacementTransform(relu_graph, step_graph), + FadeTransform(relu_label, step_label), + ) + self.wait() + + parabola = axes.get_graph(lambda x: 0.25 * x**2) + parabola.set_stroke(BLUE) + self.play( + FadeOut(step_graph), + FadeOut(step_label), + ShowCreation(parabola) + ) + self.wait() + + # You can use axes.input_to_graph_point, abbreviated + # to axes.i2gp, to find a particular point on a graph + dot = Dot(color=RED) + dot.move_to(axes.i2gp(2, parabola)) + self.play(FadeIn(dot, scale=0.5)) + + # A value tracker lets us animate a parameter, usually + # with the intent of having other mobjects update based + # on the parameter + x_tracker = ValueTracker(2) + f_always( + dot.move_to, + lambda: axes.i2gp(x_tracker.get_value(), parabola) + ) + + self.play(x_tracker.set_value, 4, run_time=3) + self.play(x_tracker.set_value, -2, run_time=3) + self.wait() + + +class CoordinateSystemExample(Scene): + def construct(self): + axes = Axes( + # x-axis ranges from -1 to 10, with a default step size of 1 + x_range=(-1, 10), + # y-axis ranges from -2 to 10 with a step size of 0.5 + y_range=(-2, 2, 0.5), + # The axes will be stretched so as to match the specified + # height and width + height=6, + width=10, + # Axes is made of two NumberLine mobjects. You can specify + # their configuration with axis_config + axis_config={ + "stroke_color": GREY_A, + "stroke_width": 2, + }, + # Alternatively, you can specify configuration for just one + # of them, like this. + y_axis_config={ + "include_tip": False, + } + ) + # Keyword arguments of add_coordinate_labels can be used to + # configure the DecimalNumber mobjects which it creates and + # adds to the axes + axes.add_coordinate_labels( + font_size=20, + num_decimal_places=1, + ) + self.add(axes) + + # Axes descends from the CoordinateSystem class, meaning + # you can call call axes.coords_to_point, abbreviated to + # axes.c2p, to associate a set of coordinates with a point, + # like so: + dot = Dot(color=RED) + dot.move_to(axes.c2p(0, 0)) + self.play(FadeIn(dot, scale=0.5)) + self.play(dot.move_to, axes.c2p(3, 2)) + self.wait() + self.play(dot.move_to, axes.c2p(5, 0.5)) + self.wait() + + # Similarly, you can call axes.point_to_coords, or axes.p2c + # print(axes.p2c(dot.get_center())) + + # We can draw lines from the axes to better mark the coordinates + # of a given point. + # Here, the always_redraw command means that on each new frame + # the lines will be redrawn + h_line = always_redraw(lambda: axes.get_h_line(dot.get_left())) + v_line = always_redraw(lambda: axes.get_v_line(dot.get_bottom())) + + self.play( + ShowCreation(h_line), + ShowCreation(v_line), + ) + self.play(dot.move_to, axes.c2p(3, -2)) + self.wait() + self.play(dot.move_to, axes.c2p(1, 1)) + self.wait() + + # If we tie the dot to a particular set of coordinates, notice + # that as we move the axes around it respects the coordinate + # system defined by them. + f_always(dot.move_to, lambda: axes.c2p(1, 1)) + self.play( + axes.scale, 0.75, + axes.to_corner, UL, + run_time=2, + ) + self.wait() + self.play(FadeOut(VGroup(axes, dot, h_line, v_line))) + + # Other coordinate systems you can play around with include + # ThreeDAxes, NumberPlane, and ComplexPlane. + + class SurfaceExample(Scene): CONFIG = { "camera_class": ThreeDCamera, @@ -452,6 +573,52 @@ class SurfaceExample(Scene): self.wait() +class InteractiveDevlopment(Scene): + def construct(self): + circle = Circle() + circle.set_fill(BLUE, opacity=0.5) + circle.set_stroke(BLUE_E, width=4) + square = Square() + + self.play(ShowCreation(square)) + self.wait() + + # This opens an iPython termnial where you can keep writing + # lines as if they were part of this construct method. + # In particular, 'square', 'circle' and 'self' will all be + # part of the local namespace in that terminal. + self.embed() + + # Try copying and pasting some of the lines below into + # the interactive shell + self.play(ReplacementTransform(square, circle)) + self.wait() + self.play(circle.stretch, 4, 0) + self.play(Rotate(circle, 90 * DEGREES)) + self.play(circle.shift, 2 * RIGHT, circle.scale, 0.25) + + text = Text(""" + In general, using the interactive shell + is very helpful when developing new scenes + """) + self.play(Write(text)) + + # In the interactive shell, you can just type + # play, add, remove, clear, wait, save_state and restore, + # instead of self.play, self.add, self.remove, etc. + + # To interact with the window, type touch(). You can then + # scroll in the window, or zoom by holding down 'z' while scrolling, + # and change camera perspective by holding down 'd' while moving + # the mouse. Press 'r' to reset to the standard camera position. + # Press 'q' to stop interacting with the window and go back to + # typing new commands into the shell. + + # In principle you can customize a scene to be responsive to + # mouse and keyboard interactions + always(circle.move_to, self.mouse_point) + + class ControlsExample(Scene): def setup(self): self.textbox = Textbox()