example_scenes.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. from manimlib import *
  2. import numpy as np
  3. # To watch one of these scenes, run the following:
  4. # manimgl example_scenes.py OpeningManimExample
  5. # Use -s to skip to the end and just save the final frame
  6. # Use -w to write the animation to a file
  7. # Use -o to write it to a file and open it once done
  8. # Use -n <number> to skip ahead to the n'th animation of a scene.
  9. class OpeningManimExample(Scene):
  10. def construct(self):
  11. intro_words = Text("""
  12. The original motivation for manim was to
  13. better illustrate mathematical functions
  14. as transformations.
  15. """)
  16. intro_words.to_edge(UP)
  17. self.play(Write(intro_words))
  18. self.wait(2)
  19. # Linear transform
  20. grid = NumberPlane((-10, 10), (-5, 5))
  21. matrix = [[1, 1], [0, 1]]
  22. linear_transform_words = VGroup(
  23. Text("This is what the matrix"),
  24. IntegerMatrix(matrix),
  25. Text("looks like")
  26. )
  27. linear_transform_words.arrange(RIGHT)
  28. linear_transform_words.to_edge(UP)
  29. linear_transform_words.set_backstroke(width=5)
  30. self.play(
  31. ShowCreation(grid),
  32. FadeTransform(intro_words, linear_transform_words)
  33. )
  34. self.wait()
  35. self.play(grid.animate.apply_matrix(matrix), run_time=3)
  36. self.wait()
  37. # Complex map
  38. c_grid = ComplexPlane()
  39. moving_c_grid = c_grid.copy()
  40. moving_c_grid.prepare_for_nonlinear_transform()
  41. c_grid.set_stroke(BLUE_E, 1)
  42. c_grid.add_coordinate_labels(font_size=24)
  43. complex_map_words = TexText("""
  44. Or thinking of the plane as $\\mathds{C}$,\\\\
  45. this is the map $z \\rightarrow z^2$
  46. """)
  47. complex_map_words.to_corner(UR)
  48. complex_map_words.set_backstroke(width=5)
  49. self.play(
  50. FadeOut(grid),
  51. Write(c_grid, run_time=3),
  52. FadeIn(moving_c_grid),
  53. FadeTransform(linear_transform_words, complex_map_words),
  54. )
  55. self.wait()
  56. self.play(
  57. moving_c_grid.animate.apply_complex_function(lambda z: z**2),
  58. run_time=6,
  59. )
  60. self.wait(2)
  61. class AnimatingMethods(Scene):
  62. def construct(self):
  63. grid = Tex(R"\pi").get_grid(10, 10, height=4)
  64. self.add(grid)
  65. # You can animate the application of mobject methods with the
  66. # ".animate" syntax:
  67. self.play(grid.animate.shift(LEFT))
  68. # Both of those will interpolate between the mobject's initial
  69. # state and whatever happens when you apply that method.
  70. # For this example, calling grid.shift(LEFT) would shift the
  71. # grid one unit to the left, but both of the previous calls to
  72. # "self.play" animate that motion.
  73. # The same applies for any method, including those setting colors.
  74. self.play(grid.animate.set_color(YELLOW))
  75. self.wait()
  76. self.play(grid.animate.set_submobject_colors_by_gradient(BLUE, GREEN))
  77. self.wait()
  78. self.play(grid.animate.set_height(TAU - MED_SMALL_BUFF))
  79. self.wait()
  80. # The method Mobject.apply_complex_function lets you apply arbitrary
  81. # complex functions, treating the points defining the mobject as
  82. # complex numbers.
  83. self.play(grid.animate.apply_complex_function(np.exp), run_time=5)
  84. self.wait()
  85. # Even more generally, you could apply Mobject.apply_function,
  86. # which takes in functions form R^3 to R^3
  87. self.play(
  88. grid.animate.apply_function(
  89. lambda p: [
  90. p[0] + 0.5 * math.sin(p[1]),
  91. p[1] + 0.5 * math.sin(p[0]),
  92. p[2]
  93. ]
  94. ),
  95. run_time=5,
  96. )
  97. self.wait()
  98. class TextExample(Scene):
  99. def construct(self):
  100. # To run this scene properly, you should have "Consolas" font in your computer
  101. # for full usage, you can see https://github.com/3b1b/manim/pull/680
  102. text = Text("Here is a text", font="Consolas", font_size=90)
  103. difference = Text(
  104. """
  105. The most important difference between Text and TexText is that\n
  106. you can change the font more easily, but can't use the LaTeX grammar
  107. """,
  108. font="Arial", font_size=24,
  109. # t2c is a dict that you can choose color for different text
  110. t2c={"Text": BLUE, "TexText": BLUE, "LaTeX": ORANGE}
  111. )
  112. VGroup(text, difference).arrange(DOWN, buff=1)
  113. self.play(Write(text))
  114. self.play(FadeIn(difference, UP))
  115. self.wait(3)
  116. fonts = Text(
  117. "And you can also set the font according to different words",
  118. font="Arial",
  119. t2f={"font": "Consolas", "words": "Consolas"},
  120. t2c={"font": BLUE, "words": GREEN}
  121. )
  122. fonts.set_width(FRAME_WIDTH - 1)
  123. slant = Text(
  124. "And the same as slant and weight",
  125. font="Consolas",
  126. t2s={"slant": ITALIC},
  127. t2w={"weight": BOLD},
  128. t2c={"slant": ORANGE, "weight": RED}
  129. )
  130. VGroup(fonts, slant).arrange(DOWN, buff=0.8)
  131. self.play(FadeOut(text), FadeOut(difference, shift=DOWN))
  132. self.play(Write(fonts))
  133. self.wait()
  134. self.play(Write(slant))
  135. self.wait()
  136. class TexTransformExample(Scene):
  137. def construct(self):
  138. # Tex to color map
  139. t2c = {
  140. "A": BLUE,
  141. "B": TEAL,
  142. "C": GREEN,
  143. }
  144. # Configuration to pass along to each Tex mobject
  145. kw = dict(font_size=72, t2c=t2c)
  146. lines = VGroup(
  147. Tex("A^2 + B^2 = C^2", **kw),
  148. Tex("A^2 = C^2 - B^2", **kw),
  149. Tex("A^2 = (C + B)(C - B)", **kw),
  150. Tex(R"A = \sqrt{(C + B)(C - B)}", **kw),
  151. )
  152. lines.arrange(DOWN, buff=LARGE_BUFF)
  153. self.add(lines[0])
  154. # The animation TransformMatchingStrings will line up parts
  155. # of the source and target which have matching substring strings.
  156. # Here, giving it a little path_arc makes each part rotate into
  157. # their final positions, which feels appropriate for the idea of
  158. # rearranging an equation
  159. self.play(
  160. TransformMatchingStrings(
  161. lines[0].copy(), lines[1],
  162. # matched_keys specifies which substring should
  163. # line up. If it's not specified, the animation
  164. # will align the longest matching substrings.
  165. # In this case, the substring "^2 = C^2" would
  166. # trip it up
  167. matched_keys=["A^2", "B^2", "C^2"],
  168. # When you want a substring from the source
  169. # to go to a non-equal substring from the target,
  170. # use the key map.
  171. key_map={"+": "-"},
  172. path_arc=90 * DEGREES,
  173. ),
  174. )
  175. self.wait()
  176. self.play(TransformMatchingStrings(
  177. lines[1].copy(), lines[2],
  178. matched_keys=["A^2"]
  179. ))
  180. self.wait()
  181. self.play(
  182. TransformMatchingStrings(
  183. lines[2].copy(), lines[3],
  184. key_map={"2": R"\sqrt"},
  185. path_arc=-30 * DEGREES,
  186. ),
  187. )
  188. self.wait(2)
  189. self.play(LaggedStartMap(FadeOut, lines, shift=2 * RIGHT))
  190. # TransformMatchingShapes will try to line up all pieces of a
  191. # source mobject with those of a target, regardless of the
  192. # what Mobject type they are.
  193. source = Text("the morse code", height=1)
  194. target = Text("here come dots", height=1)
  195. saved_source = source.copy()
  196. self.play(Write(source))
  197. self.wait()
  198. kw = dict(run_time=3, path_arc=PI / 2)
  199. self.play(TransformMatchingShapes(source, target, **kw))
  200. self.wait()
  201. self.play(TransformMatchingShapes(target, saved_source, **kw))
  202. self.wait()
  203. class TexIndexing(Scene):
  204. def construct(self):
  205. # You can index into Tex mobject (or other StringMobjects) by substrings
  206. equation = Tex(R"e^{\pi i} = -1", font_size=144)
  207. self.add(equation)
  208. self.play(FlashAround(equation["e"]))
  209. self.wait()
  210. self.play(Indicate(equation[R"\pi"]))
  211. self.wait()
  212. self.play(TransformFromCopy(
  213. equation[R"e^{\pi i}"].copy().set_opacity(0.5),
  214. equation["-1"],
  215. path_arc=-PI / 2,
  216. run_time=3
  217. ))
  218. self.play(FadeOut(equation))
  219. # Or regular expressions
  220. equation = Tex("A^2 + B^2 = C^2", font_size=144)
  221. self.play(Write(equation))
  222. for part in equation[re.compile(r"\w\^2")]:
  223. self.play(FlashAround(part))
  224. self.wait()
  225. self.play(FadeOut(equation))
  226. # Indexing by substrings like this may not work when
  227. # the order in which Latex draws symbols does not match
  228. # the order in which they show up in the string.
  229. # For example, here the infinity is drawn before the sigma
  230. # so we don't get the desired behavior.
  231. equation = Tex(R"\sum_{n = 1}^\infty \frac{1}{n^2} = \frac{\pi^2}{6}", font_size=72)
  232. self.play(FadeIn(equation))
  233. self.play(equation[R"\infty"].animate.set_color(RED)) # Doesn't hit the infinity
  234. self.wait()
  235. self.play(FadeOut(equation))
  236. # However you can always fix this by explicitly passing in
  237. # a string you might want to isolate later. Also, using
  238. # \over instead of \frac helps to avoid the issue for fractions
  239. equation = Tex(
  240. R"\sum_{n = 1}^\infty {1 \over n^2} = {\pi^2 \over 6}",
  241. # Explicitly mark "\infty" as a substring you might want to access
  242. isolate=[R"\infty"],
  243. font_size=72
  244. )
  245. self.play(FadeIn(equation))
  246. self.play(equation[R"\infty"].animate.set_color(RED)) # Got it!
  247. self.wait()
  248. self.play(FadeOut(equation))
  249. class UpdatersExample(Scene):
  250. def construct(self):
  251. square = Square()
  252. square.set_fill(BLUE_E, 1)
  253. # On all frames, the constructor Brace(square, UP) will
  254. # be called, and the mobject brace will set its data to match
  255. # that of the newly constructed object
  256. brace = always_redraw(Brace, square, UP)
  257. label = TexText("Width = 0.00")
  258. number = label.make_number_changeable("0.00")
  259. # This ensures that the method deicmal.next_to(square)
  260. # is called on every frame
  261. label.always.next_to(brace, UP)
  262. # You could also write the following equivalent line
  263. # label.add_updater(lambda m: m.next_to(brace, UP))
  264. # If the argument itself might change, you can use f_always,
  265. # for which the arguments following the initial Mobject method
  266. # should be functions returning arguments to that method.
  267. # The following line ensures thst decimal.set_value(square.get_y())
  268. # is called every frame
  269. number.f_always.set_value(square.get_width)
  270. # You could also write the following equivalent line
  271. # number.add_updater(lambda m: m.set_value(square.get_width()))
  272. self.add(square, brace, label)
  273. # Notice that the brace and label track with the square
  274. self.play(
  275. square.animate.scale(2),
  276. rate_func=there_and_back,
  277. run_time=2,
  278. )
  279. self.wait()
  280. self.play(
  281. square.animate.set_width(5, stretch=True),
  282. run_time=3,
  283. )
  284. self.wait()
  285. self.play(
  286. square.animate.set_width(2),
  287. run_time=3
  288. )
  289. self.wait()
  290. # In general, you can alway call Mobject.add_updater, and pass in
  291. # a function that you want to be called on every frame. The function
  292. # should take in either one argument, the mobject, or two arguments,
  293. # the mobject and the amount of time since the last frame.
  294. now = self.time
  295. w0 = square.get_width()
  296. square.add_updater(
  297. lambda m: m.set_width(w0 * math.sin(self.time - now) + w0)
  298. )
  299. self.wait(4 * PI)
  300. class CoordinateSystemExample(Scene):
  301. def construct(self):
  302. axes = Axes(
  303. # x-axis ranges from -1 to 10, with a default step size of 1
  304. x_range=(-1, 10),
  305. # y-axis ranges from -2 to 2 with a step size of 0.5
  306. y_range=(-2, 2, 0.5),
  307. # The axes will be stretched so as to match the specified
  308. # height and width
  309. height=6,
  310. width=10,
  311. # Axes is made of two NumberLine mobjects. You can specify
  312. # their configuration with axis_config
  313. axis_config=dict(
  314. stroke_color=GREY_A,
  315. stroke_width=2,
  316. numbers_to_exclude=[0],
  317. ),
  318. # Alternatively, you can specify configuration for just one
  319. # of them, like this.
  320. y_axis_config=dict(
  321. big_tick_numbers=[-2, 2],
  322. )
  323. )
  324. # Keyword arguments of add_coordinate_labels can be used to
  325. # configure the DecimalNumber mobjects which it creates and
  326. # adds to the axes
  327. axes.add_coordinate_labels(
  328. font_size=20,
  329. num_decimal_places=1,
  330. )
  331. self.add(axes)
  332. # Axes descends from the CoordinateSystem class, meaning
  333. # you can call call axes.coords_to_point, abbreviated to
  334. # axes.c2p, to associate a set of coordinates with a point,
  335. # like so:
  336. dot = Dot(color=RED)
  337. dot.move_to(axes.c2p(0, 0))
  338. self.play(FadeIn(dot, scale=0.5))
  339. self.play(dot.animate.move_to(axes.c2p(3, 2)))
  340. self.wait()
  341. self.play(dot.animate.move_to(axes.c2p(5, 0.5)))
  342. self.wait()
  343. # Similarly, you can call axes.point_to_coords, or axes.p2c
  344. # print(axes.p2c(dot.get_center()))
  345. # We can draw lines from the axes to better mark the coordinates
  346. # of a given point.
  347. # Here, the always_redraw command means that on each new frame
  348. # the lines will be redrawn
  349. h_line = always_redraw(lambda: axes.get_h_line(dot.get_left()))
  350. v_line = always_redraw(lambda: axes.get_v_line(dot.get_bottom()))
  351. self.play(
  352. ShowCreation(h_line),
  353. ShowCreation(v_line),
  354. )
  355. self.play(dot.animate.move_to(axes.c2p(3, -2)))
  356. self.wait()
  357. self.play(dot.animate.move_to(axes.c2p(1, 1)))
  358. self.wait()
  359. # If we tie the dot to a particular set of coordinates, notice
  360. # that as we move the axes around it respects the coordinate
  361. # system defined by them.
  362. f_always(dot.move_to, lambda: axes.c2p(1, 1))
  363. self.play(
  364. axes.animate.scale(0.75).to_corner(UL),
  365. run_time=2,
  366. )
  367. self.wait()
  368. self.play(FadeOut(VGroup(axes, dot, h_line, v_line)))
  369. # Other coordinate systems you can play around with include
  370. # ThreeDAxes, NumberPlane, and ComplexPlane.
  371. class GraphExample(Scene):
  372. def construct(self):
  373. axes = Axes((-3, 10), (-1, 8), height=6)
  374. axes.add_coordinate_labels()
  375. self.play(Write(axes, lag_ratio=0.01, run_time=1))
  376. # Axes.get_graph will return the graph of a function
  377. sin_graph = axes.get_graph(
  378. lambda x: 2 * math.sin(x),
  379. color=BLUE,
  380. )
  381. # By default, it draws it so as to somewhat smoothly interpolate
  382. # between sampled points (x, f(x)). If the graph is meant to have
  383. # a corner, though, you can set use_smoothing to False
  384. relu_graph = axes.get_graph(
  385. lambda x: max(x, 0),
  386. use_smoothing=False,
  387. color=YELLOW,
  388. )
  389. # For discontinuous functions, you can specify the point of
  390. # discontinuity so that it does not try to draw over the gap.
  391. step_graph = axes.get_graph(
  392. lambda x: 2.0 if x > 3 else 1.0,
  393. discontinuities=[3],
  394. color=GREEN,
  395. )
  396. # Axes.get_graph_label takes in either a string or a mobject.
  397. # If it's a string, it treats it as a LaTeX expression. By default
  398. # it places the label next to the graph near the right side, and
  399. # has it match the color of the graph
  400. sin_label = axes.get_graph_label(sin_graph, "\\sin(x)")
  401. relu_label = axes.get_graph_label(relu_graph, Text("ReLU"))
  402. step_label = axes.get_graph_label(step_graph, Text("Step"), x=4)
  403. self.play(
  404. ShowCreation(sin_graph),
  405. FadeIn(sin_label, RIGHT),
  406. )
  407. self.wait(2)
  408. self.play(
  409. ReplacementTransform(sin_graph, relu_graph),
  410. FadeTransform(sin_label, relu_label),
  411. )
  412. self.wait()
  413. self.play(
  414. ReplacementTransform(relu_graph, step_graph),
  415. FadeTransform(relu_label, step_label),
  416. )
  417. self.wait()
  418. parabola = axes.get_graph(lambda x: 0.25 * x**2)
  419. parabola.set_stroke(BLUE)
  420. self.play(
  421. FadeOut(step_graph),
  422. FadeOut(step_label),
  423. ShowCreation(parabola)
  424. )
  425. self.wait()
  426. # You can use axes.input_to_graph_point, abbreviated
  427. # to axes.i2gp, to find a particular point on a graph
  428. dot = Dot(color=RED)
  429. dot.move_to(axes.i2gp(2, parabola))
  430. self.play(FadeIn(dot, scale=0.5))
  431. # A value tracker lets us animate a parameter, usually
  432. # with the intent of having other mobjects update based
  433. # on the parameter
  434. x_tracker = ValueTracker(2)
  435. dot.add_updater(lambda d: d.move_to(axes.i2gp(x_tracker.get_value(), parabola)))
  436. self.play(x_tracker.animate.set_value(4), run_time=3)
  437. self.play(x_tracker.animate.set_value(-2), run_time=3)
  438. self.wait()
  439. class TexAndNumbersExample(Scene):
  440. def construct(self):
  441. axes = Axes((-3, 3), (-3, 3), unit_size=1)
  442. axes.to_edge(DOWN)
  443. axes.add_coordinate_labels(font_size=16)
  444. circle = Circle(radius=2)
  445. circle.set_stroke(YELLOW, 3)
  446. circle.move_to(axes.get_origin())
  447. self.add(axes, circle)
  448. # When numbers show up in tex, they can be readily
  449. # replaced with DecimalMobjects so that methods like
  450. # get_value and set_value can be called on them, and
  451. # animations like ChangeDecimalToValue can be called
  452. # on them.
  453. tex = Tex("x^2 + y^2 = 4.00")
  454. tex.next_to(axes, UP, buff=0.5)
  455. value = tex.make_number_changeable("4.00")
  456. # This will tie the right hand side of our equation to
  457. # the square of the radius of the circle
  458. value.add_updater(lambda v: v.set_value(circle.get_radius()**2))
  459. self.add(tex)
  460. text = Text("""
  461. You can manipulate numbers
  462. in Tex mobjects
  463. """, font_size=30)
  464. text.next_to(tex, RIGHT, buff=1.5)
  465. arrow = Arrow(text, tex)
  466. self.add(text, arrow)
  467. self.play(
  468. circle.animate.set_height(2.0),
  469. run_time=4,
  470. rate_func=there_and_back,
  471. )
  472. # By default, tex.make_number_changeable replaces the first occurance
  473. # of the number,but by passing replace_all=True it replaces all and
  474. # returns a group of the results
  475. exponents = tex.make_number_changeable("2", replace_all=True)
  476. self.play(
  477. LaggedStartMap(
  478. FlashAround, exponents,
  479. lag_ratio=0.2, buff=0.1, color=RED
  480. ),
  481. exponents.animate.set_color(RED)
  482. )
  483. def func(x, y):
  484. # Switch from manim coords to axes coords
  485. xa, ya = axes.point_to_coords(np.array([x, y, 0]))
  486. return xa**4 + ya**4 - 4
  487. new_curve = ImplicitFunction(func)
  488. new_curve.match_style(circle)
  489. circle.rotate(angle_of_vector(new_curve.get_start())) # Align
  490. value.clear_updaters()
  491. self.play(
  492. *(ChangeDecimalToValue(exp, 4) for exp in exponents),
  493. ReplacementTransform(circle.copy(), new_curve),
  494. circle.animate.set_stroke(width=1, opacity=0.5),
  495. )
  496. class SurfaceExample(ThreeDScene):
  497. def construct(self):
  498. surface_text = Text("For 3d scenes, try using surfaces")
  499. surface_text.fix_in_frame()
  500. surface_text.to_edge(UP)
  501. self.add(surface_text)
  502. self.wait(0.1)
  503. torus1 = Torus(r1=1, r2=1)
  504. torus2 = Torus(r1=3, r2=1)
  505. sphere = Sphere(radius=3, resolution=torus1.resolution)
  506. # You can texture a surface with up to two images, which will
  507. # be interpreted as the side towards the light, and away from
  508. # the light. These can be either urls, or paths to a local file
  509. # in whatever you've set as the image directory in
  510. # the custom_config.yml file
  511. # day_texture = "EarthTextureMap"
  512. # night_texture = "NightEarthTextureMap"
  513. day_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
  514. night_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg"
  515. surfaces = [
  516. TexturedSurface(surface, day_texture, night_texture)
  517. for surface in [sphere, torus1, torus2]
  518. ]
  519. for mob in surfaces:
  520. mob.shift(IN)
  521. mob.mesh = SurfaceMesh(mob)
  522. mob.mesh.set_stroke(BLUE, 1, opacity=0.5)
  523. surface = surfaces[0]
  524. self.play(
  525. FadeIn(surface),
  526. ShowCreation(surface.mesh, lag_ratio=0.01, run_time=3),
  527. )
  528. for mob in surfaces:
  529. mob.add(mob.mesh)
  530. surface.save_state()
  531. self.play(Rotate(surface, PI / 2), run_time=2)
  532. for mob in surfaces[1:]:
  533. mob.rotate(PI / 2)
  534. self.play(
  535. Transform(surface, surfaces[1]),
  536. run_time=3
  537. )
  538. self.play(
  539. Transform(surface, surfaces[2]),
  540. # Move camera frame during the transition
  541. self.frame.animate.increment_phi(-10 * DEGREES),
  542. self.frame.animate.increment_theta(-20 * DEGREES),
  543. run_time=3
  544. )
  545. # Add ambient rotation
  546. self.frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))
  547. # Play around with where the light is
  548. light_text = Text("You can move around the light source")
  549. light_text.move_to(surface_text)
  550. light_text.fix_in_frame()
  551. self.play(FadeTransform(surface_text, light_text))
  552. light = self.camera.light_source
  553. light_dot = GlowDot(color=WHITE, radius=0.5)
  554. light_dot.always.move_to(light)
  555. self.add(light, light_dot)
  556. light.save_state()
  557. self.play(light.animate.move_to(3 * IN), run_time=5)
  558. self.play(light.animate.shift(10 * OUT), run_time=5)
  559. drag_text = Text("Try moving the mouse while pressing d or f")
  560. drag_text.move_to(light_text)
  561. drag_text.fix_in_frame()
  562. self.play(FadeTransform(light_text, drag_text))
  563. self.wait()
  564. class InteractiveDevelopment(Scene):
  565. def construct(self):
  566. circle = Circle()
  567. circle.set_fill(BLUE, opacity=0.5)
  568. circle.set_stroke(BLUE_E, width=4)
  569. square = Square()
  570. self.play(ShowCreation(square))
  571. self.wait()
  572. # This opens an iPython terminal where you can keep writing
  573. # lines as if they were part of this construct method.
  574. # In particular, 'square', 'circle' and 'self' will all be
  575. # part of the local namespace in that terminal.
  576. self.embed()
  577. # Try copying and pasting some of the lines below into
  578. # the interactive shell
  579. self.play(ReplacementTransform(square, circle))
  580. self.wait()
  581. self.play(circle.animate.stretch(4, 0))
  582. self.play(Rotate(circle, 90 * DEGREES))
  583. self.play(circle.animate.shift(2 * RIGHT).scale(0.25))
  584. text = Text("""
  585. In general, using the interactive shell
  586. is very helpful when developing new scenes
  587. """)
  588. self.play(Write(text))
  589. # In the interactive shell, you can just type
  590. # play, add, remove, clear, wait, save_state and restore,
  591. # instead of self.play, self.add, self.remove, etc.
  592. # To interact with the window, type touch(). You can then
  593. # scroll in the window, or zoom by holding down 'z' while scrolling,
  594. # and change camera perspective by holding down 'd' while moving
  595. # the mouse. Press 'r' to reset to the standard camera position.
  596. # Press 'q' to stop interacting with the window and go back to
  597. # typing new commands into the shell.
  598. # In principle you can customize a scene to be responsive to
  599. # mouse and keyboard interactions
  600. always(circle.move_to, self.mouse_point)
  601. class ControlsExample(Scene):
  602. drag_to_pan = False
  603. def setup(self):
  604. self.textbox = Textbox()
  605. self.checkbox = Checkbox()
  606. self.color_picker = ColorSliders()
  607. self.panel = ControlPanel(
  608. Text("Text", font_size=24), self.textbox, Line(),
  609. Text("Show/Hide Text", font_size=24), self.checkbox, Line(),
  610. Text("Color of Text", font_size=24), self.color_picker
  611. )
  612. self.add(self.panel)
  613. def construct(self):
  614. text = Text("text", font_size=96)
  615. def text_updater(old_text):
  616. assert(isinstance(old_text, Text))
  617. new_text = Text(self.textbox.get_value(), font_size=old_text.font_size)
  618. # new_text.align_data_and_family(old_text)
  619. new_text.move_to(old_text)
  620. if self.checkbox.get_value():
  621. new_text.set_fill(
  622. color=self.color_picker.get_picked_color(),
  623. opacity=self.color_picker.get_picked_opacity()
  624. )
  625. else:
  626. new_text.set_opacity(0)
  627. old_text.become(new_text)
  628. text.add_updater(text_updater)
  629. self.add(MotionMobject(text))
  630. self.textbox.set_value("Manim")
  631. # self.wait(60)
  632. # self.embed()
  633. # See https://github.com/3b1b/videos for many, many more