123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734 |
- from manimlib import *
- import numpy as np
- # To watch one of these scenes, run the following:
- # manimgl example_scenes.py OpeningManimExample
- # Use -s to skip to the end and just save the final frame
- # Use -w to write the animation to a file
- # Use -o to write it to a file and open it once done
- # Use -n <number> to skip ahead to the n'th animation of a scene.
- class OpeningManimExample(Scene):
- def construct(self):
- intro_words = Text("""
- The original motivation for manim was to
- better illustrate mathematical functions
- as transformations.
- """)
- intro_words.to_edge(UP)
- self.play(Write(intro_words))
- self.wait(2)
- # Linear transform
- grid = NumberPlane((-10, 10), (-5, 5))
- matrix = [[1, 1], [0, 1]]
- linear_transform_words = VGroup(
- Text("This is what the matrix"),
- IntegerMatrix(matrix),
- Text("looks like")
- )
- linear_transform_words.arrange(RIGHT)
- linear_transform_words.to_edge(UP)
- linear_transform_words.set_backstroke(width=5)
- self.play(
- ShowCreation(grid),
- FadeTransform(intro_words, linear_transform_words)
- )
- self.wait()
- self.play(grid.animate.apply_matrix(matrix), run_time=3)
- self.wait()
- # Complex map
- c_grid = ComplexPlane()
- moving_c_grid = c_grid.copy()
- moving_c_grid.prepare_for_nonlinear_transform()
- c_grid.set_stroke(BLUE_E, 1)
- c_grid.add_coordinate_labels(font_size=24)
- complex_map_words = TexText("""
- Or thinking of the plane as $\\mathds{C}$,\\\\
- this is the map $z \\rightarrow z^2$
- """)
- complex_map_words.to_corner(UR)
- complex_map_words.set_backstroke(width=5)
- self.play(
- FadeOut(grid),
- Write(c_grid, run_time=3),
- FadeIn(moving_c_grid),
- FadeTransform(linear_transform_words, complex_map_words),
- )
- self.wait()
- self.play(
- moving_c_grid.animate.apply_complex_function(lambda z: z**2),
- run_time=6,
- )
- self.wait(2)
- class AnimatingMethods(Scene):
- def construct(self):
- grid = Tex(R"\pi").get_grid(10, 10, height=4)
- self.add(grid)
- # You can animate the application of mobject methods with the
- # ".animate" syntax:
- self.play(grid.animate.shift(LEFT))
- # Both of those will interpolate between the mobject's initial
- # state and whatever happens when you apply that method.
- # For this example, calling grid.shift(LEFT) would shift the
- # grid one unit to the left, but both of the previous calls to
- # "self.play" animate that motion.
- # The same applies for any method, including those setting colors.
- self.play(grid.animate.set_color(YELLOW))
- self.wait()
- self.play(grid.animate.set_submobject_colors_by_gradient(BLUE, GREEN))
- self.wait()
- self.play(grid.animate.set_height(TAU - MED_SMALL_BUFF))
- self.wait()
- # The method Mobject.apply_complex_function lets you apply arbitrary
- # complex functions, treating the points defining the mobject as
- # complex numbers.
- self.play(grid.animate.apply_complex_function(np.exp), run_time=5)
- self.wait()
- # Even more generally, you could apply Mobject.apply_function,
- # which takes in functions form R^3 to R^3
- self.play(
- grid.animate.apply_function(
- lambda p: [
- p[0] + 0.5 * math.sin(p[1]),
- p[1] + 0.5 * math.sin(p[0]),
- p[2]
- ]
- ),
- run_time=5,
- )
- self.wait()
- class TextExample(Scene):
- def construct(self):
- # To run this scene properly, you should have "Consolas" font in your computer
- # for full usage, you can see https://github.com/3b1b/manim/pull/680
- text = Text("Here is a text", font="Consolas", font_size=90)
- difference = Text(
- """
- The most important difference between Text and TexText is that\n
- you can change the font more easily, but can't use the LaTeX grammar
- """,
- font="Arial", font_size=24,
- # t2c is a dict that you can choose color for different text
- t2c={"Text": BLUE, "TexText": BLUE, "LaTeX": ORANGE}
- )
- VGroup(text, difference).arrange(DOWN, buff=1)
- self.play(Write(text))
- self.play(FadeIn(difference, UP))
- self.wait(3)
- fonts = Text(
- "And you can also set the font according to different words",
- font="Arial",
- t2f={"font": "Consolas", "words": "Consolas"},
- t2c={"font": BLUE, "words": GREEN}
- )
- fonts.set_width(FRAME_WIDTH - 1)
- slant = Text(
- "And the same as slant and weight",
- font="Consolas",
- t2s={"slant": ITALIC},
- t2w={"weight": BOLD},
- t2c={"slant": ORANGE, "weight": RED}
- )
- VGroup(fonts, slant).arrange(DOWN, buff=0.8)
- self.play(FadeOut(text), FadeOut(difference, shift=DOWN))
- self.play(Write(fonts))
- self.wait()
- self.play(Write(slant))
- self.wait()
- class TexTransformExample(Scene):
- def construct(self):
- # Tex to color map
- t2c = {
- "A": BLUE,
- "B": TEAL,
- "C": GREEN,
- }
- # Configuration to pass along to each Tex mobject
- kw = dict(font_size=72, t2c=t2c)
- lines = VGroup(
- Tex("A^2 + B^2 = C^2", **kw),
- Tex("A^2 = C^2 - B^2", **kw),
- Tex("A^2 = (C + B)(C - B)", **kw),
- Tex(R"A = \sqrt{(C + B)(C - B)}", **kw),
- )
- lines.arrange(DOWN, buff=LARGE_BUFF)
- self.add(lines[0])
- # The animation TransformMatchingStrings will line up parts
- # of the source and target which have matching substring strings.
- # Here, giving it a little path_arc makes each part rotate into
- # their final positions, which feels appropriate for the idea of
- # rearranging an equation
- self.play(
- TransformMatchingStrings(
- lines[0].copy(), lines[1],
- # matched_keys specifies which substring should
- # line up. If it's not specified, the animation
- # will align the longest matching substrings.
- # In this case, the substring "^2 = C^2" would
- # trip it up
- matched_keys=["A^2", "B^2", "C^2"],
- # When you want a substring from the source
- # to go to a non-equal substring from the target,
- # use the key map.
- key_map={"+": "-"},
- path_arc=90 * DEGREES,
- ),
- )
- self.wait()
- self.play(TransformMatchingStrings(
- lines[1].copy(), lines[2],
- matched_keys=["A^2"]
- ))
- self.wait()
- self.play(
- TransformMatchingStrings(
- lines[2].copy(), lines[3],
- key_map={"2": R"\sqrt"},
- path_arc=-30 * DEGREES,
- ),
- )
- self.wait(2)
- self.play(LaggedStartMap(FadeOut, lines, shift=2 * RIGHT))
- # TransformMatchingShapes will try to line up all pieces of a
- # source mobject with those of a target, regardless of the
- # what Mobject type they are.
- source = Text("the morse code", height=1)
- target = Text("here come dots", height=1)
- saved_source = source.copy()
- self.play(Write(source))
- self.wait()
- kw = dict(run_time=3, path_arc=PI / 2)
- self.play(TransformMatchingShapes(source, target, **kw))
- self.wait()
- self.play(TransformMatchingShapes(target, saved_source, **kw))
- self.wait()
- class TexIndexing(Scene):
- def construct(self):
- # You can index into Tex mobject (or other StringMobjects) by substrings
- equation = Tex(R"e^{\pi i} = -1", font_size=144)
- self.add(equation)
- self.play(FlashAround(equation["e"]))
- self.wait()
- self.play(Indicate(equation[R"\pi"]))
- self.wait()
- self.play(TransformFromCopy(
- equation[R"e^{\pi i}"].copy().set_opacity(0.5),
- equation["-1"],
- path_arc=-PI / 2,
- run_time=3
- ))
- self.play(FadeOut(equation))
- # Or regular expressions
- equation = Tex("A^2 + B^2 = C^2", font_size=144)
- self.play(Write(equation))
- for part in equation[re.compile(r"\w\^2")]:
- self.play(FlashAround(part))
- self.wait()
- self.play(FadeOut(equation))
-
- # Indexing by substrings like this may not work when
- # the order in which Latex draws symbols does not match
- # the order in which they show up in the string.
- # For example, here the infinity is drawn before the sigma
- # so we don't get the desired behavior.
- equation = Tex(R"\sum_{n = 1}^\infty \frac{1}{n^2} = \frac{\pi^2}{6}", font_size=72)
- self.play(FadeIn(equation))
- self.play(equation[R"\infty"].animate.set_color(RED)) # Doesn't hit the infinity
- self.wait()
- self.play(FadeOut(equation))
- # However you can always fix this by explicitly passing in
- # a string you might want to isolate later. Also, using
- # \over instead of \frac helps to avoid the issue for fractions
- equation = Tex(
- R"\sum_{n = 1}^\infty {1 \over n^2} = {\pi^2 \over 6}",
- # Explicitly mark "\infty" as a substring you might want to access
- isolate=[R"\infty"],
- font_size=72
- )
- self.play(FadeIn(equation))
- self.play(equation[R"\infty"].animate.set_color(RED)) # Got it!
- self.wait()
- self.play(FadeOut(equation))
- class UpdatersExample(Scene):
- def construct(self):
- square = Square()
- square.set_fill(BLUE_E, 1)
- # On all frames, the constructor Brace(square, UP) will
- # be called, and the mobject brace will set its data to match
- # that of the newly constructed object
- brace = always_redraw(Brace, square, UP)
- label = TexText("Width = 0.00")
- number = label.make_number_changeable("0.00")
- # This ensures that the method deicmal.next_to(square)
- # is called on every frame
- label.always.next_to(brace, UP)
- # You could also write the following equivalent line
- # label.add_updater(lambda m: m.next_to(brace, UP))
- # If the argument itself might change, you can use f_always,
- # for which the arguments following the initial Mobject method
- # should be functions returning arguments to that method.
- # The following line ensures thst decimal.set_value(square.get_y())
- # is called every frame
- number.f_always.set_value(square.get_width)
- # You could also write the following equivalent line
- # number.add_updater(lambda m: m.set_value(square.get_width()))
- self.add(square, brace, label)
- # Notice that the brace and label track with the square
- self.play(
- square.animate.scale(2),
- rate_func=there_and_back,
- run_time=2,
- )
- self.wait()
- self.play(
- square.animate.set_width(5, stretch=True),
- run_time=3,
- )
- self.wait()
- self.play(
- square.animate.set_width(2),
- run_time=3
- )
- self.wait()
- # In general, you can alway call Mobject.add_updater, and pass in
- # a function that you want to be called on every frame. The function
- # should take in either one argument, the mobject, or two arguments,
- # the mobject and the amount of time since the last frame.
- now = self.time
- w0 = square.get_width()
- square.add_updater(
- lambda m: m.set_width(w0 * math.sin(self.time - now) + w0)
- )
- self.wait(4 * PI)
- 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 2 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=dict(
- stroke_color=GREY_A,
- stroke_width=2,
- numbers_to_exclude=[0],
- ),
- # Alternatively, you can specify configuration for just one
- # of them, like this.
- y_axis_config=dict(
- big_tick_numbers=[-2, 2],
- )
- )
- # 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.animate.move_to(axes.c2p(3, 2)))
- self.wait()
- self.play(dot.animate.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.animate.move_to(axes.c2p(3, -2)))
- self.wait()
- self.play(dot.animate.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.animate.scale(0.75).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 GraphExample(Scene):
- def construct(self):
- axes = Axes((-3, 10), (-1, 8), height=6)
- 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)
- dot.add_updater(lambda d: d.move_to(axes.i2gp(x_tracker.get_value(), parabola)))
- self.play(x_tracker.animate.set_value(4), run_time=3)
- self.play(x_tracker.animate.set_value(-2), run_time=3)
- self.wait()
- class TexAndNumbersExample(Scene):
- def construct(self):
- axes = Axes((-3, 3), (-3, 3), unit_size=1)
- axes.to_edge(DOWN)
- axes.add_coordinate_labels(font_size=16)
- circle = Circle(radius=2)
- circle.set_stroke(YELLOW, 3)
- circle.move_to(axes.get_origin())
- self.add(axes, circle)
- # When numbers show up in tex, they can be readily
- # replaced with DecimalMobjects so that methods like
- # get_value and set_value can be called on them, and
- # animations like ChangeDecimalToValue can be called
- # on them.
- tex = Tex("x^2 + y^2 = 4.00")
- tex.next_to(axes, UP, buff=0.5)
- value = tex.make_number_changeable("4.00")
- # This will tie the right hand side of our equation to
- # the square of the radius of the circle
- value.add_updater(lambda v: v.set_value(circle.get_radius()**2))
- self.add(tex)
- text = Text("""
- You can manipulate numbers
- in Tex mobjects
- """, font_size=30)
- text.next_to(tex, RIGHT, buff=1.5)
- arrow = Arrow(text, tex)
- self.add(text, arrow)
- self.play(
- circle.animate.set_height(2.0),
- run_time=4,
- rate_func=there_and_back,
- )
- # By default, tex.make_number_changeable replaces the first occurance
- # of the number,but by passing replace_all=True it replaces all and
- # returns a group of the results
- exponents = tex.make_number_changeable("2", replace_all=True)
- self.play(
- LaggedStartMap(
- FlashAround, exponents,
- lag_ratio=0.2, buff=0.1, color=RED
- ),
- exponents.animate.set_color(RED)
- )
- def func(x, y):
- # Switch from manim coords to axes coords
- xa, ya = axes.point_to_coords(np.array([x, y, 0]))
- return xa**4 + ya**4 - 4
- new_curve = ImplicitFunction(func)
- new_curve.match_style(circle)
- circle.rotate(angle_of_vector(new_curve.get_start())) # Align
- value.clear_updaters()
- self.play(
- *(ChangeDecimalToValue(exp, 4) for exp in exponents),
- ReplacementTransform(circle.copy(), new_curve),
- circle.animate.set_stroke(width=1, opacity=0.5),
- )
- class SurfaceExample(ThreeDScene):
- def construct(self):
- surface_text = Text("For 3d scenes, try using surfaces")
- surface_text.fix_in_frame()
- surface_text.to_edge(UP)
- self.add(surface_text)
- self.wait(0.1)
- torus1 = Torus(r1=1, r2=1)
- torus2 = Torus(r1=3, r2=1)
- sphere = Sphere(radius=3, resolution=torus1.resolution)
- # You can texture a surface with up to two images, which will
- # be interpreted as the side towards the light, and away from
- # the light. These can be either urls, or paths to a local file
- # in whatever you've set as the image directory in
- # the custom_config.yml file
- # day_texture = "EarthTextureMap"
- # night_texture = "NightEarthTextureMap"
- day_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
- night_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg"
- surfaces = [
- TexturedSurface(surface, day_texture, night_texture)
- for surface in [sphere, torus1, torus2]
- ]
- for mob in surfaces:
- mob.shift(IN)
- mob.mesh = SurfaceMesh(mob)
- mob.mesh.set_stroke(BLUE, 1, opacity=0.5)
- surface = surfaces[0]
- self.play(
- FadeIn(surface),
- ShowCreation(surface.mesh, lag_ratio=0.01, run_time=3),
- )
- for mob in surfaces:
- mob.add(mob.mesh)
- surface.save_state()
- self.play(Rotate(surface, PI / 2), run_time=2)
- for mob in surfaces[1:]:
- mob.rotate(PI / 2)
- self.play(
- Transform(surface, surfaces[1]),
- run_time=3
- )
- self.play(
- Transform(surface, surfaces[2]),
- # Move camera frame during the transition
- self.frame.animate.increment_phi(-10 * DEGREES),
- self.frame.animate.increment_theta(-20 * DEGREES),
- run_time=3
- )
- # Add ambient rotation
- self.frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))
- # Play around with where the light is
- light_text = Text("You can move around the light source")
- light_text.move_to(surface_text)
- light_text.fix_in_frame()
- self.play(FadeTransform(surface_text, light_text))
- light = self.camera.light_source
- light_dot = GlowDot(color=WHITE, radius=0.5)
- light_dot.always.move_to(light)
- self.add(light, light_dot)
- light.save_state()
- self.play(light.animate.move_to(3 * IN), run_time=5)
- self.play(light.animate.shift(10 * OUT), run_time=5)
- drag_text = Text("Try moving the mouse while pressing d or f")
- drag_text.move_to(light_text)
- drag_text.fix_in_frame()
- self.play(FadeTransform(light_text, drag_text))
- self.wait()
- class InteractiveDevelopment(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 terminal 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.animate.stretch(4, 0))
- self.play(Rotate(circle, 90 * DEGREES))
- self.play(circle.animate.shift(2 * RIGHT).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):
- drag_to_pan = False
- def setup(self):
- self.textbox = Textbox()
- self.checkbox = Checkbox()
- self.color_picker = ColorSliders()
- self.panel = ControlPanel(
- Text("Text", font_size=24), self.textbox, Line(),
- Text("Show/Hide Text", font_size=24), self.checkbox, Line(),
- Text("Color of Text", font_size=24), self.color_picker
- )
- self.add(self.panel)
- def construct(self):
- text = Text("text", font_size=96)
- def text_updater(old_text):
- assert(isinstance(old_text, Text))
- new_text = Text(self.textbox.get_value(), font_size=old_text.font_size)
- # new_text.align_data_and_family(old_text)
- new_text.move_to(old_text)
- if self.checkbox.get_value():
- new_text.set_fill(
- color=self.color_picker.get_picked_color(),
- opacity=self.color_picker.get_picked_opacity()
- )
- else:
- new_text.set_opacity(0)
- old_text.become(new_text)
- text.add_updater(text_updater)
- self.add(MotionMobject(text))
- self.textbox.set_value("Manim")
- # self.wait(60)
- # self.embed()
- # See https://github.com/3b1b/videos for many, many more
|