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.
- manim/__init__.py +11 -6
- manim/__main__.py +62 -19
- manim/_config/__init__.py +10 -9
- manim/_config/cli_colors.py +26 -9
- manim/_config/default.cfg +1 -3
- manim/_config/logger_utils.py +23 -13
- manim/_config/utils.py +662 -468
- manim/animation/animation.py +164 -18
- manim/animation/changing.py +34 -23
- manim/animation/composition.py +265 -67
- manim/animation/creation.py +208 -26
- manim/animation/fading.py +16 -18
- manim/animation/growing.py +35 -15
- manim/animation/indication.py +150 -76
- manim/animation/movement.py +56 -22
- manim/animation/numbers.py +64 -6
- manim/animation/rotation.py +78 -7
- manim/animation/specialized.py +6 -7
- manim/animation/speedmodifier.py +13 -10
- manim/animation/transform.py +14 -11
- manim/animation/transform_matching_parts.py +3 -4
- manim/animation/updaters/mobject_update_utils.py +152 -30
- manim/animation/updaters/update.py +10 -7
- manim/camera/camera.py +182 -118
- manim/camera/mapping_camera.py +34 -3
- manim/camera/moving_camera.py +95 -74
- manim/camera/multi_camera.py +23 -15
- manim/camera/three_d_camera.py +70 -52
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +76 -44
- manim/cli/checkhealth/checks.py +192 -0
- manim/cli/checkhealth/commands.py +90 -0
- manim/cli/default_group.py +158 -25
- manim/cli/init/commands.py +33 -25
- manim/cli/plugins/commands.py +16 -3
- manim/cli/render/commands.py +72 -60
- manim/cli/render/ease_of_access_options.py +4 -3
- manim/cli/render/global_options.py +59 -17
- manim/cli/render/output_options.py +6 -5
- manim/cli/render/render_options.py +98 -33
- manim/constants.py +109 -59
- manim/data_structures.py +31 -0
- manim/mobject/frame.py +8 -5
- manim/mobject/geometry/__init__.py +1 -0
- manim/mobject/geometry/arc.py +277 -135
- manim/mobject/geometry/boolean_ops.py +32 -31
- manim/mobject/geometry/labeled.py +376 -0
- manim/mobject/geometry/line.py +192 -87
- manim/mobject/geometry/polygram.py +224 -58
- manim/mobject/geometry/shape_matchers.py +61 -25
- manim/mobject/geometry/tips.py +122 -48
- manim/mobject/graph.py +1027 -419
- manim/mobject/graphing/coordinate_systems.py +533 -278
- manim/mobject/graphing/functions.py +53 -32
- manim/mobject/graphing/number_line.py +123 -65
- manim/mobject/graphing/probability.py +88 -62
- manim/mobject/graphing/scale.py +33 -19
- manim/mobject/logo.py +118 -28
- manim/mobject/matrix.py +87 -83
- manim/mobject/mobject.py +912 -442
- manim/mobject/opengl/dot_cloud.py +16 -5
- manim/mobject/opengl/opengl_compatibility.py +4 -2
- manim/mobject/opengl/opengl_geometry.py +254 -153
- manim/mobject/opengl/opengl_image_mobject.py +3 -1
- manim/mobject/opengl/opengl_mobject.py +779 -482
- manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
- manim/mobject/opengl/opengl_surface.py +14 -92
- manim/mobject/opengl/opengl_three_dimensions.py +12 -8
- manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
- manim/mobject/svg/brace.py +173 -41
- manim/mobject/svg/svg_mobject.py +139 -53
- manim/mobject/table.py +61 -68
- manim/mobject/text/code_mobject.py +193 -539
- manim/mobject/text/numbers.py +81 -34
- manim/mobject/text/tex_mobject.py +130 -78
- manim/mobject/text/text_mobject.py +288 -164
- manim/mobject/three_d/polyhedra.py +111 -13
- manim/mobject/three_d/three_d_utils.py +17 -8
- manim/mobject/three_d/three_dimensions.py +239 -106
- manim/mobject/types/image_mobject.py +50 -30
- manim/mobject/types/point_cloud_mobject.py +120 -75
- manim/mobject/types/vectorized_mobject.py +841 -408
- manim/mobject/value_tracker.py +105 -38
- manim/mobject/vector_field.py +50 -31
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +14 -1
- manim/plugins/plugins_flags.py +10 -14
- manim/renderer/cairo_renderer.py +65 -50
- manim/renderer/opengl_renderer.py +89 -69
- manim/renderer/opengl_renderer_window.py +39 -18
- manim/renderer/shader.py +123 -87
- manim/renderer/shader_wrapper.py +44 -28
- manim/renderer/vectorized_mobject_rendering.py +38 -10
- manim/scene/moving_camera_scene.py +32 -3
- manim/scene/scene.py +507 -242
- manim/scene/scene_file_writer.py +371 -220
- manim/scene/section.py +20 -16
- manim/scene/three_d_scene.py +14 -22
- manim/scene/vector_space_scene.py +223 -129
- manim/scene/zoomed_scene.py +46 -41
- manim/typing.py +990 -0
- manim/utils/bezier.py +1823 -371
- manim/utils/caching.py +12 -5
- manim/utils/color/AS2700.py +236 -0
- manim/utils/color/BS381.py +318 -0
- manim/utils/color/DVIPSNAMES.py +96 -0
- manim/utils/color/SVGNAMES.py +179 -0
- manim/utils/color/X11.py +533 -0
- manim/utils/color/XKCD.py +952 -0
- manim/utils/color/__init__.py +61 -0
- manim/utils/color/core.py +1667 -0
- manim/utils/color/manim_colors.py +218 -0
- manim/utils/commands.py +48 -20
- manim/utils/config_ops.py +39 -19
- manim/utils/debug.py +8 -7
- manim/utils/deprecation.py +86 -39
- manim/utils/docbuild/__init__.py +17 -0
- manim/utils/docbuild/autoaliasattr_directive.py +236 -0
- manim/utils/docbuild/autocolor_directive.py +99 -0
- manim/utils/docbuild/manim_directive.py +94 -41
- manim/utils/docbuild/module_parsing.py +245 -0
- manim/utils/exceptions.py +6 -0
- manim/utils/family.py +5 -3
- manim/utils/family_ops.py +17 -4
- manim/utils/file_ops.py +27 -17
- manim/utils/hashing.py +55 -45
- manim/utils/images.py +13 -7
- manim/utils/ipython_magic.py +13 -7
- manim/utils/iterables.py +163 -120
- manim/utils/module_ops.py +66 -24
- manim/utils/opengl.py +77 -24
- manim/utils/parameter_parsing.py +32 -0
- manim/utils/paths.py +30 -33
- manim/utils/polylabel.py +235 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +98 -32
- manim/utils/simple_functions.py +25 -33
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +188 -115
- manim/utils/testing/__init__.py +17 -0
- manim/utils/testing/_frames_testers.py +13 -8
- manim/utils/testing/_show_diff.py +5 -3
- manim/utils/testing/_test_class_makers.py +34 -18
- manim/utils/testing/frames_comparison.py +37 -19
- manim/utils/tex.py +130 -198
- manim/utils/tex_file_writing.py +77 -47
- manim/utils/tex_templates.py +2 -1
- manim/utils/unit.py +6 -5
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
- manim-0.19.1.dist-info/RECORD +220 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
- manim-0.19.1.dist-info/entry_points.txt +3 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
- manim/cli/new/group.py +0 -189
- manim/communitycolors.py +0 -9
- manim/gui/__init__.py +0 -0
- manim/gui/gui.py +0 -82
- manim/plugins/import_plugins.py +0 -43
- manim/utils/color.py +0 -552
- manim-0.17.0.dist-info/RECORD +0 -206
- manim-0.17.0.dist-info/entry_points.txt +0 -4
- /manim/cli/{new → checkhealth}/__init__.py +0 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
manim/animation/numbers.py
CHANGED
|
@@ -5,7 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
__all__ = ["ChangingDecimal", "ChangeDecimalToValue"]
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
import
|
|
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:
|
|
21
|
-
suspend_mobject_updating: bool
|
|
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__(
|
manim/animation/rotation.py
CHANGED
|
@@ -4,7 +4,8 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
__all__ = ["Rotating", "Rotate"]
|
|
6
6
|
|
|
7
|
-
from
|
|
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.
|
|
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
|
manim/animation/specialized.py
CHANGED
|
@@ -2,9 +2,11 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
__all__ = ["Broadcast"]
|
|
4
4
|
|
|
5
|
-
from
|
|
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
|
-
|
|
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.
|
|
86
|
+
mob.set(width=self.initial_width)
|
|
88
87
|
|
|
89
88
|
if fill_o:
|
|
90
89
|
mob.set_opacity(self.initial_opacity)
|
manim/animation/speedmodifier.py
CHANGED
|
@@ -2,18 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import inspect
|
|
5
6
|
import types
|
|
6
|
-
from
|
|
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,
|
|
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
|
|
115
|
-
)
|
|
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
|
-
|
|
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
|
manim/animation/transform.py
CHANGED
|
@@ -28,10 +28,12 @@ __all__ = [
|
|
|
28
28
|
|
|
29
29
|
import inspect
|
|
30
30
|
import types
|
|
31
|
-
from
|
|
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.
|
|
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
|
|
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
|
|
445
|
-
method.__func__(self.mobject, *
|
|
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.
|
|
230
|
-
|
|
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
|
-
|
|
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
|
|
105
|
+
mob.add_updater(lambda _: mob.become(func()))
|
|
61
106
|
return mob
|
|
62
107
|
|
|
63
108
|
|
|
64
|
-
def always_shift(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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(
|
|
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 =
|
|
92
|
-
|
|
93
|
-
def update(m, dt):
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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)
|