manim 0.17.0__py3-none-any.whl → 0.19.1__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.
Files changed (163) hide show
  1. manim/__init__.py +11 -6
  2. manim/__main__.py +62 -19
  3. manim/_config/__init__.py +10 -9
  4. manim/_config/cli_colors.py +26 -9
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +23 -13
  7. manim/_config/utils.py +662 -468
  8. manim/animation/animation.py +164 -18
  9. manim/animation/changing.py +34 -23
  10. manim/animation/composition.py +265 -67
  11. manim/animation/creation.py +208 -26
  12. manim/animation/fading.py +16 -18
  13. manim/animation/growing.py +35 -15
  14. manim/animation/indication.py +150 -76
  15. manim/animation/movement.py +56 -22
  16. manim/animation/numbers.py +64 -6
  17. manim/animation/rotation.py +78 -7
  18. manim/animation/specialized.py +6 -7
  19. manim/animation/speedmodifier.py +13 -10
  20. manim/animation/transform.py +14 -11
  21. manim/animation/transform_matching_parts.py +3 -4
  22. manim/animation/updaters/mobject_update_utils.py +152 -30
  23. manim/animation/updaters/update.py +10 -7
  24. manim/camera/camera.py +182 -118
  25. manim/camera/mapping_camera.py +34 -3
  26. manim/camera/moving_camera.py +95 -74
  27. manim/camera/multi_camera.py +23 -15
  28. manim/camera/three_d_camera.py +70 -52
  29. manim/cli/__init__.py +17 -0
  30. manim/cli/cfg/group.py +76 -44
  31. manim/cli/checkhealth/checks.py +192 -0
  32. manim/cli/checkhealth/commands.py +90 -0
  33. manim/cli/default_group.py +158 -25
  34. manim/cli/init/commands.py +33 -25
  35. manim/cli/plugins/commands.py +16 -3
  36. manim/cli/render/commands.py +72 -60
  37. manim/cli/render/ease_of_access_options.py +4 -3
  38. manim/cli/render/global_options.py +59 -17
  39. manim/cli/render/output_options.py +6 -5
  40. manim/cli/render/render_options.py +98 -33
  41. manim/constants.py +109 -59
  42. manim/data_structures.py +31 -0
  43. manim/mobject/frame.py +8 -5
  44. manim/mobject/geometry/__init__.py +1 -0
  45. manim/mobject/geometry/arc.py +277 -135
  46. manim/mobject/geometry/boolean_ops.py +32 -31
  47. manim/mobject/geometry/labeled.py +376 -0
  48. manim/mobject/geometry/line.py +192 -87
  49. manim/mobject/geometry/polygram.py +224 -58
  50. manim/mobject/geometry/shape_matchers.py +61 -25
  51. manim/mobject/geometry/tips.py +122 -48
  52. manim/mobject/graph.py +1027 -419
  53. manim/mobject/graphing/coordinate_systems.py +533 -278
  54. manim/mobject/graphing/functions.py +53 -32
  55. manim/mobject/graphing/number_line.py +123 -65
  56. manim/mobject/graphing/probability.py +88 -62
  57. manim/mobject/graphing/scale.py +33 -19
  58. manim/mobject/logo.py +118 -28
  59. manim/mobject/matrix.py +87 -83
  60. manim/mobject/mobject.py +912 -442
  61. manim/mobject/opengl/dot_cloud.py +16 -5
  62. manim/mobject/opengl/opengl_compatibility.py +4 -2
  63. manim/mobject/opengl/opengl_geometry.py +254 -153
  64. manim/mobject/opengl/opengl_image_mobject.py +3 -1
  65. manim/mobject/opengl/opengl_mobject.py +779 -482
  66. manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
  67. manim/mobject/opengl/opengl_surface.py +14 -92
  68. manim/mobject/opengl/opengl_three_dimensions.py +12 -8
  69. manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
  70. manim/mobject/svg/brace.py +173 -41
  71. manim/mobject/svg/svg_mobject.py +139 -53
  72. manim/mobject/table.py +61 -68
  73. manim/mobject/text/code_mobject.py +193 -539
  74. manim/mobject/text/numbers.py +81 -34
  75. manim/mobject/text/tex_mobject.py +130 -78
  76. manim/mobject/text/text_mobject.py +288 -164
  77. manim/mobject/three_d/polyhedra.py +111 -13
  78. manim/mobject/three_d/three_d_utils.py +17 -8
  79. manim/mobject/three_d/three_dimensions.py +239 -106
  80. manim/mobject/types/image_mobject.py +50 -30
  81. manim/mobject/types/point_cloud_mobject.py +120 -75
  82. manim/mobject/types/vectorized_mobject.py +841 -408
  83. manim/mobject/value_tracker.py +105 -38
  84. manim/mobject/vector_field.py +50 -31
  85. manim/opengl/__init__.py +3 -3
  86. manim/plugins/__init__.py +14 -1
  87. manim/plugins/plugins_flags.py +10 -14
  88. manim/renderer/cairo_renderer.py +65 -50
  89. manim/renderer/opengl_renderer.py +89 -69
  90. manim/renderer/opengl_renderer_window.py +39 -18
  91. manim/renderer/shader.py +123 -87
  92. manim/renderer/shader_wrapper.py +44 -28
  93. manim/renderer/vectorized_mobject_rendering.py +38 -10
  94. manim/scene/moving_camera_scene.py +32 -3
  95. manim/scene/scene.py +507 -242
  96. manim/scene/scene_file_writer.py +371 -220
  97. manim/scene/section.py +20 -16
  98. manim/scene/three_d_scene.py +14 -22
  99. manim/scene/vector_space_scene.py +223 -129
  100. manim/scene/zoomed_scene.py +46 -41
  101. manim/typing.py +990 -0
  102. manim/utils/bezier.py +1823 -371
  103. manim/utils/caching.py +12 -5
  104. manim/utils/color/AS2700.py +236 -0
  105. manim/utils/color/BS381.py +318 -0
  106. manim/utils/color/DVIPSNAMES.py +96 -0
  107. manim/utils/color/SVGNAMES.py +179 -0
  108. manim/utils/color/X11.py +533 -0
  109. manim/utils/color/XKCD.py +952 -0
  110. manim/utils/color/__init__.py +61 -0
  111. manim/utils/color/core.py +1667 -0
  112. manim/utils/color/manim_colors.py +218 -0
  113. manim/utils/commands.py +48 -20
  114. manim/utils/config_ops.py +39 -19
  115. manim/utils/debug.py +8 -7
  116. manim/utils/deprecation.py +86 -39
  117. manim/utils/docbuild/__init__.py +17 -0
  118. manim/utils/docbuild/autoaliasattr_directive.py +236 -0
  119. manim/utils/docbuild/autocolor_directive.py +99 -0
  120. manim/utils/docbuild/manim_directive.py +94 -41
  121. manim/utils/docbuild/module_parsing.py +245 -0
  122. manim/utils/exceptions.py +6 -0
  123. manim/utils/family.py +5 -3
  124. manim/utils/family_ops.py +17 -4
  125. manim/utils/file_ops.py +27 -17
  126. manim/utils/hashing.py +55 -45
  127. manim/utils/images.py +13 -7
  128. manim/utils/ipython_magic.py +13 -7
  129. manim/utils/iterables.py +163 -120
  130. manim/utils/module_ops.py +66 -24
  131. manim/utils/opengl.py +77 -24
  132. manim/utils/parameter_parsing.py +32 -0
  133. manim/utils/paths.py +30 -33
  134. manim/utils/polylabel.py +235 -0
  135. manim/utils/qhull.py +218 -0
  136. manim/utils/rate_functions.py +98 -32
  137. manim/utils/simple_functions.py +25 -33
  138. manim/utils/sounds.py +7 -1
  139. manim/utils/space_ops.py +188 -115
  140. manim/utils/testing/__init__.py +17 -0
  141. manim/utils/testing/_frames_testers.py +13 -8
  142. manim/utils/testing/_show_diff.py +5 -3
  143. manim/utils/testing/_test_class_makers.py +34 -18
  144. manim/utils/testing/frames_comparison.py +37 -19
  145. manim/utils/tex.py +130 -198
  146. manim/utils/tex_file_writing.py +77 -47
  147. manim/utils/tex_templates.py +2 -1
  148. manim/utils/unit.py +6 -5
  149. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
  150. manim-0.19.1.dist-info/RECORD +220 -0
  151. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
  152. manim-0.19.1.dist-info/entry_points.txt +3 -0
  153. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
  154. manim/cli/new/group.py +0 -189
  155. manim/communitycolors.py +0 -9
  156. manim/gui/__init__.py +0 -0
  157. manim/gui/gui.py +0 -82
  158. manim/plugins/import_plugins.py +0 -43
  159. manim/utils/color.py +0 -552
  160. manim-0.17.0.dist-info/RECORD +0 -206
  161. manim-0.17.0.dist-info/entry_points.txt +0 -4
  162. /manim/cli/{new → checkhealth}/__init__.py +0 -0
  163. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
@@ -1,26 +1,25 @@
1
1
  """Tools for displaying multiple animations at once."""
2
2
 
3
-
4
3
  from __future__ import annotations
5
4
 
6
- from typing import TYPE_CHECKING, Callable, Sequence
5
+ from collections.abc import Callable, Iterable, Sequence
6
+ from typing import TYPE_CHECKING, Any
7
7
 
8
8
  import numpy as np
9
9
 
10
- from manim.mobject.opengl.opengl_mobject import OpenGLGroup
11
-
12
- from .._config import config
13
- from ..animation.animation import Animation, prepare_animation
14
- from ..constants import RendererType
15
- from ..mobject.mobject import Group, Mobject
16
- from ..scene.scene import Scene
17
- from ..utils.iterables import remove_list_redundancies
18
- from ..utils.rate_functions import linear
10
+ from manim._config import config
11
+ from manim.animation.animation import Animation, prepare_animation
12
+ from manim.constants import RendererType
13
+ from manim.mobject.mobject import Group, Mobject
14
+ from manim.mobject.opengl.opengl_mobject import OpenGLGroup, OpenGLMobject
15
+ from manim.scene.scene import Scene
16
+ from manim.utils.iterables import remove_list_redundancies
17
+ from manim.utils.parameter_parsing import flatten_iterable_parameters
18
+ from manim.utils.rate_functions import linear
19
19
 
20
20
  if TYPE_CHECKING:
21
21
  from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup
22
-
23
- from ..mobject.types.vectorized_mobject import VGroup
22
+ from manim.mobject.types.vectorized_mobject import VGroup
24
23
 
25
24
  __all__ = ["AnimationGroup", "Succession", "LaggedStart", "LaggedStartMap"]
26
25
 
@@ -29,47 +28,82 @@ DEFAULT_LAGGED_START_LAG_RATIO: float = 0.05
29
28
 
30
29
 
31
30
  class AnimationGroup(Animation):
31
+ """Plays a group or series of :class:`~.Animation`.
32
+
33
+ Parameters
34
+ ----------
35
+ animations
36
+ Sequence of :class:`~.Animation` objects to be played.
37
+ group
38
+ A group of multiple :class:`~.Mobject`.
39
+ run_time
40
+ The duration of the animation in seconds.
41
+ rate_func
42
+ The function defining the animation progress based on the relative
43
+ runtime (see :mod:`~.rate_functions`) .
44
+ lag_ratio
45
+ Defines the delay after which the animation is applied to submobjects. A lag_ratio of
46
+ ``n.nn`` means the next animation will play when ``nnn%`` of the current animation has played.
47
+ Defaults to 0.0, meaning that all animations will be played together.
48
+
49
+ This does not influence the total runtime of the animation. Instead the runtime
50
+ of individual animations is adjusted so that the complete animation has the defined
51
+ run time.
52
+ """
53
+
32
54
  def __init__(
33
55
  self,
34
- *animations: Animation,
35
- group: Group | VGroup | OpenGLGroup | OpenGLVGroup = None,
56
+ *animations: Animation | Iterable[Animation],
57
+ group: Group | VGroup | OpenGLGroup | OpenGLVGroup | None = None,
36
58
  run_time: float | None = None,
37
59
  rate_func: Callable[[float], float] = linear,
38
60
  lag_ratio: float = 0,
39
- **kwargs,
40
- ) -> None:
41
- self.animations = [prepare_animation(anim) for anim in animations]
61
+ **kwargs: Any,
62
+ ):
63
+ arg_anim = flatten_iterable_parameters(animations)
64
+ self.animations = [prepare_animation(anim) for anim in arg_anim]
42
65
  self.rate_func = rate_func
43
- self.group = group
44
- if self.group is None:
66
+ if group is None:
45
67
  mobjects = remove_list_redundancies(
46
68
  [anim.mobject for anim in self.animations if not anim.is_introducer()],
47
69
  )
48
70
  if config["renderer"] == RendererType.OPENGL:
49
- self.group = OpenGLGroup(*mobjects)
71
+ self.group: Group | VGroup | OpenGLGroup | OpenGLVGroup = OpenGLGroup(
72
+ *mobjects
73
+ )
50
74
  else:
51
75
  self.group = Group(*mobjects)
76
+ else:
77
+ self.group = group
52
78
  super().__init__(
53
79
  self.group, rate_func=self.rate_func, lag_ratio=lag_ratio, **kwargs
54
80
  )
55
81
  self.run_time: float = self.init_run_time(run_time)
56
82
 
57
- def get_all_mobjects(self) -> Sequence[Mobject]:
83
+ def get_all_mobjects(self) -> Sequence[Mobject | OpenGLMobject]:
58
84
  return list(self.group)
59
85
 
60
86
  def begin(self) -> None:
87
+ if not self.animations:
88
+ raise ValueError(
89
+ f"Trying to play {self} without animations, this is not supported. "
90
+ "Please add at least one subanimation."
91
+ )
92
+ self.anim_group_time = 0.0
61
93
  if self.suspend_mobject_updating:
62
94
  self.group.suspend_updating()
63
95
  for anim in self.animations:
64
96
  anim.begin()
65
97
 
66
- def _setup_scene(self, scene) -> None:
98
+ def _setup_scene(self, scene: Scene) -> None:
67
99
  for anim in self.animations:
68
100
  anim._setup_scene(scene)
69
101
 
70
102
  def finish(self) -> None:
71
103
  for anim in self.animations:
72
104
  anim.finish()
105
+ self.anims_begun[:] = True
106
+ self.anims_finished[:] = True
73
107
  if self.suspend_mobject_updating:
74
108
  self.group.resume_updating()
75
109
 
@@ -81,30 +115,49 @@ class AnimationGroup(Animation):
81
115
  anim.clean_up_from_scene(scene)
82
116
 
83
117
  def update_mobjects(self, dt: float) -> None:
84
- for anim in self.animations:
118
+ for anim in self.anims_with_timings["anim"][
119
+ self.anims_begun & ~self.anims_finished
120
+ ]:
85
121
  anim.update_mobjects(dt)
86
122
 
87
- def init_run_time(self, run_time) -> float:
123
+ def init_run_time(self, run_time: float | None) -> float:
124
+ """Calculates the run time of the animation, if different from ``run_time``.
125
+
126
+ Parameters
127
+ ----------
128
+ run_time
129
+ The duration of the animation in seconds.
130
+
131
+ Returns
132
+ -------
133
+ run_time
134
+ The duration of the animation in seconds.
135
+ """
88
136
  self.build_animations_with_timings()
89
- if self.anims_with_timings:
90
- self.max_end_time = np.max([awt[2] for awt in self.anims_with_timings])
91
- else:
92
- self.max_end_time = 0
137
+ # Note: if lag_ratio < 1, then not necessarily the final animation's
138
+ # end time will be the max end time! Therefore we must calculate the
139
+ # maximum over all the end times, and not just take the last one.
140
+ # Example: if you want to play 2 animations of 10s and 1s with a
141
+ # lag_ratio of 0.1, the 1st one will end at t=10 and the 2nd one will
142
+ # end at t=2, so the AnimationGroup will end at t=10.
143
+ self.max_end_time = max(self.anims_with_timings["end"], default=0)
93
144
  return self.max_end_time if run_time is None else run_time
94
145
 
95
146
  def build_animations_with_timings(self) -> None:
96
- """
97
- Creates a list of triplets of the form
98
- (anim, start_time, end_time)
99
- """
100
- self.anims_with_timings = []
101
- curr_time: float = 0
102
- for anim in self.animations:
103
- start_time: float = curr_time
104
- end_time: float = start_time + anim.get_run_time()
105
- self.anims_with_timings.append((anim, start_time, end_time))
106
- # Start time of next animation is based on the lag_ratio
107
- curr_time = (1 - self.lag_ratio) * start_time + self.lag_ratio * end_time
147
+ """Creates a list of triplets of the form (anim, start_time, end_time)."""
148
+ run_times = np.array([anim.run_time for anim in self.animations])
149
+ num_animations = run_times.shape[0]
150
+ dtype = [("anim", "O"), ("start", "f8"), ("end", "f8")]
151
+ self.anims_with_timings: np.ndarray = np.zeros(num_animations, dtype=dtype)
152
+ self.anims_begun: np.ndarray = np.zeros(num_animations, dtype=bool)
153
+ self.anims_finished: np.ndarray = np.zeros(num_animations, dtype=bool)
154
+ if num_animations == 0:
155
+ return
156
+
157
+ lags = run_times[:-1] * self.lag_ratio
158
+ self.anims_with_timings["anim"] = self.animations
159
+ self.anims_with_timings["start"][1:] = np.add.accumulate(lags)
160
+ self.anims_with_timings["end"] = self.anims_with_timings["start"] + run_times
108
161
 
109
162
  def interpolate(self, alpha: float) -> None:
110
163
  # Note, if the run_time of AnimationGroup has been
@@ -112,22 +165,80 @@ class AnimationGroup(Animation):
112
165
  # times might not correspond to actual times,
113
166
  # e.g. of the surrounding scene. Instead they'd
114
167
  # be a rescaled version. But that's okay!
115
- time = self.rate_func(alpha) * self.max_end_time
116
- for anim, start_time, end_time in self.anims_with_timings:
117
- anim_time = end_time - start_time
118
- if anim_time == 0:
119
- sub_alpha = 0
120
- else:
121
- sub_alpha = np.clip((time - start_time) / anim_time, 0, 1)
122
- anim.interpolate(sub_alpha)
168
+ anim_group_time = self.rate_func(alpha) * self.max_end_time
169
+ time_goes_back = anim_group_time < self.anim_group_time
170
+
171
+ # Only update ongoing animations
172
+ awt = self.anims_with_timings
173
+ new_begun = anim_group_time >= awt["start"]
174
+ new_finished = anim_group_time > awt["end"]
175
+ to_update = awt[
176
+ (self.anims_begun | new_begun) & (~self.anims_finished | ~new_finished)
177
+ ]
178
+
179
+ run_times = to_update["end"] - to_update["start"]
180
+ with_zero_run_time = run_times == 0
181
+ run_times[with_zero_run_time] = 1
182
+ sub_alphas = (anim_group_time - to_update["start"]) / run_times
183
+ if time_goes_back:
184
+ sub_alphas[(sub_alphas < 0) | with_zero_run_time] = 0
185
+ else:
186
+ sub_alphas[(sub_alphas > 1) | with_zero_run_time] = 1
187
+
188
+ for anim_to_update, sub_alpha in zip(to_update["anim"], sub_alphas):
189
+ anim_to_update.interpolate(sub_alpha)
190
+
191
+ self.anim_group_time = anim_group_time
192
+ self.anims_begun = new_begun
193
+ self.anims_finished = new_finished
123
194
 
124
195
 
125
196
  class Succession(AnimationGroup):
126
- def __init__(self, *animations: Animation, lag_ratio: float = 1, **kwargs) -> None:
197
+ """Plays a series of animations in succession.
198
+
199
+ Parameters
200
+ ----------
201
+ animations
202
+ Sequence of :class:`~.Animation` objects to be played.
203
+ lag_ratio
204
+ Defines the delay after which the animation is applied to submobjects. A lag_ratio of
205
+ ``n.nn`` means the next animation will play when ``nnn%`` of the current animation has played.
206
+ Defaults to 1.0, meaning that the next animation will begin when 100% of the current
207
+ animation has played.
208
+
209
+ This does not influence the total runtime of the animation. Instead the runtime
210
+ of individual animations is adjusted so that the complete animation has the defined
211
+ run time.
212
+
213
+ Examples
214
+ --------
215
+ .. manim:: SuccessionExample
216
+
217
+ class SuccessionExample(Scene):
218
+ def construct(self):
219
+ dot1 = Dot(point=LEFT * 2 + UP * 2, radius=0.16, color=BLUE)
220
+ dot2 = Dot(point=LEFT * 2 + DOWN * 2, radius=0.16, color=MAROON)
221
+ dot3 = Dot(point=RIGHT * 2 + DOWN * 2, radius=0.16, color=GREEN)
222
+ dot4 = Dot(point=RIGHT * 2 + UP * 2, radius=0.16, color=YELLOW)
223
+ self.add(dot1, dot2, dot3, dot4)
224
+
225
+ self.play(Succession(
226
+ dot1.animate.move_to(dot2),
227
+ dot2.animate.move_to(dot3),
228
+ dot3.animate.move_to(dot4),
229
+ dot4.animate.move_to(dot1)
230
+ ))
231
+ """
232
+
233
+ def __init__(self, *animations: Animation, lag_ratio: float = 1, **kwargs: Any):
127
234
  super().__init__(*animations, lag_ratio=lag_ratio, **kwargs)
128
235
 
129
236
  def begin(self) -> None:
130
- assert len(self.animations) > 0
237
+ if not self.animations:
238
+ raise ValueError(
239
+ f"Trying to play {self} without animations, this is not supported. "
240
+ "Please add at least one subanimation."
241
+ )
131
242
  self.update_active_animation(0)
132
243
 
133
244
  def finish(self) -> None:
@@ -138,7 +249,7 @@ class Succession(AnimationGroup):
138
249
  if self.active_animation:
139
250
  self.active_animation.update_mobjects(dt)
140
251
 
141
- def _setup_scene(self, scene) -> None:
252
+ def _setup_scene(self, scene: Scene | None) -> None:
142
253
  if scene is None:
143
254
  return
144
255
  if self.is_introducer():
@@ -158,10 +269,14 @@ class Succession(AnimationGroup):
158
269
  self.active_animation = self.animations[index]
159
270
  self.active_animation._setup_scene(self.scene)
160
271
  self.active_animation.begin()
161
- self.active_start_time = self.anims_with_timings[index][1]
162
- self.active_end_time = self.anims_with_timings[index][2]
272
+ self.active_start_time = self.anims_with_timings[index]["start"]
273
+ self.active_end_time = self.anims_with_timings[index]["end"]
163
274
 
164
275
  def next_animation(self) -> None:
276
+ """Proceeds to the next animation.
277
+
278
+ This method is called right when the active animation finishes.
279
+ """
165
280
  if self.active_animation is not None:
166
281
  self.active_animation.finish()
167
282
  self.update_active_animation(self.active_index + 1)
@@ -172,38 +287,121 @@ class Succession(AnimationGroup):
172
287
  self.next_animation()
173
288
  if self.active_animation is not None and self.active_start_time is not None:
174
289
  elapsed = current_time - self.active_start_time
175
- active_run_time = self.active_animation.get_run_time()
290
+ active_run_time = self.active_animation.run_time
176
291
  subalpha = elapsed / active_run_time if active_run_time != 0.0 else 1.0
177
292
  self.active_animation.interpolate(subalpha)
178
293
 
179
294
 
180
295
  class LaggedStart(AnimationGroup):
296
+ """Adjusts the timing of a series of :class:`~.Animation` according to ``lag_ratio``.
297
+
298
+ Parameters
299
+ ----------
300
+ animations
301
+ Sequence of :class:`~.Animation` objects to be played.
302
+ lag_ratio
303
+ Defines the delay after which the animation is applied to submobjects. A lag_ratio of
304
+ ``n.nn`` means the next animation will play when ``nnn%`` of the current animation has played.
305
+ Defaults to 0.05, meaning that the next animation will begin when 5% of the current
306
+ animation has played.
307
+
308
+ This does not influence the total runtime of the animation. Instead the runtime
309
+ of individual animations is adjusted so that the complete animation has the defined
310
+ run time.
311
+
312
+ Examples
313
+ --------
314
+ .. manim:: LaggedStartExample
315
+
316
+ class LaggedStartExample(Scene):
317
+ def construct(self):
318
+ title = Text("lag_ratio = 0.25").to_edge(UP)
319
+
320
+ dot1 = Dot(point=LEFT * 2 + UP, radius=0.16)
321
+ dot2 = Dot(point=LEFT * 2, radius=0.16)
322
+ dot3 = Dot(point=LEFT * 2 + DOWN, radius=0.16)
323
+ line_25 = DashedLine(
324
+ start=LEFT + UP * 2,
325
+ end=LEFT + DOWN * 2,
326
+ color=RED
327
+ )
328
+ label = Text("25%", font_size=24).next_to(line_25, UP)
329
+ self.add(title, dot1, dot2, dot3, line_25, label)
330
+
331
+ self.play(LaggedStart(
332
+ dot1.animate.shift(RIGHT * 4),
333
+ dot2.animate.shift(RIGHT * 4),
334
+ dot3.animate.shift(RIGHT * 4),
335
+ lag_ratio=0.25,
336
+ run_time=4
337
+ ))
338
+ """
339
+
181
340
  def __init__(
182
341
  self,
183
342
  *animations: Animation,
184
343
  lag_ratio: float = DEFAULT_LAGGED_START_LAG_RATIO,
185
- **kwargs,
344
+ **kwargs: Any,
186
345
  ):
187
346
  super().__init__(*animations, lag_ratio=lag_ratio, **kwargs)
188
347
 
189
348
 
190
349
  class LaggedStartMap(LaggedStart):
350
+ """Plays a series of :class:`~.Animation` while mapping a function to submobjects.
351
+
352
+ Parameters
353
+ ----------
354
+ AnimationClass
355
+ :class:`~.Animation` to apply to mobject.
356
+ mobject
357
+ :class:`~.Mobject` whose submobjects the animation, and optionally the function,
358
+ are to be applied.
359
+ arg_creator
360
+ Function which will be applied to :class:`~.Mobject`.
361
+ run_time
362
+ The duration of the animation in seconds.
363
+
364
+ Examples
365
+ --------
366
+ .. manim:: LaggedStartMapExample
367
+
368
+ class LaggedStartMapExample(Scene):
369
+ def construct(self):
370
+ title = Tex("LaggedStartMap").to_edge(UP, buff=LARGE_BUFF)
371
+ dots = VGroup(
372
+ *[Dot(radius=0.16) for _ in range(35)]
373
+ ).arrange_in_grid(rows=5, cols=7, buff=MED_LARGE_BUFF)
374
+ self.add(dots, title)
375
+
376
+ # Animate yellow ripple effect
377
+ for mob in dots, title:
378
+ self.play(LaggedStartMap(
379
+ ApplyMethod, mob,
380
+ lambda m : (m.set_color, YELLOW),
381
+ lag_ratio = 0.1,
382
+ rate_func = there_and_back,
383
+ run_time = 2
384
+ ))
385
+ """
386
+
191
387
  def __init__(
192
388
  self,
193
- AnimationClass: Callable[..., Animation],
389
+ animation_class: type[Animation],
194
390
  mobject: Mobject,
195
- arg_creator: Callable[[Mobject], str] = None,
391
+ arg_creator: Callable[[Mobject], Iterable[Any]] | None = None,
196
392
  run_time: float = 2,
197
- **kwargs,
198
- ) -> None:
199
- args_list = []
200
- for submob in mobject:
201
- if arg_creator:
202
- args_list.append(arg_creator(submob))
203
- else:
204
- args_list.append((submob,))
393
+ **kwargs: Any,
394
+ ):
395
+ if arg_creator is None:
396
+
397
+ def identity(mob: Mobject) -> Mobject:
398
+ return mob
399
+
400
+ arg_creator = identity
401
+
402
+ args_list = [arg_creator(submob) for submob in mobject]
205
403
  anim_kwargs = dict(kwargs)
206
404
  if "lag_ratio" in anim_kwargs:
207
405
  anim_kwargs.pop("lag_ratio")
208
- animations = [AnimationClass(*args, **anim_kwargs) for args in args_list]
406
+ animations = [animation_class(*args, **anim_kwargs) for args in args_list]
209
407
  super().__init__(*animations, run_time=run_time, **kwargs)