manim 0.18.0.post0__py3-none-any.whl → 0.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of manim might be problematic. Click here for more details.

Files changed (146) hide show
  1. manim/__init__.py +3 -6
  2. manim/__main__.py +61 -20
  3. manim/_config/__init__.py +6 -3
  4. manim/_config/cli_colors.py +16 -8
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +14 -8
  7. manim/_config/utils.py +651 -472
  8. manim/animation/animation.py +152 -5
  9. manim/animation/composition.py +80 -39
  10. manim/animation/creation.py +196 -14
  11. manim/animation/fading.py +5 -9
  12. manim/animation/indication.py +103 -47
  13. manim/animation/movement.py +22 -5
  14. manim/animation/rotation.py +3 -2
  15. manim/animation/specialized.py +4 -6
  16. manim/animation/speedmodifier.py +10 -5
  17. manim/animation/transform.py +4 -5
  18. manim/animation/transform_matching_parts.py +1 -1
  19. manim/animation/updaters/mobject_update_utils.py +17 -14
  20. manim/camera/camera.py +15 -6
  21. manim/cli/__init__.py +17 -0
  22. manim/cli/cfg/group.py +70 -44
  23. manim/cli/checkhealth/checks.py +93 -75
  24. manim/cli/checkhealth/commands.py +14 -5
  25. manim/cli/default_group.py +157 -25
  26. manim/cli/init/commands.py +32 -24
  27. manim/cli/plugins/commands.py +16 -3
  28. manim/cli/render/commands.py +72 -60
  29. manim/cli/render/ease_of_access_options.py +4 -3
  30. manim/cli/render/global_options.py +51 -15
  31. manim/cli/render/output_options.py +6 -5
  32. manim/cli/render/render_options.py +97 -32
  33. manim/constants.py +65 -19
  34. manim/gui/gui.py +2 -0
  35. manim/mobject/frame.py +0 -1
  36. manim/mobject/geometry/arc.py +112 -78
  37. manim/mobject/geometry/boolean_ops.py +32 -25
  38. manim/mobject/geometry/labeled.py +300 -77
  39. manim/mobject/geometry/line.py +132 -64
  40. manim/mobject/geometry/polygram.py +126 -30
  41. manim/mobject/geometry/shape_matchers.py +35 -15
  42. manim/mobject/geometry/tips.py +38 -29
  43. manim/mobject/graph.py +414 -133
  44. manim/mobject/graphing/coordinate_systems.py +126 -64
  45. manim/mobject/graphing/functions.py +25 -15
  46. manim/mobject/graphing/number_line.py +24 -10
  47. manim/mobject/graphing/probability.py +2 -10
  48. manim/mobject/graphing/scale.py +6 -5
  49. manim/mobject/matrix.py +17 -19
  50. manim/mobject/mobject.py +314 -165
  51. manim/mobject/opengl/opengl_compatibility.py +2 -0
  52. manim/mobject/opengl/opengl_geometry.py +30 -9
  53. manim/mobject/opengl/opengl_image_mobject.py +2 -0
  54. manim/mobject/opengl/opengl_mobject.py +509 -343
  55. manim/mobject/opengl/opengl_point_cloud_mobject.py +5 -7
  56. manim/mobject/opengl/opengl_surface.py +3 -2
  57. manim/mobject/opengl/opengl_three_dimensions.py +2 -0
  58. manim/mobject/opengl/opengl_vectorized_mobject.py +46 -79
  59. manim/mobject/svg/brace.py +63 -13
  60. manim/mobject/svg/svg_mobject.py +4 -3
  61. manim/mobject/table.py +11 -13
  62. manim/mobject/text/code_mobject.py +186 -548
  63. manim/mobject/text/numbers.py +9 -7
  64. manim/mobject/text/tex_mobject.py +23 -14
  65. manim/mobject/text/text_mobject.py +70 -24
  66. manim/mobject/three_d/polyhedra.py +98 -1
  67. manim/mobject/three_d/three_d_utils.py +4 -4
  68. manim/mobject/three_d/three_dimensions.py +62 -34
  69. manim/mobject/types/image_mobject.py +42 -24
  70. manim/mobject/types/point_cloud_mobject.py +105 -67
  71. manim/mobject/types/vectorized_mobject.py +496 -228
  72. manim/mobject/value_tracker.py +5 -4
  73. manim/mobject/vector_field.py +5 -5
  74. manim/opengl/__init__.py +3 -3
  75. manim/plugins/__init__.py +14 -1
  76. manim/plugins/plugins_flags.py +14 -8
  77. manim/renderer/cairo_renderer.py +20 -10
  78. manim/renderer/opengl_renderer.py +21 -23
  79. manim/renderer/opengl_renderer_window.py +2 -0
  80. manim/renderer/shader.py +2 -3
  81. manim/renderer/shader_wrapper.py +5 -2
  82. manim/renderer/vectorized_mobject_rendering.py +5 -0
  83. manim/scene/moving_camera_scene.py +23 -0
  84. manim/scene/scene.py +90 -43
  85. manim/scene/scene_file_writer.py +316 -165
  86. manim/scene/section.py +17 -15
  87. manim/scene/three_d_scene.py +13 -21
  88. manim/scene/vector_space_scene.py +22 -9
  89. manim/typing.py +830 -70
  90. manim/utils/bezier.py +1667 -399
  91. manim/utils/caching.py +13 -5
  92. manim/utils/color/AS2700.py +2 -0
  93. manim/utils/color/BS381.py +3 -0
  94. manim/utils/color/DVIPSNAMES.py +96 -0
  95. manim/utils/color/SVGNAMES.py +179 -0
  96. manim/utils/color/X11.py +3 -0
  97. manim/utils/color/XKCD.py +3 -0
  98. manim/utils/color/__init__.py +8 -5
  99. manim/utils/color/core.py +844 -309
  100. manim/utils/color/manim_colors.py +7 -9
  101. manim/utils/commands.py +48 -20
  102. manim/utils/config_ops.py +18 -13
  103. manim/utils/debug.py +8 -7
  104. manim/utils/deprecation.py +90 -40
  105. manim/utils/docbuild/__init__.py +17 -0
  106. manim/utils/docbuild/autoaliasattr_directive.py +234 -0
  107. manim/utils/docbuild/autocolor_directive.py +21 -17
  108. manim/utils/docbuild/manim_directive.py +50 -35
  109. manim/utils/docbuild/module_parsing.py +245 -0
  110. manim/utils/exceptions.py +6 -0
  111. manim/utils/family.py +5 -3
  112. manim/utils/family_ops.py +17 -4
  113. manim/utils/file_ops.py +26 -16
  114. manim/utils/hashing.py +9 -7
  115. manim/utils/images.py +10 -4
  116. manim/utils/ipython_magic.py +14 -8
  117. manim/utils/iterables.py +161 -119
  118. manim/utils/module_ops.py +57 -19
  119. manim/utils/opengl.py +83 -24
  120. manim/utils/parameter_parsing.py +32 -0
  121. manim/utils/paths.py +21 -23
  122. manim/utils/polylabel.py +168 -0
  123. manim/utils/qhull.py +218 -0
  124. manim/utils/rate_functions.py +74 -39
  125. manim/utils/simple_functions.py +24 -15
  126. manim/utils/sounds.py +7 -1
  127. manim/utils/space_ops.py +125 -69
  128. manim/utils/testing/__init__.py +17 -0
  129. manim/utils/testing/_frames_testers.py +13 -8
  130. manim/utils/testing/_show_diff.py +5 -3
  131. manim/utils/testing/_test_class_makers.py +33 -18
  132. manim/utils/testing/frames_comparison.py +27 -19
  133. manim/utils/tex.py +127 -197
  134. manim/utils/tex_file_writing.py +47 -45
  135. manim/utils/tex_templates.py +2 -1
  136. manim/utils/unit.py +6 -5
  137. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE.community +1 -1
  138. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/METADATA +40 -39
  139. manim-0.19.0.dist-info/RECORD +221 -0
  140. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
  141. manim/cli/new/__init__.py +0 -0
  142. manim/cli/new/group.py +0 -189
  143. manim/plugins/import_plugins.py +0 -43
  144. manim-0.18.0.post0.dist-info/RECORD +0 -217
  145. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
  146. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
@@ -45,7 +45,7 @@ class ValueTracker(Mobject, metaclass=ConvertToOpenGL):
45
45
  self.wait(1)
46
46
  tracker -= 4
47
47
  self.wait(0.5)
48
- self.play(tracker.animate.set_value(5)),
48
+ self.play(tracker.animate.set_value(5))
49
49
  self.wait(0.5)
50
50
  self.play(tracker.animate.set_value(3))
51
51
  self.play(tracker.animate.increment_value(-2))
@@ -71,7 +71,7 @@ class ValueTracker(Mobject, metaclass=ConvertToOpenGL):
71
71
 
72
72
  def __init__(self, value=0, **kwargs):
73
73
  super().__init__(**kwargs)
74
- self.set_points(np.zeros((1, 3)))
74
+ self.set(points=np.zeros((1, 3)))
75
75
  self.set_value(value)
76
76
 
77
77
  def get_value(self) -> float:
@@ -132,7 +132,7 @@ class ValueTracker(Mobject, metaclass=ConvertToOpenGL):
132
132
  Turns self into an interpolation between mobject1
133
133
  and mobject2.
134
134
  """
135
- self.set_points(path_func(mobject1.points, mobject2.points, alpha))
135
+ self.set(points=path_func(mobject1.points, mobject2.points, alpha))
136
136
  return self
137
137
 
138
138
 
@@ -165,7 +165,8 @@ class ComplexValueTracker(ValueTracker):
165
165
  """Get the current value of this value tracker as a complex number.
166
166
 
167
167
  The value is internally stored as a points array [a, b, 0]. This can be accessed directly
168
- to represent the value geometrically, see the usage example."""
168
+ to represent the value geometrically, see the usage example.
169
+ """
169
170
  return complex(*self.points[0, :2])
170
171
 
171
172
  def set_value(self, z):
@@ -10,8 +10,9 @@ __all__ = [
10
10
 
11
11
  import itertools as it
12
12
  import random
13
+ from collections.abc import Iterable, Sequence
13
14
  from math import ceil, floor
14
- from typing import Callable, Iterable, Sequence
15
+ from typing import Callable
15
16
 
16
17
  import numpy as np
17
18
  from PIL import Image
@@ -352,7 +353,6 @@ class VectorField(VGroup):
352
353
  This vector field.
353
354
 
354
355
  """
355
-
356
356
  self.stop_submobject_movement()
357
357
  self.submob_movement_updater = lambda mob, dt: mob.nudge_submobjects(
358
358
  dt * speed,
@@ -829,7 +829,9 @@ class StreamLines(VectorField):
829
829
  step = max(1, int(len(points) / self.max_anchors_per_line))
830
830
  line.set_points_smoothly(points[::step])
831
831
  if self.single_color:
832
- line.set_stroke(self.color)
832
+ line.set_stroke(
833
+ color=self.color, width=self.stroke_width, opacity=opacity
834
+ )
833
835
  else:
834
836
  if config.renderer == RendererType.OPENGL:
835
837
  # scaled for compatibility with cairo
@@ -948,7 +950,6 @@ class StreamLines(VectorField):
948
950
  self.wait(stream_lines.virtual_time / stream_lines.flow_speed)
949
951
 
950
952
  """
951
-
952
953
  for line in self.stream_lines:
953
954
  run_time = line.duration / flow_speed
954
955
  line.anim = line_animation_class(
@@ -1008,7 +1009,6 @@ class StreamLines(VectorField):
1008
1009
  self.play(stream_lines.end_animation())
1009
1010
 
1010
1011
  """
1011
-
1012
1012
  if self.flow_animation is None:
1013
1013
  raise ValueError("You have to start the animation before fading it out.")
1014
1014
 
manim/opengl/__init__.py CHANGED
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- try:
3
+ import contextlib
4
+
5
+ with contextlib.suppress(ImportError):
4
6
  from dearpygui import dearpygui as dpg
5
- except ImportError:
6
- pass
7
7
 
8
8
 
9
9
  from manim.mobject.opengl.dot_cloud import *
manim/plugins/__init__.py CHANGED
@@ -1,3 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
- from .import_plugins import *
3
+ from manim._config import config, logger
4
+ from manim.plugins.plugins_flags import get_plugins, list_plugins
5
+
6
+ __all__ = [
7
+ "get_plugins",
8
+ "list_plugins",
9
+ ]
10
+
11
+ requested_plugins: set[str] = set(config["plugins"])
12
+ missing_plugins = requested_plugins - set(get_plugins().keys())
13
+
14
+
15
+ if missing_plugins:
16
+ logger.warning("Missing Plugins: %s", missing_plugins)
@@ -2,24 +2,30 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import pkg_resources
5
+ import sys
6
+ from typing import Any
6
7
 
7
- from manim import console
8
+ if sys.version_info < (3, 10):
9
+ from importlib_metadata import entry_points
10
+ else:
11
+ from importlib.metadata import entry_points
12
+
13
+ from manim._config import console
8
14
 
9
15
  __all__ = ["list_plugins"]
10
16
 
11
17
 
12
- def get_plugins():
13
- plugins = {
18
+ def get_plugins() -> dict[str, Any]:
19
+ plugins: dict[str, Any] = {
14
20
  entry_point.name: entry_point.load()
15
- for entry_point in pkg_resources.iter_entry_points("manim.plugins")
21
+ for entry_point in entry_points(group="manim.plugins")
16
22
  }
17
23
  return plugins
18
24
 
19
25
 
20
- def list_plugins():
26
+ def list_plugins() -> None:
21
27
  console.print("[green bold]Plugins:[/green bold]", justify="left")
22
28
 
23
29
  plugins = get_plugins()
24
- for plugin in plugins:
25
- console.print(f" • {plugin}")
30
+ for plugin_name in plugins:
31
+ console.print(f" • {plugin_name}")
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import typing
4
- from typing import Any
5
4
 
6
5
  import numpy as np
7
6
 
@@ -9,14 +8,21 @@ from manim.utils.hashing import get_hash_from_play_call
9
8
 
10
9
  from .. import config, logger
11
10
  from ..camera.camera import Camera
12
- from ..mobject.mobject import Mobject
11
+ from ..mobject.mobject import Mobject, _AnimationBuilder
13
12
  from ..scene.scene_file_writer import SceneFileWriter
14
13
  from ..utils.exceptions import EndSceneEarlyException
15
14
  from ..utils.iterables import list_update
16
15
 
17
16
  if typing.TYPE_CHECKING:
17
+ from typing import Any
18
+
19
+ from manim.animation.animation import Animation
18
20
  from manim.scene.scene import Scene
19
21
 
22
+ from ..typing import PixelArray
23
+
24
+ __all__ = ["CairoRenderer"]
25
+
20
26
 
21
27
  class CairoRenderer:
22
28
  """A renderer using Cairo.
@@ -51,7 +57,12 @@ class CairoRenderer:
51
57
  scene.__class__.__name__,
52
58
  )
53
59
 
54
- def play(self, scene, *args, **kwargs):
60
+ def play(
61
+ self,
62
+ scene: Scene,
63
+ *args: Animation | Mobject | _AnimationBuilder,
64
+ **kwargs,
65
+ ):
55
66
  # Reset skip_animations to the original state.
56
67
  # Needed when rendering only some animations, and skipping others.
57
68
  self.skip_animations = self._original_skipping_status
@@ -149,7 +160,7 @@ class CairoRenderer:
149
160
  self.update_frame(scene, moving_mobjects)
150
161
  self.add_frame(self.get_frame())
151
162
 
152
- def get_frame(self):
163
+ def get_frame(self) -> PixelArray:
153
164
  """
154
165
  Gets the current frame as NumPy array.
155
166
 
@@ -176,8 +187,7 @@ class CairoRenderer:
176
187
  if self.skip_animations:
177
188
  return
178
189
  self.time += num_frames * dt
179
- for _ in range(num_frames):
180
- self.file_writer.write_frame(frame)
190
+ self.file_writer.write_frame(frame, num_frames=num_frames)
181
191
 
182
192
  def freeze_current_frame(self, duration: float):
183
193
  """Adds a static frame to the movie for a given duration. The static frame is the current frame.
@@ -242,13 +252,13 @@ class CairoRenderer:
242
252
  if config["save_last_frame"]:
243
253
  self.skip_animations = True
244
254
  if (
245
- config["from_animation_number"]
246
- and self.num_plays < config["from_animation_number"]
255
+ config.from_animation_number > 0
256
+ and self.num_plays < config.from_animation_number
247
257
  ):
248
258
  self.skip_animations = True
249
259
  if (
250
- config["upto_animation_number"]
251
- and self.num_plays > config["upto_animation_number"]
260
+ config.upto_animation_number >= 0
261
+ and self.num_plays > config.upto_animation_number
252
262
  ):
253
263
  self.skip_animations = True
254
264
  raise EndSceneEarlyException()
@@ -1,15 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import itertools as it
4
- import sys
5
5
  import time
6
+ from functools import cached_property
6
7
  from typing import Any
7
8
 
8
- if sys.version_info < (3, 8):
9
- from backports.cached_property import cached_property
10
- else:
11
- from functools import cached_property
12
-
13
9
  import moderngl
14
10
  import numpy as np
15
11
  from PIL import Image
@@ -39,6 +35,8 @@ from .vectorized_mobject_rendering import (
39
35
  render_opengl_vectorized_mobject_stroke,
40
36
  )
41
37
 
38
+ __all__ = ["OpenGLCamera", "OpenGLRenderer"]
39
+
42
40
 
43
41
  class OpenGLCamera(OpenGLMobject):
44
42
  euler_angles = _Data()
@@ -65,12 +63,12 @@ class OpenGLCamera(OpenGLMobject):
65
63
  if self.orthographic:
66
64
  self.projection_matrix = opengl.orthographic_projection_matrix()
67
65
  self.unformatted_projection_matrix = opengl.orthographic_projection_matrix(
68
- format=False,
66
+ format_=False,
69
67
  )
70
68
  else:
71
69
  self.projection_matrix = opengl.perspective_projection_matrix()
72
70
  self.unformatted_projection_matrix = opengl.perspective_projection_matrix(
73
- format=False,
71
+ format_=False,
74
72
  )
75
73
 
76
74
  if frame_shape is None:
@@ -217,11 +215,12 @@ class OpenGLCamera(OpenGLMobject):
217
215
  self.refresh_rotation_matrix()
218
216
 
219
217
 
220
- points_per_curve = 3
221
-
222
-
223
218
  class OpenGLRenderer:
224
- def __init__(self, file_writer_class=SceneFileWriter, skip_animations=False):
219
+ def __init__(
220
+ self,
221
+ file_writer_class: type[SceneFileWriter] = SceneFileWriter,
222
+ skip_animations: bool = False,
223
+ ) -> None:
225
224
  # Measured in pixel widths, used for vector graphics
226
225
  self.anti_alias_width = 1.5
227
226
  self._file_writer_class = file_writer_class
@@ -342,10 +341,8 @@ class OpenGLRenderer:
342
341
  shader_wrapper.uniforms.items(),
343
342
  self.perspective_uniforms.items(),
344
343
  ):
345
- try:
344
+ with contextlib.suppress(KeyError):
346
345
  shader.set_uniform(name, value)
347
- except KeyError:
348
- pass
349
346
  try:
350
347
  shader.set_uniform(
351
348
  "u_view_matrix", self.scene.camera.formatted_view_matrix
@@ -391,7 +388,7 @@ class OpenGLRenderer:
391
388
 
392
389
  return self.path_to_texture_id[repr(path)]
393
390
 
394
- def update_skipping_status(self):
391
+ def update_skipping_status(self) -> None:
395
392
  """
396
393
  This method is used internally to check if the current
397
394
  animation needs to be skipped or not. It also checks if
@@ -403,13 +400,13 @@ class OpenGLRenderer:
403
400
  if self.file_writer.sections[-1].skip_animations:
404
401
  self.skip_animations = True
405
402
  if (
406
- config["from_animation_number"]
407
- and self.num_plays < config["from_animation_number"]
403
+ config.from_animation_number > 0
404
+ and self.num_plays < config.from_animation_number
408
405
  ):
409
406
  self.skip_animations = True
410
407
  if (
411
- config["upto_animation_number"]
412
- and self.num_plays > config["upto_animation_number"]
408
+ config.upto_animation_number >= 0
409
+ and self.num_plays > config.upto_animation_number
413
410
  ):
414
411
  self.skip_animations = True
415
412
  raise EndSceneEarlyException()
@@ -426,8 +423,9 @@ class OpenGLRenderer:
426
423
  self.update_frame(scene)
427
424
 
428
425
  if not self.skip_animations:
429
- for _ in range(int(config.frame_rate * scene.duration)):
430
- self.file_writer.write_frame(self)
426
+ self.file_writer.write_frame(
427
+ self, num_frames=int(config.frame_rate * scene.duration)
428
+ )
431
429
 
432
430
  if self.window is not None:
433
431
  self.window.swap_buffers()
@@ -573,7 +571,7 @@ class OpenGLRenderer:
573
571
  if pixel_shape is None:
574
572
  return np.array([0, 0, 0])
575
573
  pw, ph = pixel_shape
576
- fw, fh = config["frame_width"], config["frame_height"]
574
+ fh = config["frame_height"]
577
575
  fc = self.camera.get_center()
578
576
  if relative:
579
577
  return 2 * np.array([px / pw, py / ph, 0])
@@ -7,6 +7,8 @@ from screeninfo import get_monitors
7
7
 
8
8
  from .. import __version__, config
9
9
 
10
+ __all__ = ["Window"]
11
+
10
12
 
11
13
  class Window(PygletWindow):
12
14
  fullscreen = False
manim/renderer/shader.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import inspect
4
5
  import re
5
6
  import textwrap
@@ -382,10 +383,8 @@ class Shader:
382
383
  shader_program_cache[self.name] = self.shader_program
383
384
 
384
385
  def set_uniform(self, name, value):
385
- try:
386
+ with contextlib.suppress(KeyError):
386
387
  self.shader_program[name] = value
387
- except KeyError:
388
- pass
389
388
 
390
389
 
391
390
  class FullScreenQuad(Mesh):
@@ -1,20 +1,23 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import copy
4
+ import logging
4
5
  import re
5
6
  from pathlib import Path
6
7
 
7
8
  import moderngl
8
9
  import numpy as np
9
10
 
10
- from .. import logger
11
-
12
11
  # Mobjects that should be rendered with
13
12
  # the same shader will be organized and
14
13
  # clumped together based on keeping track
15
14
  # of a dict holding all the relevant information
16
15
  # to that shader
17
16
 
17
+ __all__ = ["ShaderWrapper"]
18
+
19
+ logger = logging.getLogger("manim")
20
+
18
21
 
19
22
  def get_shader_dir():
20
23
  return Path(__file__).parent / "shaders"
@@ -8,6 +8,11 @@ from ..utils import opengl
8
8
  from ..utils.space_ops import cross2d, earclip_triangulation
9
9
  from .shader import Shader
10
10
 
11
+ __all__ = [
12
+ "render_opengl_vectorized_mobject_fill",
13
+ "render_opengl_vectorized_mobject_stroke",
14
+ ]
15
+
11
16
 
12
17
  def build_matrix_lists(mob):
13
18
  root_hierarchical_matrix = mob.hierarchical_model_matrix()
@@ -64,6 +64,25 @@ Examples
64
64
  self.play(Restore(self.camera.frame))
65
65
  self.wait()
66
66
 
67
+ .. manim:: SlidingMultipleScenes
68
+
69
+ class SlidingMultipleScenes(MovingCameraScene):
70
+ def construct(self):
71
+ def create_scene(number):
72
+ frame = Rectangle(width=16,height=9)
73
+ circ = Circle().shift(LEFT)
74
+ text = Tex(f"This is Scene {str(number)}").next_to(circ, RIGHT)
75
+ frame.add(circ,text)
76
+ return frame
77
+
78
+ group = VGroup(*(create_scene(i) for i in range(4))).arrange_in_grid(buff=4)
79
+ self.add(group)
80
+ self.camera.auto_zoom(group[0], animate=False)
81
+ for scene in group:
82
+ self.play(self.camera.auto_zoom(scene))
83
+ self.wait()
84
+
85
+ self.play(self.camera.auto_zoom(group, margin=2))
67
86
  """
68
87
 
69
88
  from __future__ import annotations
@@ -83,8 +102,12 @@ class MovingCameraScene(Scene):
83
102
  This is a Scene, with special configurations and properties that
84
103
  make it suitable for cases where the camera must be moved around.
85
104
 
105
+ Note: Examples are included in the moving_camera_scene module
106
+ documentation, see below in the 'see also' section.
107
+
86
108
  .. SEEALSO::
87
109
 
110
+ :mod:`.moving_camera_scene`
88
111
  :class:`.MovingCamera`
89
112
  """
90
113