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
@@ -5,7 +5,8 @@ from __future__ import annotations
5
5
  __all__ = ["ChangingDecimal", "ChangeDecimalToValue"]
6
6
 
7
7
 
8
- import typing
8
+ from collections.abc import Callable
9
+ from typing import Any
9
10
 
10
11
  from manim.mobject.text.numbers import DecimalNumber
11
12
 
@@ -14,12 +15,47 @@ from ..utils.bezier import interpolate
14
15
 
15
16
 
16
17
  class ChangingDecimal(Animation):
18
+ """Animate a :class:`~.DecimalNumber` to values specified by a user-supplied function.
19
+
20
+ Parameters
21
+ ----------
22
+ decimal_mob
23
+ The :class:`~.DecimalNumber` instance to animate.
24
+ number_update_func
25
+ A function that returns the number to display at each point in the animation.
26
+ suspend_mobject_updating
27
+ If ``True``, the mobject is not updated outside this animation.
28
+
29
+ Raises
30
+ ------
31
+ TypeError
32
+ If ``decimal_mob`` is not an instance of :class:`~.DecimalNumber`.
33
+
34
+ Examples
35
+ --------
36
+
37
+ .. manim:: ChangingDecimalExample
38
+
39
+ class ChangingDecimalExample(Scene):
40
+ def construct(self):
41
+ number = DecimalNumber(0)
42
+ self.add(number)
43
+ self.play(
44
+ ChangingDecimal(
45
+ number,
46
+ lambda a: 5 * a,
47
+ run_time=3
48
+ )
49
+ )
50
+ self.wait()
51
+ """
52
+
17
53
  def __init__(
18
54
  self,
19
55
  decimal_mob: DecimalNumber,
20
- number_update_func: typing.Callable[[float], float],
21
- suspend_mobject_updating: bool | None = False,
22
- **kwargs,
56
+ number_update_func: Callable[[float], float],
57
+ suspend_mobject_updating: bool = False,
58
+ **kwargs: Any,
23
59
  ) -> None:
24
60
  self.check_validity_of_input(decimal_mob)
25
61
  self.number_update_func = number_update_func
@@ -32,12 +68,34 @@ class ChangingDecimal(Animation):
32
68
  raise TypeError("ChangingDecimal can only take in a DecimalNumber")
33
69
 
34
70
  def interpolate_mobject(self, alpha: float) -> None:
35
- self.mobject.set_value(self.number_update_func(self.rate_func(alpha)))
71
+ self.mobject.set_value(self.number_update_func(self.rate_func(alpha))) # type: ignore[attr-defined]
36
72
 
37
73
 
38
74
  class ChangeDecimalToValue(ChangingDecimal):
75
+ """Animate a :class:`~.DecimalNumber` to a target value using linear interpolation.
76
+
77
+ Parameters
78
+ ----------
79
+ decimal_mob
80
+ The :class:`~.DecimalNumber` instance to animate.
81
+ target_number
82
+ The target value to transition to.
83
+
84
+ Examples
85
+ --------
86
+
87
+ .. manim:: ChangeDecimalToValueExample
88
+
89
+ class ChangeDecimalToValueExample(Scene):
90
+ def construct(self):
91
+ number = DecimalNumber(0)
92
+ self.add(number)
93
+ self.play(ChangeDecimalToValue(number, 10, run_time=3))
94
+ self.wait()
95
+ """
96
+
39
97
  def __init__(
40
- self, decimal_mob: DecimalNumber, target_number: int, **kwargs
98
+ self, decimal_mob: DecimalNumber, target_number: int, **kwargs: Any
41
99
  ) -> None:
42
100
  start_number = decimal_mob.number
43
101
  super().__init__(
@@ -4,7 +4,8 @@ from __future__ import annotations
4
4
 
5
5
  __all__ = ["Rotating", "Rotate"]
6
6
 
7
- from typing import TYPE_CHECKING, Callable, Sequence
7
+ from collections.abc import Callable, Sequence
8
+ from typing import TYPE_CHECKING, Any
8
9
 
9
10
  import numpy as np
10
11
 
@@ -18,19 +19,85 @@ if TYPE_CHECKING:
18
19
 
19
20
 
20
21
  class Rotating(Animation):
22
+ """Animation that rotates a Mobject.
23
+
24
+ Parameters
25
+ ----------
26
+ mobject
27
+ The mobject to be rotated.
28
+ angle
29
+ The rotation angle in radians. Predefined constants such as ``DEGREES``
30
+ can also be used to specify the angle in degrees.
31
+ axis
32
+ The rotation axis as a numpy vector.
33
+ about_point
34
+ The rotation center.
35
+ about_edge
36
+ If ``about_point`` is ``None``, this argument specifies
37
+ the direction of the bounding box point to be taken as
38
+ the rotation center.
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
+ **kwargs
45
+ Additional keyword arguments passed to :class:`~.Animation`.
46
+
47
+ Examples
48
+ --------
49
+ .. manim:: RotatingDemo
50
+
51
+ class RotatingDemo(Scene):
52
+ def construct(self):
53
+ circle = Circle(radius=1, color=BLUE)
54
+ line = Line(start=ORIGIN, end=RIGHT)
55
+ arrow = Arrow(start=ORIGIN, end=RIGHT, buff=0, color=GOLD)
56
+ vg = VGroup(circle,line,arrow)
57
+ self.add(vg)
58
+ anim_kw = {"about_point": arrow.get_start(), "run_time": 1}
59
+ self.play(Rotating(arrow, 180*DEGREES, **anim_kw))
60
+ self.play(Rotating(arrow, PI, **anim_kw))
61
+ self.play(Rotating(vg, PI, about_point=RIGHT))
62
+ self.play(Rotating(vg, PI, axis=UP, about_point=ORIGIN))
63
+ self.play(Rotating(vg, PI, axis=RIGHT, about_edge=UP))
64
+ self.play(vg.animate.move_to(ORIGIN))
65
+
66
+ .. manim:: RotatingDifferentAxis
67
+
68
+ class RotatingDifferentAxis(ThreeDScene):
69
+ def construct(self):
70
+ axes = ThreeDAxes()
71
+ cube = Cube()
72
+ arrow2d = Arrow(start=[0, -1.2, 1], end=[0, 1.2, 1], color=YELLOW_E)
73
+ cube_group = VGroup(cube,arrow2d)
74
+ self.set_camera_orientation(gamma=0, phi=40*DEGREES, theta=40*DEGREES)
75
+ self.add(axes, cube_group)
76
+ play_kw = {"run_time": 1.5}
77
+ self.play(Rotating(cube_group, PI), **play_kw)
78
+ self.play(Rotating(cube_group, PI, axis=UP), **play_kw)
79
+ self.play(Rotating(cube_group, 180*DEGREES, axis=RIGHT), **play_kw)
80
+ self.wait(0.5)
81
+
82
+ See also
83
+ --------
84
+ :class:`~.Rotate`, :meth:`~.Mobject.rotate`
85
+
86
+ """
87
+
21
88
  def __init__(
22
89
  self,
23
90
  mobject: Mobject,
91
+ angle: float = TAU,
24
92
  axis: np.ndarray = OUT,
25
- radians: np.ndarray = TAU,
26
93
  about_point: np.ndarray | None = None,
27
94
  about_edge: np.ndarray | None = None,
28
95
  run_time: float = 5,
29
96
  rate_func: Callable[[float], float] = linear,
30
- **kwargs,
97
+ **kwargs: Any,
31
98
  ) -> None:
99
+ self.angle = angle
32
100
  self.axis = axis
33
- self.radians = radians
34
101
  self.about_point = about_point
35
102
  self.about_edge = about_edge
36
103
  super().__init__(mobject, run_time=run_time, rate_func=rate_func, **kwargs)
@@ -38,7 +105,7 @@ class Rotating(Animation):
38
105
  def interpolate_mobject(self, alpha: float) -> None:
39
106
  self.mobject.become(self.starting_mobject)
40
107
  self.mobject.rotate(
41
- self.rate_func(alpha) * self.radians,
108
+ self.rate_func(alpha) * self.angle,
42
109
  axis=self.axis,
43
110
  about_point=self.about_point,
44
111
  about_edge=self.about_edge,
@@ -59,7 +126,7 @@ class Rotate(Transform):
59
126
  about_point
60
127
  The rotation center.
61
128
  about_edge
62
- If ``about_point``is ``None``, this argument specifies
129
+ If ``about_point`` is ``None``, this argument specifies
63
130
  the direction of the bounding box point to be taken as
64
131
  the rotation center.
65
132
 
@@ -79,6 +146,10 @@ class Rotate(Transform):
79
146
  Rotate(Square(side_length=0.5), angle=2*PI, rate_func=linear),
80
147
  )
81
148
 
149
+ See also
150
+ --------
151
+ :class:`~.Rotating`, :meth:`~.Mobject.rotate`
152
+
82
153
  """
83
154
 
84
155
  def __init__(
@@ -88,7 +159,7 @@ class Rotate(Transform):
88
159
  axis: np.ndarray = OUT,
89
160
  about_point: Sequence[float] | None = None,
90
161
  about_edge: Sequence[float] | None = None,
91
- **kwargs,
162
+ **kwargs: Any,
92
163
  ) -> None:
93
164
  if "path_arc" not in kwargs:
94
165
  kwargs["path_arc"] = angle
@@ -2,9 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  __all__ = ["Broadcast"]
4
4
 
5
- from typing import Any, Sequence
5
+ from collections.abc import Sequence
6
+ from typing import Any
6
7
 
7
8
  from manim.animation.transform import Restore
9
+ from manim.mobject.mobject import Mobject
8
10
 
9
11
  from ..constants import *
10
12
  from .composition import LaggedStart
@@ -49,7 +51,7 @@ class Broadcast(LaggedStart):
49
51
 
50
52
  def __init__(
51
53
  self,
52
- mobject,
54
+ mobject: Mobject,
53
55
  focal_point: Sequence[float] = ORIGIN,
54
56
  n_mobs: int = 5,
55
57
  initial_opacity: float = 1,
@@ -69,10 +71,7 @@ class Broadcast(LaggedStart):
69
71
  anims = []
70
72
 
71
73
  # Works by saving the mob that is passed into the animation, scaling it to 0 (or the initial_width) and then restoring the original mob.
72
- if mobject.fill_opacity:
73
- fill_o = True
74
- else:
75
- fill_o = False
74
+ fill_o = bool(mobject.fill_opacity)
76
75
 
77
76
  for _ in range(self.n_mobs):
78
77
  mob = mobject.copy()
@@ -84,7 +83,7 @@ class Broadcast(LaggedStart):
84
83
 
85
84
  mob.move_to(self.focal_point)
86
85
  mob.save_state()
87
- mob.set_width(self.initial_width)
86
+ mob.set(width=self.initial_width)
88
87
 
89
88
  if fill_o:
90
89
  mob.set_opacity(self.initial_opacity)
@@ -2,18 +2,23 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import inspect
5
6
  import types
6
- from typing import Callable
7
+ from collections.abc import Callable
8
+ from typing import TYPE_CHECKING
7
9
 
8
10
  from numpy import piecewise
9
11
 
10
- from manim.utils.simple_functions import get_parameters
11
-
12
12
  from ..animation.animation import Animation, Wait, prepare_animation
13
13
  from ..animation.composition import AnimationGroup
14
- from ..mobject.mobject import Mobject, Updater, _AnimationBuilder
14
+ from ..mobject.mobject import Mobject, _AnimationBuilder
15
15
  from ..scene.scene import Scene
16
16
 
17
+ if TYPE_CHECKING:
18
+ from ..mobject.mobject import Updater
19
+
20
+ __all__ = ["ChangeSpeed"]
21
+
17
22
 
18
23
  class ChangeSpeed(Animation):
19
24
  """Modifies the speed of passed animation.
@@ -97,7 +102,6 @@ class ChangeSpeed(Animation):
97
102
  affects_speed_updaters: bool = True,
98
103
  **kwargs,
99
104
  ) -> None:
100
-
101
105
  if issubclass(type(anim), AnimationGroup):
102
106
  self.anim = type(anim)(
103
107
  *map(self.setup, anim.animations),
@@ -110,9 +114,9 @@ class ChangeSpeed(Animation):
110
114
  self.anim = self.setup(anim)
111
115
 
112
116
  if affects_speed_updaters:
113
- assert (
114
- ChangeSpeed.is_changing_dt is False
115
- ), "Only one animation at a time can play that changes speed (dt) for ChangeSpeed updaters"
117
+ assert ChangeSpeed.is_changing_dt is False, (
118
+ "Only one animation at a time can play that changes speed (dt) for ChangeSpeed updaters"
119
+ )
116
120
  ChangeSpeed.is_changing_dt = True
117
121
  self.t = 0
118
122
  self.affects_speed_updaters = affects_speed_updaters
@@ -261,8 +265,7 @@ class ChangeSpeed(Animation):
261
265
  :class:`.ChangeSpeed`
262
266
  :meth:`.Mobject.add_updater`
263
267
  """
264
- parameters = get_parameters(update_function)
265
- if "dt" in parameters:
268
+ if "dt" in inspect.signature(update_function).parameters:
266
269
  mobject.add_updater(
267
270
  lambda mob, dt: update_function(
268
271
  mob, ChangeSpeed.dt if ChangeSpeed.is_changing_dt else dt
@@ -28,10 +28,12 @@ __all__ = [
28
28
 
29
29
  import inspect
30
30
  import types
31
- from typing import TYPE_CHECKING, Any, Callable, Iterable, Sequence
31
+ from collections.abc import Callable, Iterable, Sequence
32
+ from typing import TYPE_CHECKING, Any
32
33
 
33
34
  import numpy as np
34
35
 
36
+ from manim.data_structures import MethodWithArgs
35
37
  from manim.mobject.opengl.opengl_mobject import OpenGLGroup, OpenGLMobject
36
38
 
37
39
  from .. import config
@@ -122,6 +124,10 @@ class Transform(Animation):
122
124
 
123
125
  self.play(*anims, run_time=2)
124
126
  self.wait()
127
+
128
+ See also
129
+ --------
130
+ :class:`~.ReplacementTransform`, :meth:`~.Mobject.interpolate`, :meth:`~.Mobject.align_data`
125
131
  """
126
132
 
127
133
  def __init__(
@@ -203,7 +209,7 @@ class Transform(Animation):
203
209
  self.mobject.align_data(self.target_copy)
204
210
  super().begin()
205
211
 
206
- def create_target(self) -> Mobject:
212
+ def create_target(self) -> Mobject | OpenGLMobject:
207
213
  # Has no meaningful effect here, but may be useful
208
214
  # in subclasses
209
215
  return self.target_mobject
@@ -211,8 +217,7 @@ class Transform(Animation):
211
217
  def clean_up_from_scene(self, scene: Scene) -> None:
212
218
  super().clean_up_from_scene(scene)
213
219
  if self.replace_mobject_with_target_in_scene:
214
- scene.remove(self.mobject)
215
- scene.add(self.target_mobject)
220
+ scene.replace(self.mobject, self.target_mobject)
216
221
 
217
222
  def get_all_mobjects(self) -> Sequence[Mobject]:
218
223
  return [
@@ -298,9 +303,7 @@ class ReplacementTransform(Transform):
298
303
 
299
304
 
300
305
  class TransformFromCopy(Transform):
301
- """
302
- Performs a reversed Transform
303
- """
306
+ """Performs a reversed Transform"""
304
307
 
305
308
  def __init__(self, mobject: Mobject, target_mobject: Mobject, **kwargs) -> None:
306
309
  super().__init__(target_mobject, mobject, **kwargs)
@@ -431,18 +434,18 @@ class MoveToTarget(Transform):
431
434
  def check_validity_of_input(self, mobject: Mobject) -> None:
432
435
  if not hasattr(mobject, "target"):
433
436
  raise ValueError(
434
- "MoveToTarget called on mobject" "without attribute 'target'",
437
+ "MoveToTarget called on mobjectwithout attribute 'target'",
435
438
  )
436
439
 
437
440
 
438
441
  class _MethodAnimation(MoveToTarget):
439
- def __init__(self, mobject, methods):
442
+ def __init__(self, mobject: Mobject, methods: list[MethodWithArgs]) -> None:
440
443
  self.methods = methods
441
444
  super().__init__(mobject)
442
445
 
443
446
  def finish(self) -> None:
444
- for method, method_args, method_kwargs in self.methods:
445
- method.__func__(self.mobject, *method_args, **method_kwargs)
447
+ for item in self.methods:
448
+ item.method.__func__(self.mobject, *item.args, **item.kwargs)
446
449
  super().finish()
447
450
 
448
451
 
@@ -76,7 +76,6 @@ class TransformMatchingAbstractBase(AnimationGroup):
76
76
  key_map: dict | None = None,
77
77
  **kwargs,
78
78
  ):
79
-
80
79
  if isinstance(mobject, OpenGLVMobject):
81
80
  group_type = OpenGLVGroup
82
81
  elif isinstance(mobject, OpenGLMobject):
@@ -97,7 +96,6 @@ class TransformMatchingAbstractBase(AnimationGroup):
97
96
  # target_map
98
97
  transform_source = group_type()
99
98
  transform_target = group_type()
100
- kwargs["final_alpha_value"] = 0
101
99
  for key in set(source_map).intersection(target_map):
102
100
  transform_source.add(source_map[key])
103
101
  transform_target.add(target_map[key])
@@ -226,8 +224,9 @@ class TransformMatchingShapes(TransformMatchingAbstractBase):
226
224
  def get_mobject_key(mobject: Mobject) -> int:
227
225
  mobject.save_state()
228
226
  mobject.center()
229
- mobject.set_height(1)
230
- result = hash(np.round(mobject.points, 3).tobytes())
227
+ mobject.set(height=1)
228
+ rounded_points = np.round(mobject.points, 3) + 0.0
229
+ result = hash(rounded_points.tobytes())
231
230
  mobject.restore()
232
231
  return result
233
232
 
@@ -15,21 +15,27 @@ __all__ = [
15
15
 
16
16
 
17
17
  import inspect
18
+ from collections.abc import Callable
19
+ from typing import TYPE_CHECKING
18
20
 
19
21
  import numpy as np
20
22
 
21
23
  from manim.constants import DEGREES, RIGHT
22
24
  from manim.mobject.mobject import Mobject
23
25
  from manim.opengl import OpenGLMobject
26
+ from manim.utils.space_ops import normalize
24
27
 
28
+ if TYPE_CHECKING:
29
+ from manim.animation.animation import Animation
25
30
 
26
- def assert_is_mobject_method(method):
31
+
32
+ def assert_is_mobject_method(method: Callable) -> None:
27
33
  assert inspect.ismethod(method)
28
34
  mobject = method.__self__
29
35
  assert isinstance(mobject, (Mobject, OpenGLMobject))
30
36
 
31
37
 
32
- def always(method, *args, **kwargs):
38
+ def always(method: Callable, *args, **kwargs) -> Mobject:
33
39
  assert_is_mobject_method(method)
34
40
  mobject = method.__self__
35
41
  func = method.__func__
@@ -37,7 +43,7 @@ def always(method, *args, **kwargs):
37
43
  return mobject
38
44
 
39
45
 
40
- def f_always(method, *arg_generators, **kwargs):
46
+ def f_always(method: Callable[[Mobject], None], *arg_generators, **kwargs) -> Mobject:
41
47
  """
42
48
  More functional version of always, where instead
43
49
  of taking in args, it takes in functions which output
@@ -55,59 +61,175 @@ def f_always(method, *arg_generators, **kwargs):
55
61
  return mobject
56
62
 
57
63
 
58
- def always_redraw(func):
64
+ def always_redraw(func: Callable[[], Mobject]) -> Mobject:
65
+ """Redraw the mobject constructed by a function every frame.
66
+
67
+ This function returns a mobject with an attached updater that
68
+ continuously regenerates the mobject according to the
69
+ specified function.
70
+
71
+ Parameters
72
+ ----------
73
+ func
74
+ A function without (required) input arguments that returns
75
+ a mobject.
76
+
77
+ Examples
78
+ --------
79
+
80
+ .. manim:: TangentAnimation
81
+
82
+ class TangentAnimation(Scene):
83
+ def construct(self):
84
+ ax = Axes()
85
+ sine = ax.plot(np.sin, color=RED)
86
+ alpha = ValueTracker(0)
87
+ point = always_redraw(
88
+ lambda: Dot(
89
+ sine.point_from_proportion(alpha.get_value()),
90
+ color=BLUE
91
+ )
92
+ )
93
+ tangent = always_redraw(
94
+ lambda: TangentLine(
95
+ sine,
96
+ alpha=alpha.get_value(),
97
+ color=YELLOW,
98
+ length=4
99
+ )
100
+ )
101
+ self.add(ax, sine, point, tangent)
102
+ self.play(alpha.animate.set_value(1), rate_func=linear, run_time=2)
103
+ """
59
104
  mob = func()
60
- mob.add_updater(lambda m: mob.become(func()))
105
+ mob.add_updater(lambda _: mob.become(func()))
61
106
  return mob
62
107
 
63
108
 
64
- def always_shift(mobject, direction=RIGHT, rate=0.1):
65
- def normalize(v):
66
- norm = np.linalg.norm(v)
67
- if norm == 0:
68
- return v
69
- return v / norm
70
-
109
+ def always_shift(
110
+ mobject: Mobject, direction: np.ndarray[np.float64] = RIGHT, rate: float = 0.1
111
+ ) -> Mobject:
112
+ """A mobject which is continuously shifted along some direction
113
+ at a certain rate.
114
+
115
+ Parameters
116
+ ----------
117
+ mobject
118
+ The mobject to shift.
119
+ direction
120
+ The direction to shift. The vector is normalized, the specified magnitude
121
+ is not relevant.
122
+ rate
123
+ Length in Manim units which the mobject travels in one
124
+ second along the specified direction.
125
+
126
+ Examples
127
+ --------
128
+
129
+ .. manim:: ShiftingSquare
130
+
131
+ class ShiftingSquare(Scene):
132
+ def construct(self):
133
+ sq = Square().set_fill(opacity=1)
134
+ tri = Triangle()
135
+ VGroup(sq, tri).arrange(LEFT)
136
+
137
+ # construct a square which is continuously
138
+ # shifted to the right
139
+ always_shift(sq, RIGHT, rate=5)
140
+
141
+ self.add(sq)
142
+ self.play(tri.animate.set_fill(opacity=1))
143
+ """
71
144
  mobject.add_updater(lambda m, dt: m.shift(dt * rate * normalize(direction)))
72
145
  return mobject
73
146
 
74
147
 
75
- def always_rotate(mobject, rate=20 * DEGREES, **kwargs):
148
+ def always_rotate(mobject: Mobject, rate: float = 20 * DEGREES, **kwargs) -> Mobject:
149
+ """A mobject which is continuously rotated at a certain rate.
150
+
151
+ Parameters
152
+ ----------
153
+ mobject
154
+ The mobject to be rotated.
155
+ rate
156
+ The angle which the mobject is rotated by
157
+ over one second.
158
+ kwags
159
+ Further arguments to be passed to :meth:`.Mobject.rotate`.
160
+
161
+ Examples
162
+ --------
163
+
164
+ .. manim:: SpinningTriangle
165
+
166
+ class SpinningTriangle(Scene):
167
+ def construct(self):
168
+ tri = Triangle().set_fill(opacity=1).set_z_index(2)
169
+ sq = Square().to_edge(LEFT)
170
+
171
+ # will keep spinning while there is an animation going on
172
+ always_rotate(tri, rate=2*PI, about_point=ORIGIN)
173
+
174
+ self.add(tri, sq)
175
+ self.play(sq.animate.to_edge(RIGHT), rate_func=linear, run_time=1)
176
+ """
76
177
  mobject.add_updater(lambda m, dt: m.rotate(dt * rate, **kwargs))
77
178
  return mobject
78
179
 
79
180
 
80
- def turn_animation_into_updater(animation, cycle=False, **kwargs):
181
+ def turn_animation_into_updater(
182
+ animation: Animation, cycle: bool = False, delay: float = 0, **kwargs
183
+ ) -> Mobject:
81
184
  """
82
185
  Add an updater to the animation's mobject which applies
83
186
  the interpolation and update functions of the animation
84
187
 
85
188
  If cycle is True, this repeats over and over. Otherwise,
86
189
  the updater will be popped upon completion
190
+
191
+ The ``delay`` parameter is the delay (in seconds) before the animation starts..
192
+
193
+ Examples
194
+ --------
195
+
196
+ .. manim:: WelcomeToManim
197
+
198
+ class WelcomeToManim(Scene):
199
+ def construct(self):
200
+ words = Text("Welcome to")
201
+ banner = ManimBanner().scale(0.5)
202
+ VGroup(words, banner).arrange(DOWN)
203
+
204
+ turn_animation_into_updater(Write(words, run_time=0.9))
205
+ self.add(words)
206
+ self.wait(0.5)
207
+ self.play(banner.expand(), run_time=0.5)
87
208
  """
88
209
  mobject = animation.mobject
89
210
  animation.suspend_mobject_updating = False
90
211
  animation.begin()
91
- animation.total_time = 0
92
-
93
- def update(m, dt):
94
- run_time = animation.get_run_time()
95
- time_ratio = animation.total_time / run_time
96
- if cycle:
97
- alpha = time_ratio % 1
98
- else:
99
- alpha = np.clip(time_ratio, 0, 1)
100
- if alpha >= 1:
101
- animation.finish()
102
- m.remove_updater(update)
103
- return
104
- animation.interpolate(alpha)
105
- animation.update_mobjects(dt)
212
+ animation.total_time = -delay
213
+
214
+ def update(m: Mobject, dt: float):
215
+ if animation.total_time >= 0:
216
+ run_time = animation.get_run_time()
217
+ time_ratio = animation.total_time / run_time
218
+ if cycle:
219
+ alpha = time_ratio % 1
220
+ else:
221
+ alpha = np.clip(time_ratio, 0, 1)
222
+ if alpha >= 1:
223
+ animation.finish()
224
+ m.remove_updater(update)
225
+ return
226
+ animation.interpolate(alpha)
227
+ animation.update_mobjects(dt)
106
228
  animation.total_time += dt
107
229
 
108
230
  mobject.add_updater(update)
109
231
  return mobject
110
232
 
111
233
 
112
- def cycle_animation(animation, **kwargs):
234
+ def cycle_animation(animation: Animation, **kwargs) -> Mobject:
113
235
  return turn_animation_into_updater(animation, cycle=True, **kwargs)