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
|
@@ -1,22 +1,46 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import copy
|
|
4
|
+
import inspect
|
|
4
5
|
import itertools as it
|
|
5
6
|
import random
|
|
6
7
|
import sys
|
|
8
|
+
import types
|
|
9
|
+
from collections.abc import Callable, Iterable, Iterator, Sequence
|
|
7
10
|
from functools import partialmethod, wraps
|
|
8
11
|
from math import ceil
|
|
9
|
-
from typing import
|
|
12
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Protocol, TypeVar, cast
|
|
10
13
|
|
|
11
14
|
import moderngl
|
|
12
15
|
import numpy as np
|
|
13
|
-
|
|
16
|
+
import numpy.typing as npt
|
|
17
|
+
from typing_extensions import (
|
|
18
|
+
Never,
|
|
19
|
+
Self,
|
|
20
|
+
TypeAlias,
|
|
21
|
+
overload,
|
|
22
|
+
override,
|
|
23
|
+
)
|
|
14
24
|
|
|
15
25
|
from manim import config, logger
|
|
16
26
|
from manim.constants import *
|
|
27
|
+
from manim.data_structures import MethodWithArgs
|
|
28
|
+
from manim.renderer.shader_wrapper import get_colormap_code
|
|
29
|
+
from manim.typing import (
|
|
30
|
+
Point3D,
|
|
31
|
+
Point3D_Array,
|
|
32
|
+
Point3DLike,
|
|
33
|
+
Point3DLike_Array,
|
|
34
|
+
)
|
|
17
35
|
from manim.utils.bezier import integer_interpolate, interpolate
|
|
18
|
-
from manim.utils.color import
|
|
19
|
-
|
|
36
|
+
from manim.utils.color import (
|
|
37
|
+
WHITE,
|
|
38
|
+
ManimColor,
|
|
39
|
+
ParsableManimColor,
|
|
40
|
+
color_gradient,
|
|
41
|
+
color_to_rgb,
|
|
42
|
+
rgb_to_hex,
|
|
43
|
+
)
|
|
20
44
|
from manim.utils.config_ops import _Data, _Uniforms
|
|
21
45
|
|
|
22
46
|
# from ..utils.iterables import batch_by_property
|
|
@@ -31,17 +55,40 @@ from manim.utils.iterables import (
|
|
|
31
55
|
uniq_chain,
|
|
32
56
|
)
|
|
33
57
|
from manim.utils.paths import straight_path
|
|
34
|
-
from manim.utils.simple_functions import get_parameters
|
|
35
58
|
from manim.utils.space_ops import (
|
|
36
59
|
angle_between_vectors,
|
|
37
60
|
normalize,
|
|
38
61
|
rotation_matrix_transpose,
|
|
39
62
|
)
|
|
40
63
|
|
|
41
|
-
|
|
42
|
-
|
|
64
|
+
if TYPE_CHECKING:
|
|
65
|
+
from manim.animation.animation import Animation
|
|
66
|
+
from manim.renderer.shader_wrapper import ShaderWrapper
|
|
67
|
+
from manim.typing import (
|
|
68
|
+
FloatRGB_Array,
|
|
69
|
+
FloatRGBA_Array,
|
|
70
|
+
ManimFloat,
|
|
71
|
+
MappingFunction,
|
|
72
|
+
MatrixMN,
|
|
73
|
+
MultiMappingFunction,
|
|
74
|
+
PathFuncType,
|
|
75
|
+
Vector3D,
|
|
76
|
+
Vector3DLike,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
_TimeBasedUpdater: TypeAlias = Callable[["OpenGLMobject", float], object]
|
|
80
|
+
_NonTimeBasedUpdater: TypeAlias = Callable[["OpenGLMobject"], object]
|
|
81
|
+
_Updater: TypeAlias = _NonTimeBasedUpdater | _TimeBasedUpdater
|
|
82
|
+
|
|
83
|
+
_T = TypeVar("_T")
|
|
84
|
+
_T_np = TypeVar("_T_np", bound=np.generic)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def affects_shader_info_id(
|
|
88
|
+
func: Callable[[OpenGLMobject], OpenGLMobject],
|
|
89
|
+
) -> Callable[[OpenGLMobject], OpenGLMobject]:
|
|
43
90
|
@wraps(func)
|
|
44
|
-
def wrapper(self):
|
|
91
|
+
def wrapper(self: OpenGLMobject) -> OpenGLMobject:
|
|
45
92
|
for mob in self.get_family():
|
|
46
93
|
func(mob)
|
|
47
94
|
mob.refresh_shader_wrapper_id()
|
|
@@ -50,6 +97,14 @@ def affects_shader_info_id(func):
|
|
|
50
97
|
return wrapper
|
|
51
98
|
|
|
52
99
|
|
|
100
|
+
__all__ = ["OpenGLMobject", "OpenGLGroup", "OpenGLPoint", "_AnimationBuilder"]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
_ShaderDType: TypeAlias = np.void
|
|
104
|
+
"""The dtype for NumPy arrays representing shader data. It's a structured dtype with signature `(point, np.float32, (3,))`."""
|
|
105
|
+
_ShaderData: TypeAlias = npt.NDArray[_ShaderDType]
|
|
106
|
+
|
|
107
|
+
|
|
53
108
|
class OpenGLMobject:
|
|
54
109
|
"""Mathematical Object: base class for objects that can be displayed on screen.
|
|
55
110
|
|
|
@@ -66,79 +121,85 @@ class OpenGLMobject:
|
|
|
66
121
|
|
|
67
122
|
"""
|
|
68
123
|
|
|
69
|
-
|
|
124
|
+
_original__init__: ClassVar[Callable[..., None]]
|
|
125
|
+
|
|
126
|
+
shader_dtype: ClassVar[Sequence[tuple[str, type[np.generic], tuple[int, ...]]]] = [
|
|
70
127
|
("point", np.float32, (3,)),
|
|
71
128
|
]
|
|
72
|
-
shader_folder = ""
|
|
129
|
+
shader_folder: ClassVar[str] = ""
|
|
73
130
|
|
|
74
131
|
# _Data and _Uniforms are set as class variables to tell manim how to handle setting/getting these attributes later.
|
|
75
|
-
points = _Data()
|
|
76
|
-
bounding_box = _Data()
|
|
77
|
-
rgbas = _Data()
|
|
78
|
-
|
|
79
|
-
is_fixed_in_frame = _Uniforms()
|
|
80
|
-
is_fixed_orientation = _Uniforms()
|
|
81
|
-
fixed_orientation_center
|
|
82
|
-
|
|
83
|
-
|
|
132
|
+
points: _Data[Point3D_Array] = _Data()
|
|
133
|
+
bounding_box: _Data[Point3D_Array] = _Data()
|
|
134
|
+
rgbas: _Data[FloatRGBA_Array] = _Data()
|
|
135
|
+
|
|
136
|
+
is_fixed_in_frame: _Uniforms = _Uniforms()
|
|
137
|
+
is_fixed_orientation: _Uniforms = _Uniforms()
|
|
138
|
+
fixed_orientation_center: _Uniforms[tuple[float, float, float]] = (
|
|
139
|
+
_Uniforms()
|
|
140
|
+
) # for fixed orientation reference
|
|
141
|
+
gloss: _Uniforms = _Uniforms()
|
|
142
|
+
shadow: _Uniforms = _Uniforms()
|
|
84
143
|
|
|
85
144
|
def __init__(
|
|
86
145
|
self,
|
|
87
|
-
color=WHITE,
|
|
88
|
-
opacity=1,
|
|
89
|
-
dim=3, # TODO, get rid of this
|
|
146
|
+
color: ParsableManimColor | Sequence[ParsableManimColor] = WHITE,
|
|
147
|
+
opacity: float = 1,
|
|
148
|
+
dim: int = 3, # TODO, get rid of this
|
|
90
149
|
# Lighting parameters
|
|
91
150
|
# Positive gloss up to 1 makes it reflect the light.
|
|
92
|
-
gloss=0.0,
|
|
151
|
+
gloss: float = 0.0,
|
|
93
152
|
# Positive shadow up to 1 makes a side opposite the light darker
|
|
94
|
-
shadow=0.0,
|
|
153
|
+
shadow: float = 0.0,
|
|
95
154
|
# For shaders
|
|
96
|
-
render_primitive=moderngl.TRIANGLES,
|
|
97
|
-
texture_paths=None,
|
|
98
|
-
depth_test=False,
|
|
155
|
+
render_primitive: int = moderngl.TRIANGLES,
|
|
156
|
+
texture_paths: dict[str, str] | None = None,
|
|
157
|
+
depth_test: bool = False,
|
|
99
158
|
# If true, the mobject will not get rotated according to camera position
|
|
100
|
-
is_fixed_in_frame=False,
|
|
101
|
-
is_fixed_orientation=False,
|
|
159
|
+
is_fixed_in_frame: bool = False,
|
|
160
|
+
is_fixed_orientation: bool = False,
|
|
102
161
|
# Must match in attributes of vert shader
|
|
103
162
|
# Event listener
|
|
104
|
-
listen_to_events=False,
|
|
105
|
-
model_matrix=None,
|
|
106
|
-
should_render=True,
|
|
163
|
+
listen_to_events: bool = False,
|
|
164
|
+
model_matrix: MatrixMN | None = None,
|
|
165
|
+
should_render: bool = True,
|
|
107
166
|
name: str | None = None,
|
|
108
|
-
**kwargs,
|
|
167
|
+
**kwargs: Any,
|
|
109
168
|
):
|
|
110
|
-
self.name = self.__class__.__name__ if name is None else name
|
|
169
|
+
self.name: str = self.__class__.__name__ if name is None else name
|
|
111
170
|
# getattr in case data/uniforms are already defined in parent classes.
|
|
112
|
-
self.data = getattr(self, "data", {})
|
|
113
|
-
self.uniforms = getattr(
|
|
171
|
+
self.data: dict[str, npt.NDArray[Any]] = getattr(self, "data", {})
|
|
172
|
+
self.uniforms: dict[str, float | tuple[float, ...]] = getattr(
|
|
173
|
+
self, "uniforms", {}
|
|
174
|
+
)
|
|
114
175
|
|
|
115
|
-
self.opacity = opacity
|
|
116
|
-
self.dim = dim # TODO, get rid of this
|
|
176
|
+
self.opacity: float | Iterable[float] = opacity
|
|
177
|
+
self.dim: int = dim # TODO, get rid of this
|
|
117
178
|
# Lighting parameters
|
|
118
179
|
# Positive gloss up to 1 makes it reflect the light.
|
|
119
180
|
self.gloss = gloss
|
|
120
181
|
# Positive shadow up to 1 makes a side opposite the light darker
|
|
121
182
|
self.shadow = shadow
|
|
122
183
|
# For shaders
|
|
123
|
-
self.render_primitive = render_primitive
|
|
124
|
-
self.texture_paths = texture_paths
|
|
125
|
-
self.depth_test = depth_test
|
|
184
|
+
self.render_primitive: int = render_primitive
|
|
185
|
+
self.texture_paths: dict[str, str] | None = texture_paths
|
|
186
|
+
self.depth_test: bool = depth_test
|
|
126
187
|
# If true, the mobject will not get rotated according to camera position
|
|
127
188
|
self.is_fixed_in_frame = float(is_fixed_in_frame)
|
|
128
189
|
self.is_fixed_orientation = float(is_fixed_orientation)
|
|
129
190
|
self.fixed_orientation_center = (0, 0, 0)
|
|
130
191
|
# Must match in attributes of vert shader
|
|
131
192
|
# Event listener
|
|
132
|
-
self.listen_to_events = listen_to_events
|
|
133
|
-
|
|
134
|
-
self._submobjects = []
|
|
135
|
-
self.parents = []
|
|
136
|
-
self.parent = None
|
|
137
|
-
self.family = [self]
|
|
138
|
-
self.locked_data_keys = set()
|
|
139
|
-
self.needs_new_bounding_box = True
|
|
193
|
+
self.listen_to_events: bool = listen_to_events
|
|
194
|
+
|
|
195
|
+
self._submobjects: list[OpenGLMobject] = []
|
|
196
|
+
self.parents: list[OpenGLMobject] = []
|
|
197
|
+
self.parent: OpenGLMobject | None = None
|
|
198
|
+
self.family: list[OpenGLMobject] = [self]
|
|
199
|
+
self.locked_data_keys: set[str] = set()
|
|
200
|
+
self.needs_new_bounding_box: bool = True
|
|
140
201
|
if model_matrix is None:
|
|
141
|
-
self.model_matrix = np.eye(4)
|
|
202
|
+
self.model_matrix: MatrixMN = np.eye(4)
|
|
142
203
|
else:
|
|
143
204
|
self.model_matrix = model_matrix
|
|
144
205
|
|
|
@@ -146,41 +207,101 @@ class OpenGLMobject:
|
|
|
146
207
|
self.init_updaters()
|
|
147
208
|
# self.init_event_listners()
|
|
148
209
|
self.init_points()
|
|
149
|
-
self.color =
|
|
210
|
+
self.color: ManimColor | list[ManimColor] = ManimColor.parse(color)
|
|
150
211
|
self.init_colors()
|
|
151
212
|
|
|
152
|
-
self.shader_indices = None
|
|
213
|
+
self.shader_indices: Sequence[int] | None = None
|
|
153
214
|
|
|
154
215
|
if self.depth_test:
|
|
155
216
|
self.apply_depth_test()
|
|
156
217
|
|
|
157
|
-
self.should_render = should_render
|
|
218
|
+
self.should_render: bool = should_render
|
|
219
|
+
|
|
220
|
+
def _assert_valid_submobjects(self, submobjects: Iterable[OpenGLMobject]) -> Self:
|
|
221
|
+
"""Check that all submobjects are actually instances of
|
|
222
|
+
:class:`OpenGLMobject`, and that none of them is
|
|
223
|
+
``self`` (an :class:`OpenGLMobject` cannot contain itself).
|
|
224
|
+
|
|
225
|
+
This is an auxiliary function called when adding OpenGLMobjects to the
|
|
226
|
+
:attr:`submobjects` list.
|
|
227
|
+
|
|
228
|
+
This function is intended to be overridden by subclasses such as
|
|
229
|
+
:class:`OpenGLVMobject`, which should assert that only other
|
|
230
|
+
OpenGLVMobjects may be added into it.
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
submobjects
|
|
235
|
+
The list containing values to validate.
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
:class:`OpenGLMobject`
|
|
240
|
+
The OpenGLMobject itself.
|
|
241
|
+
|
|
242
|
+
Raises
|
|
243
|
+
------
|
|
244
|
+
TypeError
|
|
245
|
+
If any of the values in `submobjects` is not an
|
|
246
|
+
:class:`OpenGLMobject`.
|
|
247
|
+
ValueError
|
|
248
|
+
If there was an attempt to add an :class:`OpenGLMobject` as its own
|
|
249
|
+
submobject.
|
|
250
|
+
"""
|
|
251
|
+
return self._assert_valid_submobjects_internal(submobjects, OpenGLMobject)
|
|
252
|
+
|
|
253
|
+
def _assert_valid_submobjects_internal(
|
|
254
|
+
self, submobjects: Iterable[OpenGLMobject], mob_class: type[OpenGLMobject]
|
|
255
|
+
) -> Self:
|
|
256
|
+
for i, submob in enumerate(submobjects):
|
|
257
|
+
if not isinstance(submob, mob_class):
|
|
258
|
+
error_message = (
|
|
259
|
+
f"Only values of type {mob_class.__name__} can be added "
|
|
260
|
+
f"as submobjects of {type(self).__name__}, but the value "
|
|
261
|
+
f"{submob} (at index {i}) is of type "
|
|
262
|
+
f"{type(submob).__name__}."
|
|
263
|
+
)
|
|
264
|
+
# Intended for subclasses such as OpenGLVMobject, which
|
|
265
|
+
# cannot have regular OpenGLMobjects as submobjects
|
|
266
|
+
if isinstance(submob, OpenGLMobject):
|
|
267
|
+
error_message += (
|
|
268
|
+
" You can try adding this value into a Group instead."
|
|
269
|
+
)
|
|
270
|
+
raise TypeError(error_message)
|
|
271
|
+
if submob is self:
|
|
272
|
+
raise ValueError(
|
|
273
|
+
f"Cannot add {type(self).__name__} as a submobject of "
|
|
274
|
+
f"itself (at index {i})."
|
|
275
|
+
)
|
|
276
|
+
return self
|
|
158
277
|
|
|
159
278
|
@classmethod
|
|
160
|
-
def __init_subclass__(cls, **kwargs):
|
|
279
|
+
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
161
280
|
super().__init_subclass__(**kwargs)
|
|
162
281
|
cls._original__init__ = cls.__init__
|
|
163
282
|
|
|
164
|
-
|
|
283
|
+
@override
|
|
284
|
+
def __str__(self) -> str:
|
|
165
285
|
return self.__class__.__name__
|
|
166
286
|
|
|
167
|
-
|
|
287
|
+
@override
|
|
288
|
+
def __repr__(self) -> str:
|
|
168
289
|
return str(self.name)
|
|
169
290
|
|
|
170
|
-
def __sub__(self, other):
|
|
291
|
+
def __sub__(self, other: Never) -> object:
|
|
171
292
|
return NotImplemented
|
|
172
293
|
|
|
173
|
-
def __isub__(self, other):
|
|
294
|
+
def __isub__(self, other: Never) -> object:
|
|
174
295
|
return NotImplemented
|
|
175
296
|
|
|
176
|
-
def __add__(self, mobject):
|
|
297
|
+
def __add__(self, mobject: Never) -> object:
|
|
177
298
|
return NotImplemented
|
|
178
299
|
|
|
179
|
-
def __iadd__(self, mobject):
|
|
300
|
+
def __iadd__(self, mobject: Never) -> object:
|
|
180
301
|
return NotImplemented
|
|
181
302
|
|
|
182
303
|
@classmethod
|
|
183
|
-
def set_default(cls, **kwargs):
|
|
304
|
+
def set_default(cls, **kwargs: Any) -> None:
|
|
184
305
|
"""Sets the default values of keyword arguments.
|
|
185
306
|
|
|
186
307
|
If this method is called without any additional keyword
|
|
@@ -203,10 +324,10 @@ class OpenGLMobject:
|
|
|
203
324
|
>>> from manim import Square, GREEN
|
|
204
325
|
>>> Square.set_default(color=GREEN, fill_opacity=0.25)
|
|
205
326
|
>>> s = Square(); s.color, s.fill_opacity
|
|
206
|
-
(
|
|
327
|
+
(ManimColor('#83C167'), 0.25)
|
|
207
328
|
>>> Square.set_default()
|
|
208
329
|
>>> s = Square(); s.color, s.fill_opacity
|
|
209
|
-
(
|
|
330
|
+
(ManimColor('#FFFFFF'), 0.0)
|
|
210
331
|
|
|
211
332
|
.. manim:: ChangedDefaultTextcolor
|
|
212
333
|
:save_last_frame:
|
|
@@ -223,32 +344,37 @@ class OpenGLMobject:
|
|
|
223
344
|
|
|
224
345
|
"""
|
|
225
346
|
if kwargs:
|
|
226
|
-
|
|
347
|
+
# Apparently mypy does not correctly understand `partialmethod`:
|
|
348
|
+
# see https://github.com/python/mypy/issues/8619
|
|
349
|
+
cls.__init__ = partialmethod(cls.__init__, **kwargs) # type: ignore[assignment]
|
|
227
350
|
else:
|
|
228
351
|
cls.__init__ = cls._original__init__
|
|
229
352
|
|
|
230
|
-
def init_data(self):
|
|
353
|
+
def init_data(self) -> None:
|
|
231
354
|
"""Initializes the ``points``, ``bounding_box`` and ``rgbas`` attributes and groups them into self.data.
|
|
232
|
-
Subclasses can inherit and overwrite this method to extend `self.data`.
|
|
355
|
+
Subclasses can inherit and overwrite this method to extend `self.data`.
|
|
356
|
+
"""
|
|
233
357
|
self.points = np.zeros((0, 3))
|
|
234
358
|
self.bounding_box = np.zeros((3, 3))
|
|
235
359
|
self.rgbas = np.zeros((1, 4))
|
|
236
360
|
|
|
237
|
-
def init_colors(self):
|
|
361
|
+
def init_colors(self) -> None:
|
|
238
362
|
"""Initializes the colors.
|
|
239
363
|
|
|
240
|
-
Gets called upon creation
|
|
364
|
+
Gets called upon creation
|
|
365
|
+
"""
|
|
241
366
|
self.set_color(self.color, self.opacity)
|
|
242
367
|
|
|
243
|
-
def init_points(self):
|
|
368
|
+
def init_points(self) -> None:
|
|
244
369
|
"""Initializes :attr:`points` and therefore the shape.
|
|
245
370
|
|
|
246
371
|
Gets called upon creation. This is an empty method that can be implemented by
|
|
247
|
-
subclasses.
|
|
372
|
+
subclasses.
|
|
373
|
+
"""
|
|
248
374
|
# Typically implemented in subclass, unless purposefully left blank
|
|
249
375
|
pass
|
|
250
376
|
|
|
251
|
-
def set(self, **kwargs) ->
|
|
377
|
+
def set(self, **kwargs: object) -> Self:
|
|
252
378
|
"""Sets attributes.
|
|
253
379
|
|
|
254
380
|
Mainly to be used along with :attr:`animate` to
|
|
@@ -276,24 +402,23 @@ class OpenGLMobject:
|
|
|
276
402
|
|
|
277
403
|
|
|
278
404
|
"""
|
|
279
|
-
|
|
280
405
|
for attr, value in kwargs.items():
|
|
281
406
|
setattr(self, attr, value)
|
|
282
407
|
|
|
283
408
|
return self
|
|
284
409
|
|
|
285
|
-
def set_data(self, data):
|
|
410
|
+
def set_data(self, data: dict[str, Any]) -> Self:
|
|
286
411
|
for key in data:
|
|
287
412
|
self.data[key] = data[key].copy()
|
|
288
413
|
return self
|
|
289
414
|
|
|
290
|
-
def set_uniforms(self, uniforms):
|
|
415
|
+
def set_uniforms(self, uniforms: dict[str, Any]) -> Self:
|
|
291
416
|
for key in uniforms:
|
|
292
417
|
self.uniforms[key] = uniforms[key] # Copy?
|
|
293
418
|
return self
|
|
294
419
|
|
|
295
420
|
@property
|
|
296
|
-
def animate(self):
|
|
421
|
+
def animate(self) -> _AnimationBuilder | Self:
|
|
297
422
|
"""Used to animate the application of a method.
|
|
298
423
|
|
|
299
424
|
.. warning::
|
|
@@ -304,7 +429,9 @@ class OpenGLMobject:
|
|
|
304
429
|
|
|
305
430
|
::
|
|
306
431
|
|
|
307
|
-
self.play(
|
|
432
|
+
self.play(
|
|
433
|
+
my_mobject.animate.shift(RIGHT), my_mobject.animate.rotate(PI)
|
|
434
|
+
)
|
|
308
435
|
|
|
309
436
|
make use of method chaining for ``animate``, meaning::
|
|
310
437
|
|
|
@@ -381,7 +508,7 @@ class OpenGLMobject:
|
|
|
381
508
|
return _AnimationBuilder(self)
|
|
382
509
|
|
|
383
510
|
@property
|
|
384
|
-
def width(self):
|
|
511
|
+
def width(self) -> float:
|
|
385
512
|
"""The width of the mobject.
|
|
386
513
|
|
|
387
514
|
Returns
|
|
@@ -409,17 +536,16 @@ class OpenGLMobject:
|
|
|
409
536
|
:meth:`length_over_dim`
|
|
410
537
|
|
|
411
538
|
"""
|
|
412
|
-
|
|
413
539
|
# Get the length across the X dimension
|
|
414
540
|
return self.length_over_dim(0)
|
|
415
541
|
|
|
416
542
|
# Only these methods should directly affect points
|
|
417
543
|
@width.setter
|
|
418
|
-
def width(self, value):
|
|
544
|
+
def width(self, value: float) -> None:
|
|
419
545
|
self.rescale_to_fit(value, 0, stretch=False)
|
|
420
546
|
|
|
421
547
|
@property
|
|
422
|
-
def height(self):
|
|
548
|
+
def height(self) -> float:
|
|
423
549
|
"""The height of the mobject.
|
|
424
550
|
|
|
425
551
|
Returns
|
|
@@ -447,16 +573,15 @@ class OpenGLMobject:
|
|
|
447
573
|
:meth:`length_over_dim`
|
|
448
574
|
|
|
449
575
|
"""
|
|
450
|
-
|
|
451
576
|
# Get the length across the Y dimension
|
|
452
577
|
return self.length_over_dim(1)
|
|
453
578
|
|
|
454
579
|
@height.setter
|
|
455
|
-
def height(self, value):
|
|
580
|
+
def height(self, value: float) -> None:
|
|
456
581
|
self.rescale_to_fit(value, 1, stretch=False)
|
|
457
582
|
|
|
458
583
|
@property
|
|
459
|
-
def depth(self):
|
|
584
|
+
def depth(self) -> float:
|
|
460
585
|
"""The depth of the mobject.
|
|
461
586
|
|
|
462
587
|
Returns
|
|
@@ -468,21 +593,24 @@ class OpenGLMobject:
|
|
|
468
593
|
:meth:`length_over_dim`
|
|
469
594
|
|
|
470
595
|
"""
|
|
471
|
-
|
|
472
596
|
# Get the length across the Z dimension
|
|
473
597
|
return self.length_over_dim(2)
|
|
474
598
|
|
|
475
599
|
@depth.setter
|
|
476
|
-
def depth(self, value):
|
|
600
|
+
def depth(self, value: float) -> None:
|
|
477
601
|
self.rescale_to_fit(value, 2, stretch=False)
|
|
478
602
|
|
|
479
|
-
def resize_points(
|
|
603
|
+
def resize_points(
|
|
604
|
+
self,
|
|
605
|
+
new_length: int,
|
|
606
|
+
resize_func: Callable[[Point3D_Array, int], Point3D_Array] = resize_array,
|
|
607
|
+
) -> Self:
|
|
480
608
|
if new_length != len(self.points):
|
|
481
609
|
self.points = resize_func(self.points, new_length)
|
|
482
610
|
self.refresh_bounding_box()
|
|
483
611
|
return self
|
|
484
612
|
|
|
485
|
-
def set_points(self, points):
|
|
613
|
+
def set_points(self, points: Point3DLike_Array) -> Self:
|
|
486
614
|
if len(points) == len(self.points):
|
|
487
615
|
self.points[:] = points
|
|
488
616
|
elif isinstance(points, np.ndarray):
|
|
@@ -492,23 +620,28 @@ class OpenGLMobject:
|
|
|
492
620
|
self.refresh_bounding_box()
|
|
493
621
|
return self
|
|
494
622
|
|
|
495
|
-
def apply_over_attr_arrays(
|
|
623
|
+
def apply_over_attr_arrays(
|
|
624
|
+
self, func: Callable[[npt.NDArray[_T_np]], npt.NDArray[_T_np]]
|
|
625
|
+
) -> Self:
|
|
496
626
|
for attr in self.get_array_attrs():
|
|
497
627
|
setattr(self, attr, func(getattr(self, attr)))
|
|
498
628
|
return self
|
|
499
629
|
|
|
500
|
-
def
|
|
630
|
+
def get_array_attrs(self) -> Iterable[str]:
|
|
631
|
+
return ["points"]
|
|
632
|
+
|
|
633
|
+
def append_points(self, new_points: Point3DLike_Array) -> Self:
|
|
501
634
|
self.points = np.vstack([self.points, new_points])
|
|
502
635
|
self.refresh_bounding_box()
|
|
503
636
|
return self
|
|
504
637
|
|
|
505
|
-
def reverse_points(self):
|
|
638
|
+
def reverse_points(self) -> Self:
|
|
506
639
|
for mob in self.get_family():
|
|
507
640
|
for key in mob.data:
|
|
508
641
|
mob.data[key] = mob.data[key][::-1]
|
|
509
642
|
return self
|
|
510
643
|
|
|
511
|
-
def get_midpoint(self) ->
|
|
644
|
+
def get_midpoint(self) -> Point3D:
|
|
512
645
|
"""Get coordinates of the middle of the path that forms the :class:`~.OpenGLMobject`.
|
|
513
646
|
|
|
514
647
|
Examples
|
|
@@ -531,18 +664,19 @@ class OpenGLMobject:
|
|
|
531
664
|
"""
|
|
532
665
|
return self.point_from_proportion(0.5)
|
|
533
666
|
|
|
667
|
+
# TODO: name is inconsistent with Mobject.apply_points_function_about_point()
|
|
534
668
|
def apply_points_function(
|
|
535
669
|
self,
|
|
536
|
-
func,
|
|
537
|
-
about_point=None,
|
|
538
|
-
about_edge=ORIGIN,
|
|
539
|
-
works_on_bounding_box=False,
|
|
540
|
-
):
|
|
670
|
+
func: MultiMappingFunction,
|
|
671
|
+
about_point: Point3DLike | None = None,
|
|
672
|
+
about_edge: Vector3DLike | None = ORIGIN,
|
|
673
|
+
works_on_bounding_box: bool = False,
|
|
674
|
+
) -> Self:
|
|
541
675
|
if about_point is None and about_edge is not None:
|
|
542
676
|
about_point = self.get_bounding_box_point(about_edge)
|
|
543
677
|
|
|
544
678
|
for mob in self.get_family():
|
|
545
|
-
arrs = []
|
|
679
|
+
arrs: list[Point3D_Array] = []
|
|
546
680
|
if mob.has_points():
|
|
547
681
|
arrs.append(mob.points)
|
|
548
682
|
if works_on_bounding_box:
|
|
@@ -563,7 +697,7 @@ class OpenGLMobject:
|
|
|
563
697
|
|
|
564
698
|
# Others related to points
|
|
565
699
|
|
|
566
|
-
def match_points(self, mobject):
|
|
700
|
+
def match_points(self, mobject: OpenGLMobject) -> Self:
|
|
567
701
|
"""Edit points, positions, and submobjects to be identical
|
|
568
702
|
to another :class:`~.OpenGLMobject`, while keeping the style unchanged.
|
|
569
703
|
|
|
@@ -581,29 +715,31 @@ class OpenGLMobject:
|
|
|
581
715
|
self.wait(0.5)
|
|
582
716
|
"""
|
|
583
717
|
self.set_points(mobject.points)
|
|
718
|
+
return self
|
|
584
719
|
|
|
585
|
-
def clear_points(self):
|
|
586
|
-
self.
|
|
720
|
+
def clear_points(self) -> Self:
|
|
721
|
+
self.points = np.empty((0, 3))
|
|
722
|
+
return self
|
|
587
723
|
|
|
588
|
-
def get_num_points(self):
|
|
724
|
+
def get_num_points(self) -> int:
|
|
589
725
|
return len(self.points)
|
|
590
726
|
|
|
591
|
-
def get_all_points(self):
|
|
727
|
+
def get_all_points(self) -> Point3D_Array:
|
|
592
728
|
if self.submobjects:
|
|
593
729
|
return np.vstack([sm.points for sm in self.get_family()])
|
|
594
730
|
else:
|
|
595
731
|
return self.points
|
|
596
732
|
|
|
597
|
-
def has_points(self):
|
|
733
|
+
def has_points(self) -> bool:
|
|
598
734
|
return self.get_num_points() > 0
|
|
599
735
|
|
|
600
|
-
def get_bounding_box(self):
|
|
736
|
+
def get_bounding_box(self) -> Point3D_Array:
|
|
601
737
|
if self.needs_new_bounding_box:
|
|
602
738
|
self.bounding_box = self.compute_bounding_box()
|
|
603
739
|
self.needs_new_bounding_box = False
|
|
604
740
|
return self.bounding_box
|
|
605
741
|
|
|
606
|
-
def compute_bounding_box(self):
|
|
742
|
+
def compute_bounding_box(self) -> Point3D_Array:
|
|
607
743
|
all_points = np.vstack(
|
|
608
744
|
[
|
|
609
745
|
self.points,
|
|
@@ -623,7 +759,9 @@ class OpenGLMobject:
|
|
|
623
759
|
mids = (mins + maxs) / 2
|
|
624
760
|
return np.array([mins, mids, maxs])
|
|
625
761
|
|
|
626
|
-
def refresh_bounding_box(
|
|
762
|
+
def refresh_bounding_box(
|
|
763
|
+
self, recurse_down: bool = False, recurse_up: bool = True
|
|
764
|
+
) -> Self:
|
|
627
765
|
for mob in self.get_family(recurse_down):
|
|
628
766
|
mob.needs_new_bounding_box = True
|
|
629
767
|
if recurse_up:
|
|
@@ -631,30 +769,33 @@ class OpenGLMobject:
|
|
|
631
769
|
parent.refresh_bounding_box()
|
|
632
770
|
return self
|
|
633
771
|
|
|
634
|
-
def is_point_touching(
|
|
772
|
+
def is_point_touching(
|
|
773
|
+
self, point: Point3DLike, buff: float = MED_SMALL_BUFF
|
|
774
|
+
) -> bool:
|
|
635
775
|
bb = self.get_bounding_box()
|
|
636
776
|
mins = bb[0] - buff
|
|
637
777
|
maxs = bb[2] + buff
|
|
638
|
-
|
|
778
|
+
rv: bool = (point >= mins).all() and (point <= maxs).all()
|
|
779
|
+
return rv
|
|
639
780
|
|
|
640
781
|
# Family matters
|
|
641
782
|
|
|
642
|
-
def __getitem__(self, value):
|
|
783
|
+
def __getitem__(self, value: int | slice) -> OpenGLMobject:
|
|
643
784
|
if isinstance(value, slice):
|
|
644
785
|
GroupClass = self.get_group_class()
|
|
645
786
|
return GroupClass(*self.split().__getitem__(value))
|
|
646
787
|
return self.split().__getitem__(value)
|
|
647
788
|
|
|
648
|
-
def __iter__(self):
|
|
789
|
+
def __iter__(self) -> Iterator[OpenGLMobject]:
|
|
649
790
|
return iter(self.split())
|
|
650
791
|
|
|
651
|
-
def __len__(self):
|
|
792
|
+
def __len__(self) -> int:
|
|
652
793
|
return len(self.split())
|
|
653
794
|
|
|
654
|
-
def split(self):
|
|
795
|
+
def split(self) -> Sequence[OpenGLMobject]:
|
|
655
796
|
return self.submobjects
|
|
656
797
|
|
|
657
|
-
def assemble_family(self):
|
|
798
|
+
def assemble_family(self) -> Self:
|
|
658
799
|
sub_families = (sm.get_family() for sm in self.submobjects)
|
|
659
800
|
self.family = [self, *uniq_chain(*sub_families)]
|
|
660
801
|
self.refresh_has_updater_status()
|
|
@@ -663,18 +804,16 @@ class OpenGLMobject:
|
|
|
663
804
|
parent.assemble_family()
|
|
664
805
|
return self
|
|
665
806
|
|
|
666
|
-
def get_family(self, recurse=True):
|
|
807
|
+
def get_family(self, recurse: bool = True) -> Sequence[OpenGLMobject]:
|
|
667
808
|
if recurse and hasattr(self, "family"):
|
|
668
809
|
return self.family
|
|
669
810
|
else:
|
|
670
811
|
return [self]
|
|
671
812
|
|
|
672
|
-
def family_members_with_points(self):
|
|
813
|
+
def family_members_with_points(self) -> Sequence[OpenGLMobject]:
|
|
673
814
|
return [m for m in self.get_family() if m.has_points()]
|
|
674
815
|
|
|
675
|
-
def add(
|
|
676
|
-
self, *mobjects: OpenGLMobject, update_parent: bool = False
|
|
677
|
-
) -> OpenGLMobject:
|
|
816
|
+
def add(self, *mobjects: OpenGLMobject, update_parent: bool = False) -> Self:
|
|
678
817
|
"""Add mobjects as submobjects.
|
|
679
818
|
|
|
680
819
|
The mobjects are added to :attr:`submobjects`.
|
|
@@ -725,28 +864,33 @@ class OpenGLMobject:
|
|
|
725
864
|
>>> len(outer.submobjects)
|
|
726
865
|
1
|
|
727
866
|
|
|
867
|
+
Only OpenGLMobjects can be added::
|
|
868
|
+
|
|
869
|
+
>>> outer.add(3)
|
|
870
|
+
Traceback (most recent call last):
|
|
871
|
+
...
|
|
872
|
+
TypeError: Only values of type OpenGLMobject can be added as submobjects of OpenGLMobject, but the value 3 (at index 0) is of type int.
|
|
873
|
+
|
|
728
874
|
Adding an object to itself raises an error::
|
|
729
875
|
|
|
730
876
|
>>> outer.add(outer)
|
|
731
877
|
Traceback (most recent call last):
|
|
732
878
|
...
|
|
733
|
-
ValueError: OpenGLMobject
|
|
879
|
+
ValueError: Cannot add OpenGLMobject as a submobject of itself (at index 0).
|
|
734
880
|
|
|
735
881
|
"""
|
|
736
882
|
if update_parent:
|
|
737
883
|
assert len(mobjects) == 1, "Can't set multiple parents."
|
|
738
884
|
mobjects[0].parent = self
|
|
739
885
|
|
|
740
|
-
|
|
741
|
-
|
|
886
|
+
self._assert_valid_submobjects(mobjects)
|
|
887
|
+
|
|
742
888
|
if any(mobjects.count(elem) > 1 for elem in mobjects):
|
|
743
889
|
logger.warning(
|
|
744
890
|
"Attempted adding some Mobject as a child more than once, "
|
|
745
891
|
"this is not possible. Repetitions are ignored.",
|
|
746
892
|
)
|
|
747
893
|
for mobject in mobjects:
|
|
748
|
-
if not isinstance(mobject, OpenGLMobject):
|
|
749
|
-
raise TypeError("All submobjects must be of type OpenGLMobject")
|
|
750
894
|
if mobject not in self.submobjects:
|
|
751
895
|
self.submobjects.append(mobject)
|
|
752
896
|
if self not in mobject.parents:
|
|
@@ -754,7 +898,9 @@ class OpenGLMobject:
|
|
|
754
898
|
self.assemble_family()
|
|
755
899
|
return self
|
|
756
900
|
|
|
757
|
-
def insert(
|
|
901
|
+
def insert(
|
|
902
|
+
self, index: int, mobject: OpenGLMobject, update_parent: bool = False
|
|
903
|
+
) -> Self:
|
|
758
904
|
"""Inserts a mobject at a specific position into self.submobjects
|
|
759
905
|
|
|
760
906
|
Effectively just calls ``self.submobjects.insert(index, mobject)``,
|
|
@@ -771,15 +917,10 @@ class OpenGLMobject:
|
|
|
771
917
|
update_parent
|
|
772
918
|
Whether or not to set ``mobject.parent`` to ``self``.
|
|
773
919
|
"""
|
|
774
|
-
|
|
775
920
|
if update_parent:
|
|
776
921
|
mobject.parent = self
|
|
777
922
|
|
|
778
|
-
|
|
779
|
-
raise ValueError("OpenGLMobject cannot contain self")
|
|
780
|
-
|
|
781
|
-
if not isinstance(mobject, OpenGLMobject):
|
|
782
|
-
raise TypeError("All submobjects must be of type OpenGLMobject")
|
|
923
|
+
self._assert_valid_submobjects([mobject])
|
|
783
924
|
|
|
784
925
|
if mobject not in self.submobjects:
|
|
785
926
|
self.submobjects.insert(index, mobject)
|
|
@@ -790,9 +931,7 @@ class OpenGLMobject:
|
|
|
790
931
|
self.assemble_family()
|
|
791
932
|
return self
|
|
792
933
|
|
|
793
|
-
def remove(
|
|
794
|
-
self, *mobjects: OpenGLMobject, update_parent: bool = False
|
|
795
|
-
) -> OpenGLMobject:
|
|
934
|
+
def remove(self, *mobjects: OpenGLMobject, update_parent: bool = False) -> Self:
|
|
796
935
|
"""Remove :attr:`submobjects`.
|
|
797
936
|
|
|
798
937
|
The mobjects are removed from :attr:`submobjects`, if they exist.
|
|
@@ -826,7 +965,7 @@ class OpenGLMobject:
|
|
|
826
965
|
self.assemble_family()
|
|
827
966
|
return self
|
|
828
967
|
|
|
829
|
-
def add_to_back(self, *mobjects: OpenGLMobject) ->
|
|
968
|
+
def add_to_back(self, *mobjects: OpenGLMobject) -> Self:
|
|
830
969
|
# NOTE: is the note true OpenGLMobjects?
|
|
831
970
|
"""Add all passed mobjects to the back of the submobjects.
|
|
832
971
|
|
|
@@ -871,10 +1010,12 @@ class OpenGLMobject:
|
|
|
871
1010
|
:meth:`add`
|
|
872
1011
|
|
|
873
1012
|
"""
|
|
1013
|
+
self._assert_valid_submobjects(mobjects)
|
|
874
1014
|
self.submobjects = list_update(mobjects, self.submobjects)
|
|
875
1015
|
return self
|
|
876
1016
|
|
|
877
|
-
def replace_submobject(self, index, new_submob):
|
|
1017
|
+
def replace_submobject(self, index: int, new_submob: OpenGLMobject) -> Self:
|
|
1018
|
+
self._assert_valid_submobjects([new_submob])
|
|
878
1019
|
old_submob = self.submobjects[index]
|
|
879
1020
|
if self in old_submob.parents:
|
|
880
1021
|
old_submob.parents.remove(self)
|
|
@@ -882,36 +1023,14 @@ class OpenGLMobject:
|
|
|
882
1023
|
self.assemble_family()
|
|
883
1024
|
return self
|
|
884
1025
|
|
|
885
|
-
def invert(self, recursive=False):
|
|
886
|
-
"""Inverts the list of :attr:`submobjects`.
|
|
887
|
-
|
|
888
|
-
Parameters
|
|
889
|
-
----------
|
|
890
|
-
recursive
|
|
891
|
-
If ``True``, all submobject lists of this mobject's family are inverted.
|
|
892
|
-
|
|
893
|
-
Examples
|
|
894
|
-
--------
|
|
895
|
-
|
|
896
|
-
.. manim:: InvertSumobjectsExample
|
|
897
|
-
|
|
898
|
-
class InvertSumobjectsExample(Scene):
|
|
899
|
-
def construct(self):
|
|
900
|
-
s = VGroup(*[Dot().shift(i*0.1*RIGHT) for i in range(-20,20)])
|
|
901
|
-
s2 = s.copy()
|
|
902
|
-
s2.invert()
|
|
903
|
-
s2.shift(DOWN)
|
|
904
|
-
self.play(Write(s), Write(s2))
|
|
905
|
-
"""
|
|
906
|
-
if recursive:
|
|
907
|
-
for submob in self.submobjects:
|
|
908
|
-
submob.invert(recursive=True)
|
|
909
|
-
list.reverse(self.submobjects)
|
|
910
|
-
self.assemble_family()
|
|
911
|
-
|
|
912
1026
|
# Submobject organization
|
|
913
1027
|
|
|
914
|
-
def arrange(
|
|
1028
|
+
def arrange(
|
|
1029
|
+
self,
|
|
1030
|
+
direction: Vector3DLike = RIGHT,
|
|
1031
|
+
center: bool = True,
|
|
1032
|
+
**kwargs: Any,
|
|
1033
|
+
) -> Self:
|
|
915
1034
|
"""Sorts :class:`~.OpenGLMobject` next to each other on screen.
|
|
916
1035
|
|
|
917
1036
|
Examples
|
|
@@ -940,14 +1059,14 @@ class OpenGLMobject:
|
|
|
940
1059
|
rows: int | None = None,
|
|
941
1060
|
cols: int | None = None,
|
|
942
1061
|
buff: float | tuple[float, float] = MED_SMALL_BUFF,
|
|
943
|
-
cell_alignment:
|
|
1062
|
+
cell_alignment: Vector3DLike = ORIGIN,
|
|
944
1063
|
row_alignments: str | None = None, # "ucd"
|
|
945
1064
|
col_alignments: str | None = None, # "lcr"
|
|
946
|
-
row_heights:
|
|
947
|
-
col_widths:
|
|
1065
|
+
row_heights: Sequence[float | None] | None = None,
|
|
1066
|
+
col_widths: Sequence[float | None] | None = None,
|
|
948
1067
|
flow_order: str = "rd",
|
|
949
|
-
**kwargs,
|
|
950
|
-
) ->
|
|
1068
|
+
**kwargs: Any,
|
|
1069
|
+
) -> Self:
|
|
951
1070
|
"""Arrange submobjects in a grid.
|
|
952
1071
|
|
|
953
1072
|
Parameters
|
|
@@ -1043,16 +1162,27 @@ class OpenGLMobject:
|
|
|
1043
1162
|
start_pos = self.get_center()
|
|
1044
1163
|
|
|
1045
1164
|
# get cols / rows values if given (implicitly)
|
|
1046
|
-
def init_size(
|
|
1165
|
+
def init_size(
|
|
1166
|
+
num: int | None,
|
|
1167
|
+
alignments: str | None,
|
|
1168
|
+
sizes: Sequence[float | None] | None,
|
|
1169
|
+
name: str,
|
|
1170
|
+
) -> int:
|
|
1047
1171
|
if num is not None:
|
|
1048
1172
|
return num
|
|
1049
1173
|
if alignments is not None:
|
|
1050
1174
|
return len(alignments)
|
|
1051
1175
|
if sizes is not None:
|
|
1052
1176
|
return len(sizes)
|
|
1177
|
+
raise ValueError(
|
|
1178
|
+
f"At least one of the following parameters: '{name}s', "
|
|
1179
|
+
f"'{name}_alignments' or "
|
|
1180
|
+
f"'{name}_{'widths' if name == 'col' else 'heights'}', "
|
|
1181
|
+
"must not be None"
|
|
1182
|
+
)
|
|
1053
1183
|
|
|
1054
|
-
cols = init_size(cols, col_alignments, col_widths)
|
|
1055
|
-
rows = init_size(rows, row_alignments, row_heights)
|
|
1184
|
+
cols = init_size(cols, col_alignments, col_widths, "col")
|
|
1185
|
+
rows = init_size(rows, row_alignments, row_heights, "row")
|
|
1056
1186
|
|
|
1057
1187
|
# calculate rows cols
|
|
1058
1188
|
if rows is None and cols is None:
|
|
@@ -1076,34 +1206,37 @@ class OpenGLMobject:
|
|
|
1076
1206
|
buff_x = buff_y = buff
|
|
1077
1207
|
|
|
1078
1208
|
# Initialize alignments correctly
|
|
1079
|
-
def init_alignments(
|
|
1080
|
-
|
|
1209
|
+
def init_alignments(
|
|
1210
|
+
str_alignments: str | None,
|
|
1211
|
+
num: int,
|
|
1212
|
+
mapping: dict[str, Vector3D],
|
|
1213
|
+
name: str,
|
|
1214
|
+
direction: Vector3D,
|
|
1215
|
+
) -> Sequence[Vector3D]:
|
|
1216
|
+
if str_alignments is None:
|
|
1081
1217
|
# Use cell_alignment as fallback
|
|
1082
|
-
return [cell_alignment *
|
|
1083
|
-
if len(
|
|
1218
|
+
return [cast(Vector3D, cell_alignment * direction)] * num
|
|
1219
|
+
if len(str_alignments) != num:
|
|
1084
1220
|
raise ValueError(f"{name}_alignments has a mismatching size.")
|
|
1085
|
-
|
|
1086
|
-
for i in range(num):
|
|
1087
|
-
alignments[i] = mapping[alignments[i]]
|
|
1088
|
-
return alignments
|
|
1221
|
+
return [mapping[letter] for letter in str_alignments]
|
|
1089
1222
|
|
|
1090
|
-
|
|
1223
|
+
row_alignments_seq: Sequence[Vector3D] = init_alignments(
|
|
1091
1224
|
row_alignments,
|
|
1092
1225
|
rows,
|
|
1093
1226
|
{"u": UP, "c": ORIGIN, "d": DOWN},
|
|
1094
1227
|
"row",
|
|
1095
1228
|
RIGHT,
|
|
1096
1229
|
)
|
|
1097
|
-
|
|
1230
|
+
col_alignments_seq: Sequence[Vector3D] = init_alignments(
|
|
1098
1231
|
col_alignments,
|
|
1099
1232
|
cols,
|
|
1100
1233
|
{"l": LEFT, "c": ORIGIN, "r": RIGHT},
|
|
1101
1234
|
"col",
|
|
1102
1235
|
UP,
|
|
1103
1236
|
)
|
|
1104
|
-
# Now
|
|
1237
|
+
# Now row_alignments_seq[r] + col_alignment_seq[c] is the alignment in cell [r][c]
|
|
1105
1238
|
|
|
1106
|
-
mapper = {
|
|
1239
|
+
mapper: dict[str, Callable[[int, int], int]] = {
|
|
1107
1240
|
"dr": lambda r, c: (rows - r - 1) + c * rows,
|
|
1108
1241
|
"dl": lambda r, c: (rows - r - 1) + (cols - c - 1) * rows,
|
|
1109
1242
|
"ur": lambda r, c: r + c * rows,
|
|
@@ -1114,20 +1247,31 @@ class OpenGLMobject:
|
|
|
1114
1247
|
"lu": lambda r, c: r * cols + (cols - c - 1),
|
|
1115
1248
|
}
|
|
1116
1249
|
if flow_order not in mapper:
|
|
1250
|
+
valid_flow_orders = ",".join([f'"{key}"' for key in mapper])
|
|
1117
1251
|
raise ValueError(
|
|
1118
|
-
|
|
1252
|
+
f"flow_order must be one of the following values: {valid_flow_orders}.",
|
|
1119
1253
|
)
|
|
1120
|
-
|
|
1254
|
+
flow_order_func = mapper[flow_order]
|
|
1121
1255
|
|
|
1122
1256
|
# Reverse row_alignments and row_heights. Necessary since the
|
|
1123
1257
|
# grid filling is handled bottom up for simplicity reasons.
|
|
1124
|
-
|
|
1258
|
+
if TYPE_CHECKING:
|
|
1259
|
+
|
|
1260
|
+
@overload
|
|
1261
|
+
def reverse(maybe_list: None) -> None: ...
|
|
1262
|
+
@overload
|
|
1263
|
+
def reverse(maybe_list: Sequence[_T]) -> list[_T]: ...
|
|
1264
|
+
@overload
|
|
1265
|
+
def reverse(maybe_list: Sequence[_T] | None) -> list[_T] | None: ...
|
|
1266
|
+
|
|
1267
|
+
def reverse(maybe_list: Sequence[_T] | None) -> list[_T] | None:
|
|
1125
1268
|
if maybe_list is not None:
|
|
1126
1269
|
maybe_list = list(maybe_list)
|
|
1127
1270
|
maybe_list.reverse()
|
|
1128
1271
|
return maybe_list
|
|
1272
|
+
return None
|
|
1129
1273
|
|
|
1130
|
-
|
|
1274
|
+
row_alignments_seq = reverse(row_alignments_seq)
|
|
1131
1275
|
row_heights = reverse(row_heights)
|
|
1132
1276
|
|
|
1133
1277
|
placeholder = OpenGLMobject()
|
|
@@ -1136,7 +1280,7 @@ class OpenGLMobject:
|
|
|
1136
1280
|
# properties of 0.
|
|
1137
1281
|
|
|
1138
1282
|
mobs.extend([placeholder] * (rows * cols - len(mobs)))
|
|
1139
|
-
grid = [[mobs[
|
|
1283
|
+
grid = [[mobs[flow_order_func(r, c)] for c in range(cols)] for r in range(rows)]
|
|
1140
1284
|
|
|
1141
1285
|
measured_heigths = [
|
|
1142
1286
|
max(grid[r][c].height for c in range(cols)) for r in range(rows)
|
|
@@ -1146,24 +1290,30 @@ class OpenGLMobject:
|
|
|
1146
1290
|
]
|
|
1147
1291
|
|
|
1148
1292
|
# Initialize row_heights / col_widths correctly using measurements as fallback
|
|
1149
|
-
def init_sizes(
|
|
1293
|
+
def init_sizes(
|
|
1294
|
+
sizes: Sequence[float | None] | None,
|
|
1295
|
+
num: int,
|
|
1296
|
+
measures: Sequence[float],
|
|
1297
|
+
name: str,
|
|
1298
|
+
) -> Sequence[float]:
|
|
1150
1299
|
if sizes is None:
|
|
1151
1300
|
sizes = [None] * num
|
|
1152
1301
|
if len(sizes) != num:
|
|
1153
1302
|
raise ValueError(f"{name} has a mismatching size.")
|
|
1154
1303
|
return [
|
|
1155
|
-
|
|
1304
|
+
size if (size := sizes[i]) is not None else measures[i]
|
|
1305
|
+
for i in range(num)
|
|
1156
1306
|
]
|
|
1157
1307
|
|
|
1158
1308
|
heights = init_sizes(row_heights, rows, measured_heigths, "row_heights")
|
|
1159
1309
|
widths = init_sizes(col_widths, cols, measured_widths, "col_widths")
|
|
1160
1310
|
|
|
1161
|
-
x, y = 0, 0
|
|
1311
|
+
x, y = 0.0, 0.0
|
|
1162
1312
|
for r in range(rows):
|
|
1163
|
-
x = 0
|
|
1313
|
+
x = 0.0
|
|
1164
1314
|
for c in range(cols):
|
|
1165
1315
|
if grid[r][c] is not placeholder:
|
|
1166
|
-
alignment =
|
|
1316
|
+
alignment = row_alignments_seq[r] + col_alignments_seq[c]
|
|
1167
1317
|
line = Line(
|
|
1168
1318
|
x * RIGHT + y * UP,
|
|
1169
1319
|
(x + widths[c]) * RIGHT + (y + heights[r]) * UP,
|
|
@@ -1179,7 +1329,13 @@ class OpenGLMobject:
|
|
|
1179
1329
|
self.move_to(start_pos)
|
|
1180
1330
|
return self
|
|
1181
1331
|
|
|
1182
|
-
def get_grid(
|
|
1332
|
+
def get_grid(
|
|
1333
|
+
self,
|
|
1334
|
+
n_rows: int,
|
|
1335
|
+
n_cols: int,
|
|
1336
|
+
height: float | None = None,
|
|
1337
|
+
**kwargs: Any,
|
|
1338
|
+
) -> OpenGLGroup:
|
|
1183
1339
|
"""
|
|
1184
1340
|
Returns a new mobject containing multiple copies of this one
|
|
1185
1341
|
arranged in a grid
|
|
@@ -1190,11 +1346,15 @@ class OpenGLMobject:
|
|
|
1190
1346
|
grid.set_height(height)
|
|
1191
1347
|
return grid
|
|
1192
1348
|
|
|
1193
|
-
def duplicate(self, n: int):
|
|
1194
|
-
"""Returns an :class:`~.
|
|
1349
|
+
def duplicate(self, n: int) -> OpenGLGroup:
|
|
1350
|
+
"""Returns an :class:`~.OpenGLGroup` containing ``n`` copies of the mobject."""
|
|
1195
1351
|
return self.get_group_class()(*[self.copy() for _ in range(n)])
|
|
1196
1352
|
|
|
1197
|
-
def sort(
|
|
1353
|
+
def sort(
|
|
1354
|
+
self,
|
|
1355
|
+
point_to_num_func: Callable[[Point3DLike], float] = lambda p: p[0],
|
|
1356
|
+
submob_func: Callable[[OpenGLMobject], Any] | None = None,
|
|
1357
|
+
) -> Self:
|
|
1198
1358
|
"""Sorts the list of :attr:`submobjects` by a function defined by ``submob_func``."""
|
|
1199
1359
|
if submob_func is not None:
|
|
1200
1360
|
self.submobjects.sort(key=submob_func)
|
|
@@ -1202,7 +1362,7 @@ class OpenGLMobject:
|
|
|
1202
1362
|
self.submobjects.sort(key=lambda m: point_to_num_func(m.get_center()))
|
|
1203
1363
|
return self
|
|
1204
1364
|
|
|
1205
|
-
def shuffle(self, recurse=False):
|
|
1365
|
+
def shuffle(self, recurse: bool = False) -> Self:
|
|
1206
1366
|
"""Shuffles the order of :attr:`submobjects`
|
|
1207
1367
|
|
|
1208
1368
|
Examples
|
|
@@ -1225,7 +1385,7 @@ class OpenGLMobject:
|
|
|
1225
1385
|
self.assemble_family()
|
|
1226
1386
|
return self
|
|
1227
1387
|
|
|
1228
|
-
def invert(self, recursive=False):
|
|
1388
|
+
def invert(self, recursive: bool = False) -> Self:
|
|
1229
1389
|
"""Inverts the list of :attr:`submobjects`.
|
|
1230
1390
|
|
|
1231
1391
|
Parameters
|
|
@@ -1249,11 +1409,13 @@ class OpenGLMobject:
|
|
|
1249
1409
|
if recursive:
|
|
1250
1410
|
for submob in self.submobjects:
|
|
1251
1411
|
submob.invert(recursive=True)
|
|
1252
|
-
|
|
1412
|
+
self.submobjects.reverse()
|
|
1413
|
+
self.assemble_family()
|
|
1414
|
+
return self
|
|
1253
1415
|
|
|
1254
1416
|
# Copying
|
|
1255
1417
|
|
|
1256
|
-
def copy(self, shallow: bool = False):
|
|
1418
|
+
def copy(self, shallow: bool = False) -> OpenGLMobject:
|
|
1257
1419
|
"""Create and return an identical copy of the :class:`OpenGLMobject` including all
|
|
1258
1420
|
:attr:`submobjects`.
|
|
1259
1421
|
|
|
@@ -1311,95 +1473,103 @@ class OpenGLMobject:
|
|
|
1311
1473
|
# setattr(copy_mobject, attr, value.copy())
|
|
1312
1474
|
return copy_mobject
|
|
1313
1475
|
|
|
1314
|
-
def deepcopy(self):
|
|
1476
|
+
def deepcopy(self) -> OpenGLMobject:
|
|
1315
1477
|
parents = self.parents
|
|
1316
1478
|
self.parents = []
|
|
1317
1479
|
result = copy.deepcopy(self)
|
|
1318
1480
|
self.parents = parents
|
|
1319
1481
|
return result
|
|
1320
1482
|
|
|
1321
|
-
def generate_target(self, use_deepcopy: bool = False):
|
|
1322
|
-
self.target = None # Prevent exponential explosion
|
|
1483
|
+
def generate_target(self, use_deepcopy: bool = False) -> OpenGLMobject:
|
|
1484
|
+
self.target: OpenGLMobject | None = None # Prevent exponential explosion
|
|
1323
1485
|
if use_deepcopy:
|
|
1324
1486
|
self.target = self.deepcopy()
|
|
1325
1487
|
else:
|
|
1326
1488
|
self.target = self.copy()
|
|
1327
1489
|
return self.target
|
|
1328
1490
|
|
|
1329
|
-
def save_state(self, use_deepcopy: bool = False):
|
|
1491
|
+
def save_state(self, use_deepcopy: bool = False) -> Self:
|
|
1330
1492
|
"""Save the current state (position, color & size). Can be restored with :meth:`~.OpenGLMobject.restore`."""
|
|
1331
1493
|
if hasattr(self, "saved_state"):
|
|
1332
1494
|
# Prevent exponential growth of data
|
|
1333
|
-
self.saved_state = None
|
|
1495
|
+
self.saved_state: OpenGLMobject | None = None
|
|
1334
1496
|
if use_deepcopy:
|
|
1335
1497
|
self.saved_state = self.deepcopy()
|
|
1336
1498
|
else:
|
|
1337
1499
|
self.saved_state = self.copy()
|
|
1338
1500
|
return self
|
|
1339
1501
|
|
|
1340
|
-
def restore(self):
|
|
1502
|
+
def restore(self) -> Self:
|
|
1341
1503
|
"""Restores the state that was previously saved with :meth:`~.OpenGLMobject.save_state`."""
|
|
1342
|
-
if not hasattr(self, "saved_state") or self.
|
|
1504
|
+
if not hasattr(self, "saved_state") or self.saved_state is None:
|
|
1343
1505
|
raise Exception("Trying to restore without having saved")
|
|
1344
1506
|
self.become(self.saved_state)
|
|
1345
1507
|
return self
|
|
1346
1508
|
|
|
1347
1509
|
# Updating
|
|
1348
1510
|
|
|
1349
|
-
def init_updaters(self):
|
|
1350
|
-
self.time_based_updaters = []
|
|
1351
|
-
self.non_time_updaters = []
|
|
1352
|
-
self.has_updaters = False
|
|
1353
|
-
self.updating_suspended = False
|
|
1354
|
-
|
|
1355
|
-
def update(self, dt=0, recurse=True):
|
|
1356
|
-
if
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
updater(self)
|
|
1511
|
+
def init_updaters(self) -> None:
|
|
1512
|
+
self.time_based_updaters: list["_TimeBasedUpdater"] = [] # noqa: UP037
|
|
1513
|
+
self.non_time_updaters: list["_NonTimeBasedUpdater"] = [] # noqa: UP037
|
|
1514
|
+
self.has_updaters: bool = False
|
|
1515
|
+
self.updating_suspended: bool = False
|
|
1516
|
+
|
|
1517
|
+
def update(self, dt: float = 0, recurse: bool = True) -> Self:
|
|
1518
|
+
if self.has_updaters and not self.updating_suspended:
|
|
1519
|
+
for time_based_updater in self.time_based_updaters:
|
|
1520
|
+
time_based_updater(self, dt)
|
|
1521
|
+
for non_time_updater in self.non_time_updaters:
|
|
1522
|
+
non_time_updater(self)
|
|
1362
1523
|
if recurse:
|
|
1363
1524
|
for submob in self.submobjects:
|
|
1364
1525
|
submob.update(dt, recurse)
|
|
1365
1526
|
return self
|
|
1366
1527
|
|
|
1367
|
-
def get_time_based_updaters(self):
|
|
1528
|
+
def get_time_based_updaters(self) -> Sequence[_TimeBasedUpdater]:
|
|
1368
1529
|
return self.time_based_updaters
|
|
1369
1530
|
|
|
1370
|
-
def has_time_based_updater(self):
|
|
1531
|
+
def has_time_based_updater(self) -> bool:
|
|
1371
1532
|
return len(self.time_based_updaters) > 0
|
|
1372
1533
|
|
|
1373
|
-
def get_updaters(self):
|
|
1374
|
-
return self.time_based_updaters +
|
|
1534
|
+
def get_updaters(self) -> Sequence[_Updater]:
|
|
1535
|
+
return cast("list[_Updater]", self.time_based_updaters) + cast(
|
|
1536
|
+
"list[_Updater]", self.non_time_updaters
|
|
1537
|
+
)
|
|
1375
1538
|
|
|
1376
|
-
def get_family_updaters(self):
|
|
1539
|
+
def get_family_updaters(self) -> Sequence[_Updater]:
|
|
1377
1540
|
return list(it.chain(*(sm.get_updaters() for sm in self.get_family())))
|
|
1378
1541
|
|
|
1379
|
-
def add_updater(
|
|
1380
|
-
|
|
1542
|
+
def add_updater(
|
|
1543
|
+
self,
|
|
1544
|
+
update_function: _Updater,
|
|
1545
|
+
index: int | None = None,
|
|
1546
|
+
call_updater: bool = False,
|
|
1547
|
+
) -> Self:
|
|
1548
|
+
updater_list: list[_TimeBasedUpdater] | list[_NonTimeBasedUpdater]
|
|
1549
|
+
if "dt" in inspect.signature(update_function).parameters:
|
|
1381
1550
|
updater_list = self.time_based_updaters
|
|
1382
1551
|
else:
|
|
1383
1552
|
updater_list = self.non_time_updaters
|
|
1384
1553
|
|
|
1385
1554
|
if index is None:
|
|
1386
|
-
updater_list.append(update_function)
|
|
1555
|
+
cast("list[_Updater]", updater_list).append(update_function)
|
|
1387
1556
|
else:
|
|
1388
|
-
updater_list.insert(index, update_function)
|
|
1557
|
+
cast("list[_Updater]", updater_list).insert(index, update_function)
|
|
1389
1558
|
|
|
1390
1559
|
self.refresh_has_updater_status()
|
|
1391
1560
|
if call_updater:
|
|
1392
1561
|
self.update()
|
|
1393
1562
|
return self
|
|
1394
1563
|
|
|
1395
|
-
def remove_updater(self, update_function):
|
|
1564
|
+
def remove_updater(self, update_function: _Updater) -> Self:
|
|
1396
1565
|
for updater_list in [self.time_based_updaters, self.non_time_updaters]:
|
|
1566
|
+
updater_list = cast("list[_Updater]", updater_list)
|
|
1397
1567
|
while update_function in updater_list:
|
|
1398
1568
|
updater_list.remove(update_function)
|
|
1399
1569
|
self.refresh_has_updater_status()
|
|
1400
1570
|
return self
|
|
1401
1571
|
|
|
1402
|
-
def clear_updaters(self, recurse=True):
|
|
1572
|
+
def clear_updaters(self, recurse: bool = True) -> Self:
|
|
1403
1573
|
self.time_based_updaters = []
|
|
1404
1574
|
self.non_time_updaters = []
|
|
1405
1575
|
self.refresh_has_updater_status()
|
|
@@ -1408,20 +1578,20 @@ class OpenGLMobject:
|
|
|
1408
1578
|
submob.clear_updaters()
|
|
1409
1579
|
return self
|
|
1410
1580
|
|
|
1411
|
-
def match_updaters(self, mobject):
|
|
1581
|
+
def match_updaters(self, mobject: OpenGLMobject) -> Self:
|
|
1412
1582
|
self.clear_updaters()
|
|
1413
1583
|
for updater in mobject.get_updaters():
|
|
1414
1584
|
self.add_updater(updater)
|
|
1415
1585
|
return self
|
|
1416
1586
|
|
|
1417
|
-
def suspend_updating(self, recurse=True):
|
|
1587
|
+
def suspend_updating(self, recurse: bool = True) -> Self:
|
|
1418
1588
|
self.updating_suspended = True
|
|
1419
1589
|
if recurse:
|
|
1420
1590
|
for submob in self.submobjects:
|
|
1421
1591
|
submob.suspend_updating(recurse)
|
|
1422
1592
|
return self
|
|
1423
1593
|
|
|
1424
|
-
def resume_updating(self, recurse=True, call_updater=True):
|
|
1594
|
+
def resume_updating(self, recurse: bool = True, call_updater: bool = True) -> Self:
|
|
1425
1595
|
self.updating_suspended = False
|
|
1426
1596
|
if recurse:
|
|
1427
1597
|
for submob in self.submobjects:
|
|
@@ -1432,13 +1602,13 @@ class OpenGLMobject:
|
|
|
1432
1602
|
self.update(dt=0, recurse=recurse)
|
|
1433
1603
|
return self
|
|
1434
1604
|
|
|
1435
|
-
def refresh_has_updater_status(self):
|
|
1605
|
+
def refresh_has_updater_status(self) -> Self:
|
|
1436
1606
|
self.has_updaters = any(mob.get_updaters() for mob in self.get_family())
|
|
1437
1607
|
return self
|
|
1438
1608
|
|
|
1439
1609
|
# Transforming operations
|
|
1440
1610
|
|
|
1441
|
-
def shift(self, vector):
|
|
1611
|
+
def shift(self, vector: Vector3DLike) -> Self:
|
|
1442
1612
|
self.apply_points_function(
|
|
1443
1613
|
lambda points: points + vector,
|
|
1444
1614
|
about_edge=None,
|
|
@@ -1449,10 +1619,10 @@ class OpenGLMobject:
|
|
|
1449
1619
|
def scale(
|
|
1450
1620
|
self,
|
|
1451
1621
|
scale_factor: float,
|
|
1452
|
-
about_point:
|
|
1453
|
-
about_edge:
|
|
1454
|
-
**
|
|
1455
|
-
) ->
|
|
1622
|
+
about_point: Point3DLike | None = None,
|
|
1623
|
+
about_edge: Point3DLike | None = ORIGIN,
|
|
1624
|
+
**_kwargs: object,
|
|
1625
|
+
) -> Self:
|
|
1456
1626
|
r"""Scale the size by a factor.
|
|
1457
1627
|
|
|
1458
1628
|
Default behavior is to scale about the center of the mobject.
|
|
@@ -1471,7 +1641,7 @@ class OpenGLMobject:
|
|
|
1471
1641
|
if :math:`\alpha < 0`, the mobject is also flipped.
|
|
1472
1642
|
kwargs
|
|
1473
1643
|
Additional keyword arguments passed to
|
|
1474
|
-
:meth:`
|
|
1644
|
+
:meth:`apply_points_function`.
|
|
1475
1645
|
|
|
1476
1646
|
Returns
|
|
1477
1647
|
-------
|
|
@@ -1504,28 +1674,27 @@ class OpenGLMobject:
|
|
|
1504
1674
|
about_point=about_point,
|
|
1505
1675
|
about_edge=about_edge,
|
|
1506
1676
|
works_on_bounding_box=True,
|
|
1507
|
-
**kwargs,
|
|
1508
1677
|
)
|
|
1509
1678
|
return self
|
|
1510
1679
|
|
|
1511
|
-
def stretch(self, factor, dim, **kwargs):
|
|
1512
|
-
def func(points):
|
|
1680
|
+
def stretch(self, factor: float, dim: int, **kwargs: Any) -> Self:
|
|
1681
|
+
def func(points: Point3D_Array) -> Point3D_Array:
|
|
1513
1682
|
points[:, dim] *= factor
|
|
1514
1683
|
return points
|
|
1515
1684
|
|
|
1516
1685
|
self.apply_points_function(func, works_on_bounding_box=True, **kwargs)
|
|
1517
1686
|
return self
|
|
1518
1687
|
|
|
1519
|
-
def rotate_about_origin(self, angle, axis=OUT):
|
|
1688
|
+
def rotate_about_origin(self, angle: float, axis: Vector3DLike = OUT) -> Self:
|
|
1520
1689
|
return self.rotate(angle, axis, about_point=ORIGIN)
|
|
1521
1690
|
|
|
1522
1691
|
def rotate(
|
|
1523
1692
|
self,
|
|
1524
|
-
angle,
|
|
1525
|
-
axis=OUT,
|
|
1526
|
-
about_point:
|
|
1527
|
-
**kwargs,
|
|
1528
|
-
):
|
|
1693
|
+
angle: float,
|
|
1694
|
+
axis: Vector3DLike = OUT,
|
|
1695
|
+
about_point: Point3DLike | None = None,
|
|
1696
|
+
**kwargs: Any,
|
|
1697
|
+
) -> Self:
|
|
1529
1698
|
"""Rotates the :class:`~.OpenGLMobject` about a certain point."""
|
|
1530
1699
|
rot_matrix_T = rotation_matrix_transpose(angle, axis)
|
|
1531
1700
|
self.apply_points_function(
|
|
@@ -1535,7 +1704,7 @@ class OpenGLMobject:
|
|
|
1535
1704
|
)
|
|
1536
1705
|
return self
|
|
1537
1706
|
|
|
1538
|
-
def flip(self, axis=UP, **kwargs):
|
|
1707
|
+
def flip(self, axis: Vector3DLike = UP, **kwargs: Any) -> Self:
|
|
1539
1708
|
"""Flips/Mirrors an mobject about its center.
|
|
1540
1709
|
|
|
1541
1710
|
Examples
|
|
@@ -1554,25 +1723,28 @@ class OpenGLMobject:
|
|
|
1554
1723
|
"""
|
|
1555
1724
|
return self.rotate(TAU / 2, axis, **kwargs)
|
|
1556
1725
|
|
|
1557
|
-
def apply_function(self, function, **kwargs):
|
|
1726
|
+
def apply_function(self, function: MappingFunction, **kwargs: Any) -> Self:
|
|
1558
1727
|
# Default to applying matrix about the origin, not mobjects center
|
|
1559
1728
|
if len(kwargs) == 0:
|
|
1560
1729
|
kwargs["about_point"] = ORIGIN
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1730
|
+
|
|
1731
|
+
def multi_mapping_function(points: Point3D_Array) -> Point3D_Array:
|
|
1732
|
+
result: Point3D_Array = np.apply_along_axis(function, 1, points)
|
|
1733
|
+
return result
|
|
1734
|
+
|
|
1735
|
+
self.apply_points_function(multi_mapping_function, **kwargs)
|
|
1564
1736
|
return self
|
|
1565
1737
|
|
|
1566
|
-
def apply_function_to_position(self, function):
|
|
1738
|
+
def apply_function_to_position(self, function: MappingFunction) -> Self:
|
|
1567
1739
|
self.move_to(function(self.get_center()))
|
|
1568
1740
|
return self
|
|
1569
1741
|
|
|
1570
|
-
def apply_function_to_submobject_positions(self, function):
|
|
1742
|
+
def apply_function_to_submobject_positions(self, function: MappingFunction) -> Self:
|
|
1571
1743
|
for submob in self.submobjects:
|
|
1572
1744
|
submob.apply_function_to_position(function)
|
|
1573
1745
|
return self
|
|
1574
1746
|
|
|
1575
|
-
def apply_matrix(self, matrix, **kwargs):
|
|
1747
|
+
def apply_matrix(self, matrix: MatrixMN, **kwargs: Any) -> Self:
|
|
1576
1748
|
# Default to applying matrix about the origin, not mobjects center
|
|
1577
1749
|
if ("about_point" not in kwargs) and ("about_edge" not in kwargs):
|
|
1578
1750
|
kwargs["about_point"] = ORIGIN
|
|
@@ -1584,7 +1756,9 @@ class OpenGLMobject:
|
|
|
1584
1756
|
)
|
|
1585
1757
|
return self
|
|
1586
1758
|
|
|
1587
|
-
def apply_complex_function(
|
|
1759
|
+
def apply_complex_function(
|
|
1760
|
+
self, function: Callable[[complex], complex], **kwargs: Any
|
|
1761
|
+
) -> Self:
|
|
1588
1762
|
"""Applies a complex function to a :class:`OpenGLMobject`.
|
|
1589
1763
|
The x and y coordinates correspond to the real and imaginary parts respectively.
|
|
1590
1764
|
|
|
@@ -1611,14 +1785,14 @@ class OpenGLMobject:
|
|
|
1611
1785
|
self.play(t.animate.set_value(TAU), run_time=3)
|
|
1612
1786
|
"""
|
|
1613
1787
|
|
|
1614
|
-
def R3_func(point):
|
|
1788
|
+
def R3_func(point: Point3D) -> Point3D:
|
|
1615
1789
|
x, y, z = point
|
|
1616
1790
|
xy_complex = function(complex(x, y))
|
|
1617
|
-
return [xy_complex.real, xy_complex.imag, z]
|
|
1791
|
+
return np.array([xy_complex.real, xy_complex.imag, z])
|
|
1618
1792
|
|
|
1619
|
-
return self.apply_function(R3_func)
|
|
1793
|
+
return self.apply_function(R3_func, **kwargs)
|
|
1620
1794
|
|
|
1621
|
-
def hierarchical_model_matrix(self):
|
|
1795
|
+
def hierarchical_model_matrix(self) -> MatrixMN:
|
|
1622
1796
|
if self.parent is None:
|
|
1623
1797
|
return self.model_matrix
|
|
1624
1798
|
|
|
@@ -1629,7 +1803,12 @@ class OpenGLMobject:
|
|
|
1629
1803
|
current_object = current_object.parent
|
|
1630
1804
|
return np.linalg.multi_dot(list(reversed(model_matrices)))
|
|
1631
1805
|
|
|
1632
|
-
def wag(
|
|
1806
|
+
def wag(
|
|
1807
|
+
self,
|
|
1808
|
+
direction: Vector3DLike = RIGHT,
|
|
1809
|
+
axis: Vector3DLike = DOWN,
|
|
1810
|
+
wag_factor: float = 1.0,
|
|
1811
|
+
) -> Self:
|
|
1633
1812
|
for mob in self.family_members_with_points():
|
|
1634
1813
|
alphas = np.dot(mob.points, np.transpose(axis))
|
|
1635
1814
|
alphas -= min(alphas)
|
|
@@ -1646,12 +1825,16 @@ class OpenGLMobject:
|
|
|
1646
1825
|
|
|
1647
1826
|
# Positioning methods
|
|
1648
1827
|
|
|
1649
|
-
def center(self):
|
|
1828
|
+
def center(self) -> Self:
|
|
1650
1829
|
"""Moves the mobject to the center of the Scene."""
|
|
1651
1830
|
self.shift(-self.get_center())
|
|
1652
1831
|
return self
|
|
1653
1832
|
|
|
1654
|
-
def align_on_border(
|
|
1833
|
+
def align_on_border(
|
|
1834
|
+
self,
|
|
1835
|
+
direction: Vector3DLike,
|
|
1836
|
+
buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER,
|
|
1837
|
+
) -> Self:
|
|
1655
1838
|
"""
|
|
1656
1839
|
Direction just needs to be a vector pointing towards side or
|
|
1657
1840
|
corner in the 2d plane.
|
|
@@ -1662,27 +1845,35 @@ class OpenGLMobject:
|
|
|
1662
1845
|
0,
|
|
1663
1846
|
)
|
|
1664
1847
|
point_to_align = self.get_bounding_box_point(direction)
|
|
1665
|
-
shift_val = target_point - point_to_align - buff * np.
|
|
1848
|
+
shift_val = target_point - point_to_align - buff * np.asarray(direction)
|
|
1666
1849
|
shift_val = shift_val * abs(np.sign(direction))
|
|
1667
1850
|
self.shift(shift_val)
|
|
1668
1851
|
return self
|
|
1669
1852
|
|
|
1670
|
-
def to_corner(
|
|
1853
|
+
def to_corner(
|
|
1854
|
+
self,
|
|
1855
|
+
corner: Vector3DLike = LEFT + DOWN,
|
|
1856
|
+
buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER,
|
|
1857
|
+
) -> Self:
|
|
1671
1858
|
return self.align_on_border(corner, buff)
|
|
1672
1859
|
|
|
1673
|
-
def to_edge(
|
|
1860
|
+
def to_edge(
|
|
1861
|
+
self,
|
|
1862
|
+
edge: Vector3DLike = LEFT,
|
|
1863
|
+
buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER,
|
|
1864
|
+
) -> Self:
|
|
1674
1865
|
return self.align_on_border(edge, buff)
|
|
1675
1866
|
|
|
1676
1867
|
def next_to(
|
|
1677
1868
|
self,
|
|
1678
|
-
mobject_or_point,
|
|
1679
|
-
direction=RIGHT,
|
|
1680
|
-
buff=DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
|
|
1681
|
-
aligned_edge=ORIGIN,
|
|
1682
|
-
submobject_to_align=None,
|
|
1683
|
-
index_of_submobject_to_align=None,
|
|
1684
|
-
coor_mask=np.array([1, 1, 1]),
|
|
1685
|
-
):
|
|
1869
|
+
mobject_or_point: OpenGLMobject | Point3DLike,
|
|
1870
|
+
direction: Vector3DLike = RIGHT,
|
|
1871
|
+
buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
|
|
1872
|
+
aligned_edge: Vector3DLike = ORIGIN,
|
|
1873
|
+
submobject_to_align: OpenGLMobject | None = None,
|
|
1874
|
+
index_of_submobject_to_align: int | None = None,
|
|
1875
|
+
coor_mask: Vector3DLike = np.array([1, 1, 1]),
|
|
1876
|
+
) -> Self:
|
|
1686
1877
|
"""Move this :class:`~.OpenGLMobject` next to another's :class:`~.OpenGLMobject` or coordinate.
|
|
1687
1878
|
|
|
1688
1879
|
Examples
|
|
@@ -1703,6 +1894,10 @@ class OpenGLMobject:
|
|
|
1703
1894
|
self.add(d, c, s, t)
|
|
1704
1895
|
|
|
1705
1896
|
"""
|
|
1897
|
+
np_direction = np.asarray(direction)
|
|
1898
|
+
np_aligned_edge = np.asarray(aligned_edge)
|
|
1899
|
+
|
|
1900
|
+
target_point: Point3DLike
|
|
1706
1901
|
if isinstance(mobject_or_point, OpenGLMobject):
|
|
1707
1902
|
mob = mobject_or_point
|
|
1708
1903
|
if index_of_submobject_to_align is not None:
|
|
@@ -1710,7 +1905,7 @@ class OpenGLMobject:
|
|
|
1710
1905
|
else:
|
|
1711
1906
|
target_aligner = mob
|
|
1712
1907
|
target_point = target_aligner.get_bounding_box_point(
|
|
1713
|
-
|
|
1908
|
+
np_aligned_edge + np_direction,
|
|
1714
1909
|
)
|
|
1715
1910
|
else:
|
|
1716
1911
|
target_point = mobject_or_point
|
|
@@ -1720,36 +1915,43 @@ class OpenGLMobject:
|
|
|
1720
1915
|
aligner = self[index_of_submobject_to_align]
|
|
1721
1916
|
else:
|
|
1722
1917
|
aligner = self
|
|
1723
|
-
point_to_align = aligner.get_bounding_box_point(
|
|
1724
|
-
self.shift((target_point - point_to_align + buff *
|
|
1918
|
+
point_to_align = aligner.get_bounding_box_point(np_aligned_edge - np_direction)
|
|
1919
|
+
self.shift((target_point - point_to_align + buff * np_direction) * coor_mask)
|
|
1725
1920
|
return self
|
|
1726
1921
|
|
|
1727
|
-
def shift_onto_screen(self, **kwargs):
|
|
1728
|
-
space_lengths
|
|
1922
|
+
def shift_onto_screen(self, **kwargs: Any) -> Self:
|
|
1923
|
+
space_lengths: list[float] = [
|
|
1924
|
+
config["frame_x_radius"],
|
|
1925
|
+
config["frame_y_radius"],
|
|
1926
|
+
]
|
|
1729
1927
|
for vect in UP, DOWN, LEFT, RIGHT:
|
|
1730
1928
|
dim = np.argmax(np.abs(vect))
|
|
1731
|
-
buff = kwargs.get("buff", DEFAULT_MOBJECT_TO_EDGE_BUFFER)
|
|
1929
|
+
buff: float = kwargs.get("buff", DEFAULT_MOBJECT_TO_EDGE_BUFFER)
|
|
1732
1930
|
max_val = space_lengths[dim] - buff
|
|
1733
1931
|
edge_center = self.get_edge_center(vect)
|
|
1734
1932
|
if np.dot(edge_center, vect) > max_val:
|
|
1735
|
-
self.to_edge(vect,
|
|
1933
|
+
self.to_edge(vect, buff=buff)
|
|
1736
1934
|
return self
|
|
1737
1935
|
|
|
1738
|
-
def is_off_screen(self):
|
|
1936
|
+
def is_off_screen(self) -> bool:
|
|
1739
1937
|
if self.get_left()[0] > config.frame_x_radius:
|
|
1740
1938
|
return True
|
|
1741
1939
|
if self.get_right()[0] < config.frame_x_radius:
|
|
1742
1940
|
return True
|
|
1743
1941
|
if self.get_bottom()[1] > config.frame_y_radius:
|
|
1744
1942
|
return True
|
|
1745
|
-
|
|
1746
|
-
return True
|
|
1747
|
-
return False
|
|
1943
|
+
return cast(float, self.get_top()[1]) < -config.frame_y_radius
|
|
1748
1944
|
|
|
1749
|
-
def stretch_about_point(self, factor, dim, point):
|
|
1945
|
+
def stretch_about_point(self, factor: float, dim: int, point: Point3DLike) -> Self:
|
|
1750
1946
|
return self.stretch(factor, dim, about_point=point)
|
|
1751
1947
|
|
|
1752
|
-
def rescale_to_fit(
|
|
1948
|
+
def rescale_to_fit(
|
|
1949
|
+
self,
|
|
1950
|
+
length: float,
|
|
1951
|
+
dim: int,
|
|
1952
|
+
stretch: bool = False,
|
|
1953
|
+
**kwargs: Any,
|
|
1954
|
+
) -> Self:
|
|
1753
1955
|
old_length = self.length_over_dim(dim)
|
|
1754
1956
|
if old_length == 0:
|
|
1755
1957
|
return self
|
|
@@ -1759,7 +1961,7 @@ class OpenGLMobject:
|
|
|
1759
1961
|
self.scale(length / old_length, **kwargs)
|
|
1760
1962
|
return self
|
|
1761
1963
|
|
|
1762
|
-
def stretch_to_fit_width(self, width, **kwargs):
|
|
1964
|
+
def stretch_to_fit_width(self, width: float, **kwargs: Any) -> Self:
|
|
1763
1965
|
"""Stretches the :class:`~.OpenGLMobject` to fit a width, not keeping height/depth proportional.
|
|
1764
1966
|
|
|
1765
1967
|
Returns
|
|
@@ -1772,27 +1974,33 @@ class OpenGLMobject:
|
|
|
1772
1974
|
::
|
|
1773
1975
|
|
|
1774
1976
|
>>> from manim import *
|
|
1977
|
+
>>> import numpy as np
|
|
1775
1978
|
>>> sq = Square()
|
|
1776
1979
|
>>> sq.height
|
|
1777
|
-
2.0
|
|
1980
|
+
np.float64(2.0)
|
|
1778
1981
|
>>> sq.stretch_to_fit_width(5)
|
|
1779
1982
|
Square
|
|
1780
1983
|
>>> sq.width
|
|
1781
|
-
5.0
|
|
1984
|
+
np.float64(5.0)
|
|
1782
1985
|
>>> sq.height
|
|
1783
|
-
2.0
|
|
1986
|
+
np.float64(2.0)
|
|
1784
1987
|
"""
|
|
1785
1988
|
return self.rescale_to_fit(width, 0, stretch=True, **kwargs)
|
|
1786
1989
|
|
|
1787
|
-
def stretch_to_fit_height(self, height, **kwargs):
|
|
1990
|
+
def stretch_to_fit_height(self, height: float, **kwargs: Any) -> Self:
|
|
1788
1991
|
"""Stretches the :class:`~.OpenGLMobject` to fit a height, not keeping width/height proportional."""
|
|
1789
1992
|
return self.rescale_to_fit(height, 1, stretch=True, **kwargs)
|
|
1790
1993
|
|
|
1791
|
-
def stretch_to_fit_depth(self, depth, **kwargs):
|
|
1994
|
+
def stretch_to_fit_depth(self, depth: float, **kwargs: Any) -> Self:
|
|
1792
1995
|
"""Stretches the :class:`~.OpenGLMobject` to fit a depth, not keeping width/height proportional."""
|
|
1793
1996
|
return self.rescale_to_fit(depth, 1, stretch=True, **kwargs)
|
|
1794
1997
|
|
|
1795
|
-
def set_width(
|
|
1998
|
+
def set_width(
|
|
1999
|
+
self,
|
|
2000
|
+
width: float,
|
|
2001
|
+
stretch: bool = False,
|
|
2002
|
+
**kwargs: Any,
|
|
2003
|
+
) -> Self:
|
|
1796
2004
|
"""Scales the :class:`~.OpenGLMobject` to fit a width while keeping height/depth proportional.
|
|
1797
2005
|
|
|
1798
2006
|
Returns
|
|
@@ -1805,52 +2013,65 @@ class OpenGLMobject:
|
|
|
1805
2013
|
::
|
|
1806
2014
|
|
|
1807
2015
|
>>> from manim import *
|
|
2016
|
+
>>> import numpy as np
|
|
1808
2017
|
>>> sq = Square()
|
|
1809
2018
|
>>> sq.height
|
|
1810
|
-
2.0
|
|
2019
|
+
np.float64(2.0)
|
|
1811
2020
|
>>> sq.scale_to_fit_width(5)
|
|
1812
2021
|
Square
|
|
1813
2022
|
>>> sq.width
|
|
1814
|
-
5.0
|
|
2023
|
+
np.float64(5.0)
|
|
1815
2024
|
>>> sq.height
|
|
1816
|
-
5.0
|
|
2025
|
+
np.float64(5.0)
|
|
1817
2026
|
"""
|
|
1818
2027
|
return self.rescale_to_fit(width, 0, stretch=stretch, **kwargs)
|
|
1819
2028
|
|
|
1820
2029
|
scale_to_fit_width = set_width
|
|
1821
2030
|
|
|
1822
|
-
def set_height(
|
|
2031
|
+
def set_height(
|
|
2032
|
+
self,
|
|
2033
|
+
height: float,
|
|
2034
|
+
stretch: bool = False,
|
|
2035
|
+
**kwargs: Any,
|
|
2036
|
+
) -> Self:
|
|
1823
2037
|
"""Scales the :class:`~.OpenGLMobject` to fit a height while keeping width/depth proportional."""
|
|
1824
2038
|
return self.rescale_to_fit(height, 1, stretch=stretch, **kwargs)
|
|
1825
2039
|
|
|
1826
2040
|
scale_to_fit_height = set_height
|
|
1827
2041
|
|
|
1828
|
-
def set_depth(
|
|
2042
|
+
def set_depth(
|
|
2043
|
+
self,
|
|
2044
|
+
depth: float,
|
|
2045
|
+
stretch: bool = False,
|
|
2046
|
+
**kwargs: Any,
|
|
2047
|
+
) -> Self:
|
|
1829
2048
|
"""Scales the :class:`~.OpenGLMobject` to fit a depth while keeping width/height proportional."""
|
|
1830
2049
|
return self.rescale_to_fit(depth, 2, stretch=stretch, **kwargs)
|
|
1831
2050
|
|
|
1832
2051
|
scale_to_fit_depth = set_depth
|
|
1833
2052
|
|
|
1834
|
-
def set_coord(
|
|
2053
|
+
def set_coord(
|
|
2054
|
+
self, value: float, dim: int, direction: Vector3DLike = ORIGIN
|
|
2055
|
+
) -> Self:
|
|
1835
2056
|
curr = self.get_coord(dim, direction)
|
|
1836
2057
|
shift_vect = np.zeros(self.dim)
|
|
1837
2058
|
shift_vect[dim] = value - curr
|
|
1838
2059
|
self.shift(shift_vect)
|
|
1839
2060
|
return self
|
|
1840
2061
|
|
|
1841
|
-
def set_x(self, x, direction=ORIGIN):
|
|
2062
|
+
def set_x(self, x: float, direction: Vector3DLike = ORIGIN) -> Self:
|
|
1842
2063
|
"""Set x value of the center of the :class:`~.OpenGLMobject` (``int`` or ``float``)"""
|
|
1843
2064
|
return self.set_coord(x, 0, direction)
|
|
1844
2065
|
|
|
1845
|
-
def set_y(self, y, direction=ORIGIN):
|
|
2066
|
+
def set_y(self, y: float, direction: Vector3DLike = ORIGIN) -> Self:
|
|
1846
2067
|
"""Set y value of the center of the :class:`~.OpenGLMobject` (``int`` or ``float``)"""
|
|
1847
2068
|
return self.set_coord(y, 1, direction)
|
|
1848
2069
|
|
|
1849
|
-
def set_z(self, z, direction=ORIGIN):
|
|
2070
|
+
def set_z(self, z: float, direction: Vector3DLike = ORIGIN) -> Self:
|
|
1850
2071
|
"""Set z value of the center of the :class:`~.OpenGLMobject` (``int`` or ``float``)"""
|
|
1851
2072
|
return self.set_coord(z, 2, direction)
|
|
1852
2073
|
|
|
1853
|
-
def space_out_submobjects(self, factor=1.5, **kwargs):
|
|
2074
|
+
def space_out_submobjects(self, factor: float = 1.5, **kwargs: Any) -> Self:
|
|
1854
2075
|
self.scale(factor, **kwargs)
|
|
1855
2076
|
for submob in self.submobjects:
|
|
1856
2077
|
submob.scale(1.0 / factor)
|
|
@@ -1858,11 +2079,12 @@ class OpenGLMobject:
|
|
|
1858
2079
|
|
|
1859
2080
|
def move_to(
|
|
1860
2081
|
self,
|
|
1861
|
-
point_or_mobject,
|
|
1862
|
-
aligned_edge=ORIGIN,
|
|
1863
|
-
coor_mask=np.array([1, 1, 1]),
|
|
1864
|
-
):
|
|
2082
|
+
point_or_mobject: Point3DLike | OpenGLMobject,
|
|
2083
|
+
aligned_edge: Vector3DLike = ORIGIN,
|
|
2084
|
+
coor_mask: Vector3DLike = np.array([1, 1, 1]),
|
|
2085
|
+
) -> Self:
|
|
1865
2086
|
"""Move center of the :class:`~.OpenGLMobject` to certain coordinate."""
|
|
2087
|
+
target: Point3DLike
|
|
1866
2088
|
if isinstance(point_or_mobject, OpenGLMobject):
|
|
1867
2089
|
target = point_or_mobject.get_bounding_box_point(aligned_edge)
|
|
1868
2090
|
else:
|
|
@@ -1871,7 +2093,12 @@ class OpenGLMobject:
|
|
|
1871
2093
|
self.shift((target - point_to_align) * coor_mask)
|
|
1872
2094
|
return self
|
|
1873
2095
|
|
|
1874
|
-
def replace(
|
|
2096
|
+
def replace(
|
|
2097
|
+
self,
|
|
2098
|
+
mobject: OpenGLMobject,
|
|
2099
|
+
dim_to_match: int = 0,
|
|
2100
|
+
stretch: bool = False,
|
|
2101
|
+
) -> Self:
|
|
1875
2102
|
if not mobject.get_num_points() and not mobject.submobjects:
|
|
1876
2103
|
self.scale(0)
|
|
1877
2104
|
return self
|
|
@@ -1893,13 +2120,13 @@ class OpenGLMobject:
|
|
|
1893
2120
|
dim_to_match: int = 0,
|
|
1894
2121
|
stretch: bool = False,
|
|
1895
2122
|
buff: float = MED_SMALL_BUFF,
|
|
1896
|
-
):
|
|
2123
|
+
) -> Self:
|
|
1897
2124
|
self.replace(mobject, dim_to_match, stretch)
|
|
1898
2125
|
length = mobject.length_over_dim(dim_to_match)
|
|
1899
2126
|
self.scale((length + buff) / length)
|
|
1900
2127
|
return self
|
|
1901
2128
|
|
|
1902
|
-
def put_start_and_end_on(self, start, end):
|
|
2129
|
+
def put_start_and_end_on(self, start: Point3DLike, end: Point3DLike) -> Self:
|
|
1903
2130
|
curr_start, curr_end = self.get_start_and_end()
|
|
1904
2131
|
curr_vect = curr_end - curr_start
|
|
1905
2132
|
if np.all(curr_vect == 0):
|
|
@@ -1911,7 +2138,7 @@ class OpenGLMobject:
|
|
|
1911
2138
|
else OUT
|
|
1912
2139
|
)
|
|
1913
2140
|
self.scale(
|
|
1914
|
-
np.linalg.norm(target_vect) / np.linalg.norm(curr_vect),
|
|
2141
|
+
float(np.linalg.norm(target_vect) / np.linalg.norm(curr_vect)),
|
|
1915
2142
|
about_point=curr_start,
|
|
1916
2143
|
)
|
|
1917
2144
|
self.rotate(
|
|
@@ -1924,9 +2151,15 @@ class OpenGLMobject:
|
|
|
1924
2151
|
|
|
1925
2152
|
# Color functions
|
|
1926
2153
|
|
|
1927
|
-
def set_rgba_array(
|
|
2154
|
+
def set_rgba_array(
|
|
2155
|
+
self,
|
|
2156
|
+
color: ParsableManimColor | Iterable[ParsableManimColor] | None = None,
|
|
2157
|
+
opacity: float | Iterable[float] | None = None,
|
|
2158
|
+
name: str = "rgbas",
|
|
2159
|
+
recurse: bool = True,
|
|
2160
|
+
) -> Self:
|
|
1928
2161
|
if color is not None:
|
|
1929
|
-
rgbs = np.array([color_to_rgb(c) for c in listify(color)])
|
|
2162
|
+
rgbs: FloatRGB_Array = np.array([color_to_rgb(c) for c in listify(color)])
|
|
1930
2163
|
if opacity is not None:
|
|
1931
2164
|
opacities = listify(opacity)
|
|
1932
2165
|
|
|
@@ -1949,12 +2182,19 @@ class OpenGLMobject:
|
|
|
1949
2182
|
|
|
1950
2183
|
# Color and opacity
|
|
1951
2184
|
if color is not None and opacity is not None:
|
|
1952
|
-
rgbas = np.array(
|
|
2185
|
+
rgbas: FloatRGBA_Array = np.array(
|
|
2186
|
+
[[*rgb, o] for rgb, o in zip(*make_even(rgbs, opacities))]
|
|
2187
|
+
)
|
|
1953
2188
|
for mob in self.get_family(recurse):
|
|
1954
2189
|
mob.data[name] = rgbas.copy()
|
|
1955
2190
|
return self
|
|
1956
2191
|
|
|
1957
|
-
def set_rgba_array_direct(
|
|
2192
|
+
def set_rgba_array_direct(
|
|
2193
|
+
self,
|
|
2194
|
+
rgbas: FloatRGBA_Array,
|
|
2195
|
+
name: str = "rgbas",
|
|
2196
|
+
recurse: bool = True,
|
|
2197
|
+
) -> Self:
|
|
1958
2198
|
"""Directly set rgba data from `rgbas` and optionally do the same recursively
|
|
1959
2199
|
with submobjects. This can be used if the `rgbas` have already been generated
|
|
1960
2200
|
with the correct shape and simply need to be set.
|
|
@@ -1970,13 +2210,19 @@ class OpenGLMobject:
|
|
|
1970
2210
|
"""
|
|
1971
2211
|
for mob in self.get_family(recurse):
|
|
1972
2212
|
mob.data[name] = rgbas.copy()
|
|
2213
|
+
return self
|
|
1973
2214
|
|
|
1974
|
-
def set_color(
|
|
2215
|
+
def set_color(
|
|
2216
|
+
self,
|
|
2217
|
+
color: ParsableManimColor | Sequence[ParsableManimColor] | None,
|
|
2218
|
+
opacity: float | Iterable[float] | None = None,
|
|
2219
|
+
recurse: bool = True,
|
|
2220
|
+
) -> Self:
|
|
1975
2221
|
self.set_rgba_array(color, opacity, recurse=False)
|
|
1976
2222
|
# Recurse to submobjects differently from how set_rgba_array
|
|
1977
2223
|
# in case they implement set_color differently
|
|
1978
2224
|
if color is not None:
|
|
1979
|
-
self.color =
|
|
2225
|
+
self.color = ManimColor.parse(color)
|
|
1980
2226
|
if opacity is not None:
|
|
1981
2227
|
self.opacity = opacity
|
|
1982
2228
|
if recurse:
|
|
@@ -1984,24 +2230,26 @@ class OpenGLMobject:
|
|
|
1984
2230
|
submob.set_color(color, recurse=True)
|
|
1985
2231
|
return self
|
|
1986
2232
|
|
|
1987
|
-
def set_opacity(
|
|
2233
|
+
def set_opacity(
|
|
2234
|
+
self, opacity: float | Iterable[float] | None, recurse: bool = True
|
|
2235
|
+
) -> Self:
|
|
1988
2236
|
self.set_rgba_array(color=None, opacity=opacity, recurse=False)
|
|
1989
2237
|
if recurse:
|
|
1990
2238
|
for submob in self.submobjects:
|
|
1991
2239
|
submob.set_opacity(opacity, recurse=True)
|
|
1992
2240
|
return self
|
|
1993
2241
|
|
|
1994
|
-
def get_color(self):
|
|
2242
|
+
def get_color(self) -> str:
|
|
1995
2243
|
return rgb_to_hex(self.rgbas[0, :3])
|
|
1996
2244
|
|
|
1997
|
-
def get_opacity(self):
|
|
1998
|
-
|
|
2245
|
+
def get_opacity(self) -> float:
|
|
2246
|
+
rv: float = self.rgbas[0, 3]
|
|
2247
|
+
return rv
|
|
1999
2248
|
|
|
2000
|
-
def set_color_by_gradient(self, *colors):
|
|
2001
|
-
self.set_submobject_colors_by_gradient(*colors)
|
|
2002
|
-
return self
|
|
2249
|
+
def set_color_by_gradient(self, *colors: ParsableManimColor) -> Self:
|
|
2250
|
+
return self.set_submobject_colors_by_gradient(*colors)
|
|
2003
2251
|
|
|
2004
|
-
def set_submobject_colors_by_gradient(self, *colors):
|
|
2252
|
+
def set_submobject_colors_by_gradient(self, *colors: ParsableManimColor) -> Self:
|
|
2005
2253
|
if len(colors) == 0:
|
|
2006
2254
|
raise Exception("Need at least one color")
|
|
2007
2255
|
elif len(colors) == 1:
|
|
@@ -2015,21 +2263,21 @@ class OpenGLMobject:
|
|
|
2015
2263
|
mob.set_color(color)
|
|
2016
2264
|
return self
|
|
2017
2265
|
|
|
2018
|
-
def fade(self, darkness=0.5, recurse=True):
|
|
2019
|
-
self.set_opacity(1.0 - darkness, recurse=recurse)
|
|
2266
|
+
def fade(self, darkness: float = 0.5, recurse: bool = True) -> Self:
|
|
2267
|
+
return self.set_opacity(1.0 - darkness, recurse=recurse)
|
|
2020
2268
|
|
|
2021
|
-
def get_gloss(self):
|
|
2269
|
+
def get_gloss(self) -> float:
|
|
2022
2270
|
return self.gloss
|
|
2023
2271
|
|
|
2024
|
-
def set_gloss(self, gloss, recurse=True):
|
|
2272
|
+
def set_gloss(self, gloss: float, recurse: bool = True) -> Self:
|
|
2025
2273
|
for mob in self.get_family(recurse):
|
|
2026
2274
|
mob.gloss = gloss
|
|
2027
2275
|
return self
|
|
2028
2276
|
|
|
2029
|
-
def get_shadow(self):
|
|
2277
|
+
def get_shadow(self) -> float:
|
|
2030
2278
|
return self.shadow
|
|
2031
2279
|
|
|
2032
|
-
def set_shadow(self, shadow, recurse=True):
|
|
2280
|
+
def set_shadow(self, shadow: float, recurse: bool = True) -> Self:
|
|
2033
2281
|
for mob in self.get_family(recurse):
|
|
2034
2282
|
mob.shadow = shadow
|
|
2035
2283
|
return self
|
|
@@ -2037,8 +2285,11 @@ class OpenGLMobject:
|
|
|
2037
2285
|
# Background rectangle
|
|
2038
2286
|
|
|
2039
2287
|
def add_background_rectangle(
|
|
2040
|
-
self,
|
|
2041
|
-
|
|
2288
|
+
self,
|
|
2289
|
+
color: ParsableManimColor | None = None,
|
|
2290
|
+
opacity: float = 0.75,
|
|
2291
|
+
**kwargs: Any,
|
|
2292
|
+
) -> Self:
|
|
2042
2293
|
# TODO, this does not behave well when the mobject has points,
|
|
2043
2294
|
# since it gets displayed on top
|
|
2044
2295
|
"""Add a BackgroundRectangle as submobject.
|
|
@@ -2070,146 +2321,153 @@ class OpenGLMobject:
|
|
|
2070
2321
|
"""
|
|
2071
2322
|
from manim.mobject.geometry.shape_matchers import BackgroundRectangle
|
|
2072
2323
|
|
|
2073
|
-
self.background_rectangle = BackgroundRectangle(
|
|
2074
|
-
self,
|
|
2324
|
+
self.background_rectangle: BackgroundRectangle = BackgroundRectangle(
|
|
2325
|
+
self, # type: ignore[arg-type]
|
|
2326
|
+
color=color,
|
|
2327
|
+
fill_opacity=opacity,
|
|
2328
|
+
**kwargs,
|
|
2075
2329
|
)
|
|
2076
|
-
self.add_to_back(self.background_rectangle)
|
|
2330
|
+
self.add_to_back(self.background_rectangle) # type: ignore[arg-type]
|
|
2077
2331
|
return self
|
|
2078
2332
|
|
|
2079
|
-
def add_background_rectangle_to_submobjects(self, **kwargs):
|
|
2333
|
+
def add_background_rectangle_to_submobjects(self, **kwargs: Any) -> Self:
|
|
2080
2334
|
for submobject in self.submobjects:
|
|
2081
2335
|
submobject.add_background_rectangle(**kwargs)
|
|
2082
2336
|
return self
|
|
2083
2337
|
|
|
2084
|
-
def add_background_rectangle_to_family_members_with_points(
|
|
2338
|
+
def add_background_rectangle_to_family_members_with_points(
|
|
2339
|
+
self, **kwargs: Any
|
|
2340
|
+
) -> Self:
|
|
2085
2341
|
for mob in self.family_members_with_points():
|
|
2086
2342
|
mob.add_background_rectangle(**kwargs)
|
|
2087
2343
|
return self
|
|
2088
2344
|
|
|
2089
2345
|
# Getters
|
|
2090
2346
|
|
|
2091
|
-
def get_bounding_box_point(self, direction):
|
|
2347
|
+
def get_bounding_box_point(self, direction: Vector3DLike) -> Point3D:
|
|
2092
2348
|
bb = self.get_bounding_box()
|
|
2093
2349
|
indices = (np.sign(direction) + 1).astype(int)
|
|
2094
2350
|
return np.array([bb[indices[i]][i] for i in range(3)])
|
|
2095
2351
|
|
|
2096
|
-
def get_edge_center(self, direction) ->
|
|
2352
|
+
def get_edge_center(self, direction: Vector3DLike) -> Point3D:
|
|
2097
2353
|
"""Get edge coordinates for certain direction."""
|
|
2098
2354
|
return self.get_bounding_box_point(direction)
|
|
2099
2355
|
|
|
2100
|
-
def get_corner(self, direction) ->
|
|
2356
|
+
def get_corner(self, direction: Vector3DLike) -> Point3D:
|
|
2101
2357
|
"""Get corner coordinates for certain direction."""
|
|
2102
2358
|
return self.get_bounding_box_point(direction)
|
|
2103
2359
|
|
|
2104
|
-
def get_center(self) ->
|
|
2360
|
+
def get_center(self) -> Point3D:
|
|
2105
2361
|
"""Get center coordinates."""
|
|
2106
2362
|
return self.get_bounding_box()[1]
|
|
2107
2363
|
|
|
2108
|
-
def get_center_of_mass(self):
|
|
2364
|
+
def get_center_of_mass(self) -> Point3D:
|
|
2109
2365
|
return self.get_all_points().mean(0)
|
|
2110
2366
|
|
|
2111
|
-
def get_boundary_point(self, direction):
|
|
2367
|
+
def get_boundary_point(self, direction: Vector3DLike) -> Point3D:
|
|
2112
2368
|
all_points = self.get_all_points()
|
|
2113
2369
|
boundary_directions = all_points - self.get_center()
|
|
2114
2370
|
norms = np.linalg.norm(boundary_directions, axis=1)
|
|
2115
2371
|
boundary_directions /= np.repeat(norms, 3).reshape((len(norms), 3))
|
|
2116
|
-
index = np.argmax(np.dot(boundary_directions,
|
|
2372
|
+
index = np.argmax(np.dot(boundary_directions, direction))
|
|
2117
2373
|
return all_points[index]
|
|
2118
2374
|
|
|
2119
|
-
def get_continuous_bounding_box_point(self, direction):
|
|
2120
|
-
|
|
2375
|
+
def get_continuous_bounding_box_point(self, direction: Vector3DLike) -> Point3D:
|
|
2376
|
+
_dl, center, ur = self.get_bounding_box()
|
|
2121
2377
|
corner_vect = ur - center
|
|
2122
|
-
|
|
2378
|
+
np_direction = np.asarray(direction)
|
|
2379
|
+
return center + np_direction / np.max(
|
|
2123
2380
|
np.abs(
|
|
2124
2381
|
np.true_divide(
|
|
2125
|
-
|
|
2382
|
+
np_direction,
|
|
2126
2383
|
corner_vect,
|
|
2127
|
-
out=np.zeros(len(
|
|
2384
|
+
out=np.zeros(len(np_direction)),
|
|
2128
2385
|
where=((corner_vect) != 0),
|
|
2129
2386
|
),
|
|
2130
2387
|
),
|
|
2131
2388
|
)
|
|
2132
2389
|
|
|
2133
|
-
def get_top(self) ->
|
|
2390
|
+
def get_top(self) -> Point3D:
|
|
2134
2391
|
"""Get top coordinates of a box bounding the :class:`~.OpenGLMobject`"""
|
|
2135
2392
|
return self.get_edge_center(UP)
|
|
2136
2393
|
|
|
2137
|
-
def get_bottom(self) ->
|
|
2394
|
+
def get_bottom(self) -> Point3D:
|
|
2138
2395
|
"""Get bottom coordinates of a box bounding the :class:`~.OpenGLMobject`"""
|
|
2139
2396
|
return self.get_edge_center(DOWN)
|
|
2140
2397
|
|
|
2141
|
-
def get_right(self) ->
|
|
2398
|
+
def get_right(self) -> Point3D:
|
|
2142
2399
|
"""Get right coordinates of a box bounding the :class:`~.OpenGLMobject`"""
|
|
2143
2400
|
return self.get_edge_center(RIGHT)
|
|
2144
2401
|
|
|
2145
|
-
def get_left(self) ->
|
|
2402
|
+
def get_left(self) -> Point3D:
|
|
2146
2403
|
"""Get left coordinates of a box bounding the :class:`~.OpenGLMobject`"""
|
|
2147
2404
|
return self.get_edge_center(LEFT)
|
|
2148
2405
|
|
|
2149
|
-
def get_zenith(self) ->
|
|
2406
|
+
def get_zenith(self) -> Point3D:
|
|
2150
2407
|
"""Get zenith coordinates of a box bounding a 3D :class:`~.OpenGLMobject`."""
|
|
2151
2408
|
return self.get_edge_center(OUT)
|
|
2152
2409
|
|
|
2153
|
-
def get_nadir(self) ->
|
|
2410
|
+
def get_nadir(self) -> Point3D:
|
|
2154
2411
|
"""Get nadir (opposite the zenith) coordinates of a box bounding a 3D :class:`~.OpenGLMobject`."""
|
|
2155
2412
|
return self.get_edge_center(IN)
|
|
2156
2413
|
|
|
2157
|
-
def length_over_dim(self, dim):
|
|
2414
|
+
def length_over_dim(self, dim: int) -> float:
|
|
2158
2415
|
bb = self.get_bounding_box()
|
|
2159
|
-
|
|
2416
|
+
rv: float = abs((bb[2] - bb[0])[dim])
|
|
2417
|
+
return rv
|
|
2160
2418
|
|
|
2161
|
-
def get_width(self):
|
|
2419
|
+
def get_width(self) -> float:
|
|
2162
2420
|
"""Returns the width of the mobject."""
|
|
2163
2421
|
return self.length_over_dim(0)
|
|
2164
2422
|
|
|
2165
|
-
def get_height(self):
|
|
2423
|
+
def get_height(self) -> float:
|
|
2166
2424
|
"""Returns the height of the mobject."""
|
|
2167
2425
|
return self.length_over_dim(1)
|
|
2168
2426
|
|
|
2169
|
-
def get_depth(self):
|
|
2427
|
+
def get_depth(self) -> float:
|
|
2170
2428
|
"""Returns the depth of the mobject."""
|
|
2171
2429
|
return self.length_over_dim(2)
|
|
2172
2430
|
|
|
2173
|
-
def get_coord(self, dim: int, direction=ORIGIN):
|
|
2431
|
+
def get_coord(self, dim: int, direction: Vector3DLike = ORIGIN) -> ManimFloat:
|
|
2174
2432
|
"""Meant to generalize ``get_x``, ``get_y`` and ``get_z``"""
|
|
2175
2433
|
return self.get_bounding_box_point(direction)[dim]
|
|
2176
2434
|
|
|
2177
|
-
def get_x(self, direction=ORIGIN) ->
|
|
2435
|
+
def get_x(self, direction: Vector3DLike = ORIGIN) -> ManimFloat:
|
|
2178
2436
|
"""Returns x coordinate of the center of the :class:`~.OpenGLMobject` as ``float``"""
|
|
2179
2437
|
return self.get_coord(0, direction)
|
|
2180
2438
|
|
|
2181
|
-
def get_y(self, direction=ORIGIN) ->
|
|
2439
|
+
def get_y(self, direction: Vector3DLike = ORIGIN) -> ManimFloat:
|
|
2182
2440
|
"""Returns y coordinate of the center of the :class:`~.OpenGLMobject` as ``float``"""
|
|
2183
2441
|
return self.get_coord(1, direction)
|
|
2184
2442
|
|
|
2185
|
-
def get_z(self, direction=ORIGIN) ->
|
|
2443
|
+
def get_z(self, direction: Vector3DLike = ORIGIN) -> ManimFloat:
|
|
2186
2444
|
"""Returns z coordinate of the center of the :class:`~.OpenGLMobject` as ``float``"""
|
|
2187
2445
|
return self.get_coord(2, direction)
|
|
2188
2446
|
|
|
2189
|
-
def get_start(self):
|
|
2447
|
+
def get_start(self) -> Point3D:
|
|
2190
2448
|
"""Returns the point, where the stroke that surrounds the :class:`~.OpenGLMobject` starts."""
|
|
2191
2449
|
self.throw_error_if_no_points()
|
|
2192
2450
|
return np.array(self.points[0])
|
|
2193
2451
|
|
|
2194
|
-
def get_end(self):
|
|
2452
|
+
def get_end(self) -> Point3D:
|
|
2195
2453
|
"""Returns the point, where the stroke that surrounds the :class:`~.OpenGLMobject` ends."""
|
|
2196
2454
|
self.throw_error_if_no_points()
|
|
2197
2455
|
return np.array(self.points[-1])
|
|
2198
2456
|
|
|
2199
|
-
def get_start_and_end(self):
|
|
2457
|
+
def get_start_and_end(self) -> tuple[Point3D, Point3D]:
|
|
2200
2458
|
"""Returns starting and ending point of a stroke as a ``tuple``."""
|
|
2201
2459
|
return self.get_start(), self.get_end()
|
|
2202
2460
|
|
|
2203
|
-
def point_from_proportion(self, alpha):
|
|
2461
|
+
def point_from_proportion(self, alpha: float) -> Point3D:
|
|
2204
2462
|
points = self.points
|
|
2205
2463
|
i, subalpha = integer_interpolate(0, len(points) - 1, alpha)
|
|
2206
2464
|
return interpolate(points[i], points[i + 1], subalpha)
|
|
2207
2465
|
|
|
2208
|
-
def pfp(self, alpha):
|
|
2466
|
+
def pfp(self, alpha: float) -> Point3D:
|
|
2209
2467
|
"""Abbreviation for point_from_proportion"""
|
|
2210
2468
|
return self.point_from_proportion(alpha)
|
|
2211
2469
|
|
|
2212
|
-
def get_pieces(self, n_pieces):
|
|
2470
|
+
def get_pieces(self, n_pieces: int) -> OpenGLMobject:
|
|
2213
2471
|
template = self.copy()
|
|
2214
2472
|
template.submobjects = []
|
|
2215
2473
|
alphas = np.linspace(0, 1, n_pieces + 1)
|
|
@@ -2220,34 +2478,41 @@ class OpenGLMobject:
|
|
|
2220
2478
|
)
|
|
2221
2479
|
)
|
|
2222
2480
|
|
|
2223
|
-
def get_z_index_reference_point(self):
|
|
2481
|
+
def get_z_index_reference_point(self) -> Point3D:
|
|
2224
2482
|
# TODO, better place to define default z_index_group?
|
|
2225
2483
|
z_index_group = getattr(self, "z_index_group", self)
|
|
2226
2484
|
return z_index_group.get_center()
|
|
2227
2485
|
|
|
2228
2486
|
# Match other mobject properties
|
|
2229
2487
|
|
|
2230
|
-
def match_color(self, mobject: OpenGLMobject):
|
|
2488
|
+
def match_color(self, mobject: OpenGLMobject) -> Self:
|
|
2231
2489
|
"""Match the color with the color of another :class:`~.OpenGLMobject`."""
|
|
2232
2490
|
return self.set_color(mobject.get_color())
|
|
2233
2491
|
|
|
2234
|
-
def match_dim_size(
|
|
2492
|
+
def match_dim_size(
|
|
2493
|
+
self,
|
|
2494
|
+
mobject: OpenGLMobject,
|
|
2495
|
+
dim: int,
|
|
2496
|
+
**kwargs: Any,
|
|
2497
|
+
) -> Self:
|
|
2235
2498
|
"""Match the specified dimension with the dimension of another :class:`~.OpenGLMobject`."""
|
|
2236
2499
|
return self.rescale_to_fit(mobject.length_over_dim(dim), dim, **kwargs)
|
|
2237
2500
|
|
|
2238
|
-
def match_width(self, mobject: OpenGLMobject, **kwargs):
|
|
2501
|
+
def match_width(self, mobject: OpenGLMobject, **kwargs: Any) -> Self:
|
|
2239
2502
|
"""Match the width with the width of another :class:`~.OpenGLMobject`."""
|
|
2240
2503
|
return self.match_dim_size(mobject, 0, **kwargs)
|
|
2241
2504
|
|
|
2242
|
-
def match_height(self, mobject: OpenGLMobject, **kwargs):
|
|
2505
|
+
def match_height(self, mobject: OpenGLMobject, **kwargs: Any) -> Self:
|
|
2243
2506
|
"""Match the height with the height of another :class:`~.OpenGLMobject`."""
|
|
2244
2507
|
return self.match_dim_size(mobject, 1, **kwargs)
|
|
2245
2508
|
|
|
2246
|
-
def match_depth(self, mobject: OpenGLMobject, **kwargs):
|
|
2509
|
+
def match_depth(self, mobject: OpenGLMobject, **kwargs: Any) -> Self:
|
|
2247
2510
|
"""Match the depth with the depth of another :class:`~.OpenGLMobject`."""
|
|
2248
2511
|
return self.match_dim_size(mobject, 2, **kwargs)
|
|
2249
2512
|
|
|
2250
|
-
def match_coord(
|
|
2513
|
+
def match_coord(
|
|
2514
|
+
self, mobject: OpenGLMobject, dim: int, direction: Vector3DLike = ORIGIN
|
|
2515
|
+
) -> Self:
|
|
2251
2516
|
"""Match the coordinates with the coordinates of another :class:`~.OpenGLMobject`."""
|
|
2252
2517
|
return self.set_coord(
|
|
2253
2518
|
mobject.get_coord(dim, direction),
|
|
@@ -2255,23 +2520,23 @@ class OpenGLMobject:
|
|
|
2255
2520
|
direction=direction,
|
|
2256
2521
|
)
|
|
2257
2522
|
|
|
2258
|
-
def match_x(self, mobject, direction=ORIGIN):
|
|
2523
|
+
def match_x(self, mobject: OpenGLMobject, direction: Vector3DLike = ORIGIN) -> Self:
|
|
2259
2524
|
"""Match x coord. to the x coord. of another :class:`~.OpenGLMobject`."""
|
|
2260
2525
|
return self.match_coord(mobject, 0, direction)
|
|
2261
2526
|
|
|
2262
|
-
def match_y(self, mobject, direction=ORIGIN):
|
|
2527
|
+
def match_y(self, mobject: OpenGLMobject, direction: Vector3DLike = ORIGIN) -> Self:
|
|
2263
2528
|
"""Match y coord. to the x coord. of another :class:`~.OpenGLMobject`."""
|
|
2264
2529
|
return self.match_coord(mobject, 1, direction)
|
|
2265
2530
|
|
|
2266
|
-
def match_z(self, mobject, direction=ORIGIN):
|
|
2531
|
+
def match_z(self, mobject: OpenGLMobject, direction: Vector3DLike = ORIGIN) -> Self:
|
|
2267
2532
|
"""Match z coord. to the x coord. of another :class:`~.OpenGLMobject`."""
|
|
2268
2533
|
return self.match_coord(mobject, 2, direction)
|
|
2269
2534
|
|
|
2270
2535
|
def align_to(
|
|
2271
2536
|
self,
|
|
2272
|
-
mobject_or_point: OpenGLMobject |
|
|
2273
|
-
direction=ORIGIN,
|
|
2274
|
-
):
|
|
2537
|
+
mobject_or_point: OpenGLMobject | Point3DLike,
|
|
2538
|
+
direction: Vector3DLike = ORIGIN,
|
|
2539
|
+
) -> Self:
|
|
2275
2540
|
"""
|
|
2276
2541
|
Examples:
|
|
2277
2542
|
mob1.align_to(mob2, UP) moves mob1 vertically so that its
|
|
@@ -2281,6 +2546,7 @@ class OpenGLMobject:
|
|
|
2281
2546
|
horizontally so that it's center is directly above/below
|
|
2282
2547
|
the center of mob2
|
|
2283
2548
|
"""
|
|
2549
|
+
point: Point3DLike
|
|
2284
2550
|
if isinstance(mobject_or_point, OpenGLMobject):
|
|
2285
2551
|
point = mobject_or_point.get_bounding_box_point(direction)
|
|
2286
2552
|
else:
|
|
@@ -2291,21 +2557,22 @@ class OpenGLMobject:
|
|
|
2291
2557
|
self.set_coord(point[dim], dim, direction)
|
|
2292
2558
|
return self
|
|
2293
2559
|
|
|
2294
|
-
def get_group_class(self):
|
|
2560
|
+
def get_group_class(self) -> type[OpenGLGroup]:
|
|
2295
2561
|
return OpenGLGroup
|
|
2296
2562
|
|
|
2297
2563
|
@staticmethod
|
|
2298
|
-
def get_mobject_type_class():
|
|
2564
|
+
def get_mobject_type_class() -> type[OpenGLMobject]:
|
|
2299
2565
|
"""Return the base class of this mobject type."""
|
|
2300
2566
|
return OpenGLMobject
|
|
2301
2567
|
|
|
2302
2568
|
# Alignment
|
|
2303
2569
|
|
|
2304
|
-
def align_data_and_family(self, mobject):
|
|
2570
|
+
def align_data_and_family(self, mobject: OpenGLMobject) -> Self:
|
|
2305
2571
|
self.align_family(mobject)
|
|
2306
2572
|
self.align_data(mobject)
|
|
2573
|
+
return self
|
|
2307
2574
|
|
|
2308
|
-
def align_data(self, mobject):
|
|
2575
|
+
def align_data(self, mobject: OpenGLMobject) -> Self:
|
|
2309
2576
|
# In case any data arrays get resized when aligned to shader data
|
|
2310
2577
|
# self.refresh_shader_data()
|
|
2311
2578
|
for mob1, mob2 in zip(self.get_family(), mobject.get_family()):
|
|
@@ -2321,14 +2588,15 @@ class OpenGLMobject:
|
|
|
2321
2588
|
mob1.data[key] = resize_preserving_order(arr1, len(arr2))
|
|
2322
2589
|
elif len(arr1) > len(arr2):
|
|
2323
2590
|
mob2.data[key] = resize_preserving_order(arr2, len(arr1))
|
|
2591
|
+
return self
|
|
2324
2592
|
|
|
2325
|
-
def align_points(self, mobject):
|
|
2593
|
+
def align_points(self, mobject: OpenGLMobject) -> Self:
|
|
2326
2594
|
max_len = max(self.get_num_points(), mobject.get_num_points())
|
|
2327
2595
|
for mob in (self, mobject):
|
|
2328
2596
|
mob.resize_points(max_len, resize_func=resize_preserving_order)
|
|
2329
2597
|
return self
|
|
2330
2598
|
|
|
2331
|
-
def align_family(self, mobject):
|
|
2599
|
+
def align_family(self, mobject: OpenGLMobject) -> Self:
|
|
2332
2600
|
mob1 = self
|
|
2333
2601
|
mob2 = mobject
|
|
2334
2602
|
n1 = len(mob1)
|
|
@@ -2341,14 +2609,14 @@ class OpenGLMobject:
|
|
|
2341
2609
|
sm1.align_family(sm2)
|
|
2342
2610
|
return self
|
|
2343
2611
|
|
|
2344
|
-
def push_self_into_submobjects(self):
|
|
2612
|
+
def push_self_into_submobjects(self) -> Self:
|
|
2345
2613
|
copy = self.deepcopy()
|
|
2346
2614
|
copy.submobjects = []
|
|
2347
2615
|
self.resize_points(0)
|
|
2348
2616
|
self.add(copy)
|
|
2349
2617
|
return self
|
|
2350
2618
|
|
|
2351
|
-
def add_n_more_submobjects(self, n):
|
|
2619
|
+
def add_n_more_submobjects(self, n: int) -> Self:
|
|
2352
2620
|
if n == 0:
|
|
2353
2621
|
return self
|
|
2354
2622
|
|
|
@@ -2377,7 +2645,13 @@ class OpenGLMobject:
|
|
|
2377
2645
|
|
|
2378
2646
|
# Interpolate
|
|
2379
2647
|
|
|
2380
|
-
def interpolate(
|
|
2648
|
+
def interpolate(
|
|
2649
|
+
self,
|
|
2650
|
+
mobject1: OpenGLMobject,
|
|
2651
|
+
mobject2: OpenGLMobject,
|
|
2652
|
+
alpha: float,
|
|
2653
|
+
path_func: PathFuncType = straight_path(),
|
|
2654
|
+
) -> Self:
|
|
2381
2655
|
"""Turns this :class:`~.OpenGLMobject` into an interpolation between ``mobject1``
|
|
2382
2656
|
and ``mobject2``.
|
|
2383
2657
|
|
|
@@ -2406,10 +2680,7 @@ class OpenGLMobject:
|
|
|
2406
2680
|
if key not in mobject1.data or key not in mobject2.data:
|
|
2407
2681
|
continue
|
|
2408
2682
|
|
|
2409
|
-
if key in ("points", "bounding_box")
|
|
2410
|
-
func = path_func
|
|
2411
|
-
else:
|
|
2412
|
-
func = interpolate
|
|
2683
|
+
func = path_func if key in ("points", "bounding_box") else interpolate
|
|
2413
2684
|
|
|
2414
2685
|
self.data[key][:] = func(mobject1.data[key], mobject2.data[key], alpha)
|
|
2415
2686
|
|
|
@@ -2430,14 +2701,18 @@ class OpenGLMobject:
|
|
|
2430
2701
|
)
|
|
2431
2702
|
return self
|
|
2432
2703
|
|
|
2433
|
-
def pointwise_become_partial(
|
|
2704
|
+
def pointwise_become_partial(
|
|
2705
|
+
self, mobject: OpenGLMobject, a: float, b: float
|
|
2706
|
+
) -> Self:
|
|
2434
2707
|
"""
|
|
2435
2708
|
Set points in such a way as to become only
|
|
2436
2709
|
part of mobject.
|
|
2437
2710
|
Inputs 0 <= a < b <= 1 determine what portion
|
|
2438
2711
|
of mobject to become.
|
|
2712
|
+
|
|
2713
|
+
Returns `self` to allow method chaining.
|
|
2439
2714
|
"""
|
|
2440
|
-
|
|
2715
|
+
return self # To implement in subclass
|
|
2441
2716
|
|
|
2442
2717
|
def become(
|
|
2443
2718
|
self,
|
|
@@ -2447,7 +2722,7 @@ class OpenGLMobject:
|
|
|
2447
2722
|
match_depth: bool = False,
|
|
2448
2723
|
match_center: bool = False,
|
|
2449
2724
|
stretch: bool = False,
|
|
2450
|
-
):
|
|
2725
|
+
) -> Self:
|
|
2451
2726
|
"""Edit all data and submobjects to be identical
|
|
2452
2727
|
to another :class:`~.OpenGLMobject`
|
|
2453
2728
|
|
|
@@ -2482,7 +2757,6 @@ class OpenGLMobject:
|
|
|
2482
2757
|
circ.become(square)
|
|
2483
2758
|
self.wait(0.5)
|
|
2484
2759
|
"""
|
|
2485
|
-
|
|
2486
2760
|
if stretch:
|
|
2487
2761
|
mobject.stretch_to_fit_height(self.height)
|
|
2488
2762
|
mobject.stretch_to_fit_width(self.width)
|
|
@@ -2507,7 +2781,7 @@ class OpenGLMobject:
|
|
|
2507
2781
|
|
|
2508
2782
|
# Locking data
|
|
2509
2783
|
|
|
2510
|
-
def lock_data(self, keys):
|
|
2784
|
+
def lock_data(self, keys: Iterable[str]) -> None:
|
|
2511
2785
|
"""
|
|
2512
2786
|
To speed up some animations, particularly transformations,
|
|
2513
2787
|
it can be handy to acknowledge which pieces of data
|
|
@@ -2521,7 +2795,9 @@ class OpenGLMobject:
|
|
|
2521
2795
|
self.refresh_shader_data()
|
|
2522
2796
|
self.locked_data_keys = set(keys)
|
|
2523
2797
|
|
|
2524
|
-
def lock_matching_data(
|
|
2798
|
+
def lock_matching_data(
|
|
2799
|
+
self, mobject1: OpenGLMobject, mobject2: OpenGLMobject
|
|
2800
|
+
) -> Self:
|
|
2525
2801
|
for sm, sm1, sm2 in zip(
|
|
2526
2802
|
self.get_family(),
|
|
2527
2803
|
mobject1.get_family(),
|
|
@@ -2538,57 +2814,57 @@ class OpenGLMobject:
|
|
|
2538
2814
|
)
|
|
2539
2815
|
return self
|
|
2540
2816
|
|
|
2541
|
-
def unlock_data(self):
|
|
2817
|
+
def unlock_data(self) -> None:
|
|
2542
2818
|
for mob in self.get_family():
|
|
2543
2819
|
mob.locked_data_keys = set()
|
|
2544
2820
|
|
|
2545
2821
|
# Operations touching shader uniforms
|
|
2546
2822
|
|
|
2547
2823
|
@affects_shader_info_id
|
|
2548
|
-
def fix_in_frame(self):
|
|
2824
|
+
def fix_in_frame(self) -> Self:
|
|
2549
2825
|
self.is_fixed_in_frame = 1.0
|
|
2550
2826
|
return self
|
|
2551
2827
|
|
|
2552
2828
|
@affects_shader_info_id
|
|
2553
|
-
def fix_orientation(self):
|
|
2829
|
+
def fix_orientation(self) -> Self:
|
|
2554
2830
|
self.is_fixed_orientation = 1.0
|
|
2555
2831
|
self.fixed_orientation_center = tuple(self.get_center())
|
|
2556
2832
|
self.depth_test = True
|
|
2557
2833
|
return self
|
|
2558
2834
|
|
|
2559
2835
|
@affects_shader_info_id
|
|
2560
|
-
def unfix_from_frame(self):
|
|
2836
|
+
def unfix_from_frame(self) -> Self:
|
|
2561
2837
|
self.is_fixed_in_frame = 0.0
|
|
2562
2838
|
return self
|
|
2563
2839
|
|
|
2564
2840
|
@affects_shader_info_id
|
|
2565
|
-
def unfix_orientation(self):
|
|
2841
|
+
def unfix_orientation(self) -> Self:
|
|
2566
2842
|
self.is_fixed_orientation = 0.0
|
|
2567
2843
|
self.fixed_orientation_center = (0, 0, 0)
|
|
2568
2844
|
self.depth_test = False
|
|
2569
2845
|
return self
|
|
2570
2846
|
|
|
2571
2847
|
@affects_shader_info_id
|
|
2572
|
-
def apply_depth_test(self):
|
|
2848
|
+
def apply_depth_test(self) -> Self:
|
|
2573
2849
|
self.depth_test = True
|
|
2574
2850
|
return self
|
|
2575
2851
|
|
|
2576
2852
|
@affects_shader_info_id
|
|
2577
|
-
def deactivate_depth_test(self):
|
|
2853
|
+
def deactivate_depth_test(self) -> Self:
|
|
2578
2854
|
self.depth_test = False
|
|
2579
2855
|
return self
|
|
2580
2856
|
|
|
2581
2857
|
# Shader code manipulation
|
|
2582
2858
|
|
|
2583
|
-
def replace_shader_code(self,
|
|
2859
|
+
def replace_shader_code(self, old_code: str, new_code: str) -> Self:
|
|
2584
2860
|
# TODO, will this work with VMobject structure, given
|
|
2585
2861
|
# that it does not simpler return shader_wrappers of
|
|
2586
2862
|
# family?
|
|
2587
2863
|
for wrapper in self.get_shader_wrapper_list():
|
|
2588
|
-
wrapper.replace_code(
|
|
2864
|
+
wrapper.replace_code(old_code, new_code)
|
|
2589
2865
|
return self
|
|
2590
2866
|
|
|
2591
|
-
def set_color_by_code(self, glsl_code):
|
|
2867
|
+
def set_color_by_code(self, glsl_code: str) -> Self:
|
|
2592
2868
|
"""
|
|
2593
2869
|
Takes a snippet of code and inserts it into a
|
|
2594
2870
|
context which has the following variables:
|
|
@@ -2600,11 +2876,11 @@ class OpenGLMobject:
|
|
|
2600
2876
|
|
|
2601
2877
|
def set_color_by_xyz_func(
|
|
2602
2878
|
self,
|
|
2603
|
-
glsl_snippet,
|
|
2604
|
-
min_value
|
|
2605
|
-
max_value=5.0,
|
|
2606
|
-
colormap="viridis",
|
|
2607
|
-
):
|
|
2879
|
+
glsl_snippet: str,
|
|
2880
|
+
min_value: float = -5.0,
|
|
2881
|
+
max_value: float = 5.0,
|
|
2882
|
+
colormap: str = "viridis",
|
|
2883
|
+
) -> Self:
|
|
2608
2884
|
"""
|
|
2609
2885
|
Pass in a glsl expression in terms of x, y and z which returns
|
|
2610
2886
|
a float.
|
|
@@ -2613,27 +2889,27 @@ class OpenGLMobject:
|
|
|
2613
2889
|
# of the shader code
|
|
2614
2890
|
for char in "xyz":
|
|
2615
2891
|
glsl_snippet = glsl_snippet.replace(char, "point." + char)
|
|
2616
|
-
|
|
2892
|
+
# TODO: get_colormap_list does not exist
|
|
2893
|
+
# See https://github.com/ManimCommunity/manim/issues/4176
|
|
2894
|
+
rgb_list = get_colormap_list(colormap) # type: ignore[name-defined]
|
|
2617
2895
|
self.set_color_by_code(
|
|
2618
|
-
"color.rgb = float_to_color({}, {}, {}, {});"
|
|
2619
|
-
glsl_snippet,
|
|
2620
|
-
float(min_value),
|
|
2621
|
-
float(max_value),
|
|
2622
|
-
get_colormap_code(rgb_list),
|
|
2623
|
-
),
|
|
2896
|
+
f"color.rgb = float_to_color({glsl_snippet}, {float(min_value)}, {float(max_value)}, {get_colormap_code(rgb_list)});",
|
|
2624
2897
|
)
|
|
2625
2898
|
return self
|
|
2626
2899
|
|
|
2627
2900
|
# For shader data
|
|
2628
2901
|
|
|
2629
|
-
def refresh_shader_wrapper_id(self):
|
|
2630
|
-
self.
|
|
2902
|
+
def refresh_shader_wrapper_id(self) -> Self:
|
|
2903
|
+
self.get_shader_wrapper().refresh_id()
|
|
2631
2904
|
return self
|
|
2632
2905
|
|
|
2633
|
-
def get_shader_wrapper(self):
|
|
2906
|
+
def get_shader_wrapper(self) -> "ShaderWrapper": # noqa: UP037
|
|
2634
2907
|
from manim.renderer.shader_wrapper import ShaderWrapper
|
|
2635
2908
|
|
|
2636
|
-
|
|
2909
|
+
# if hasattr(self, "shader_wrapper"):
|
|
2910
|
+
# return self.shader_wrapper
|
|
2911
|
+
|
|
2912
|
+
self.shader_wrapper: ShaderWrapper = ShaderWrapper(
|
|
2637
2913
|
vert_data=self.get_shader_data(),
|
|
2638
2914
|
vert_indices=self.get_shader_vert_indices(),
|
|
2639
2915
|
uniforms=self.get_shader_uniforms(),
|
|
@@ -2644,14 +2920,14 @@ class OpenGLMobject:
|
|
|
2644
2920
|
)
|
|
2645
2921
|
return self.shader_wrapper
|
|
2646
2922
|
|
|
2647
|
-
def get_shader_wrapper_list(self):
|
|
2923
|
+
def get_shader_wrapper_list(self) -> Sequence["ShaderWrapper"]: # noqa: UP037
|
|
2648
2924
|
shader_wrappers = it.chain(
|
|
2649
2925
|
[self.get_shader_wrapper()],
|
|
2650
2926
|
*(sm.get_shader_wrapper_list() for sm in self.submobjects),
|
|
2651
2927
|
)
|
|
2652
2928
|
batches = batch_by_property(shader_wrappers, lambda sw: sw.get_id())
|
|
2653
2929
|
|
|
2654
|
-
result = []
|
|
2930
|
+
result: list["ShaderWrapper"] = [] # noqa: UP037
|
|
2655
2931
|
for wrapper_group, _ in batches:
|
|
2656
2932
|
shader_wrapper = wrapper_group[0]
|
|
2657
2933
|
if not shader_wrapper.is_valid():
|
|
@@ -2661,7 +2937,7 @@ class OpenGLMobject:
|
|
|
2661
2937
|
result.append(shader_wrapper)
|
|
2662
2938
|
return result
|
|
2663
2939
|
|
|
2664
|
-
def check_data_alignment(self, array, data_key):
|
|
2940
|
+
def check_data_alignment(self, array: _ShaderData, data_key: str) -> Self:
|
|
2665
2941
|
# Makes sure that self.data[key] can be broadcast into
|
|
2666
2942
|
# the given array, meaning its length has to be either 1
|
|
2667
2943
|
# or the length of the array
|
|
@@ -2673,45 +2949,50 @@ class OpenGLMobject:
|
|
|
2673
2949
|
)
|
|
2674
2950
|
return self
|
|
2675
2951
|
|
|
2676
|
-
def get_resized_shader_data_array(self, length):
|
|
2952
|
+
def get_resized_shader_data_array(self, length: float) -> _ShaderData:
|
|
2677
2953
|
# If possible, try to populate an existing array, rather
|
|
2678
2954
|
# than recreating it each frame
|
|
2679
2955
|
points = self.points
|
|
2680
|
-
shader_data = np.zeros(len(points), dtype=self.shader_dtype)
|
|
2956
|
+
shader_data = cast(_ShaderData, np.zeros(len(points), dtype=self.shader_dtype))
|
|
2681
2957
|
return shader_data
|
|
2682
2958
|
|
|
2683
|
-
def read_data_to_shader(
|
|
2959
|
+
def read_data_to_shader(
|
|
2960
|
+
self,
|
|
2961
|
+
shader_data: _ShaderData, # has structured data type, ex. ("point", np.float32, (3,))
|
|
2962
|
+
shader_data_key: str,
|
|
2963
|
+
data_key: str,
|
|
2964
|
+
) -> None:
|
|
2684
2965
|
if data_key in self.locked_data_keys:
|
|
2685
2966
|
return
|
|
2686
2967
|
self.check_data_alignment(shader_data, data_key)
|
|
2687
2968
|
shader_data[shader_data_key] = self.data[data_key]
|
|
2688
2969
|
|
|
2689
|
-
def get_shader_data(self):
|
|
2970
|
+
def get_shader_data(self) -> _ShaderData:
|
|
2690
2971
|
shader_data = self.get_resized_shader_data_array(self.get_num_points())
|
|
2691
2972
|
self.read_data_to_shader(shader_data, "point", "points")
|
|
2692
2973
|
return shader_data
|
|
2693
2974
|
|
|
2694
|
-
def refresh_shader_data(self):
|
|
2975
|
+
def refresh_shader_data(self) -> None:
|
|
2695
2976
|
self.get_shader_data()
|
|
2696
2977
|
|
|
2697
|
-
def get_shader_uniforms(self):
|
|
2978
|
+
def get_shader_uniforms(self) -> dict[str, Any]:
|
|
2698
2979
|
return self.uniforms
|
|
2699
2980
|
|
|
2700
|
-
def get_shader_vert_indices(self):
|
|
2981
|
+
def get_shader_vert_indices(self) -> Sequence[int] | None:
|
|
2701
2982
|
return self.shader_indices
|
|
2702
2983
|
|
|
2703
2984
|
@property
|
|
2704
|
-
def submobjects(self):
|
|
2985
|
+
def submobjects(self) -> list[OpenGLMobject]:
|
|
2705
2986
|
return self._submobjects if hasattr(self, "_submobjects") else []
|
|
2706
2987
|
|
|
2707
2988
|
@submobjects.setter
|
|
2708
|
-
def submobjects(self, submobject_list):
|
|
2989
|
+
def submobjects(self, submobject_list: Iterable[OpenGLMobject]) -> None:
|
|
2709
2990
|
self.remove(*self.submobjects)
|
|
2710
2991
|
self.add(*submobject_list)
|
|
2711
2992
|
|
|
2712
2993
|
# Errors
|
|
2713
2994
|
|
|
2714
|
-
def throw_error_if_no_points(self):
|
|
2995
|
+
def throw_error_if_no_points(self) -> None:
|
|
2715
2996
|
if not self.has_points():
|
|
2716
2997
|
message = (
|
|
2717
2998
|
"Cannot call OpenGLMobject.{} " + "for a OpenGLMobject with no points"
|
|
@@ -2721,52 +3002,57 @@ class OpenGLMobject:
|
|
|
2721
3002
|
|
|
2722
3003
|
|
|
2723
3004
|
class OpenGLGroup(OpenGLMobject):
|
|
2724
|
-
def __init__(self, *mobjects, **kwargs):
|
|
2725
|
-
if not all([isinstance(m, OpenGLMobject) for m in mobjects]):
|
|
2726
|
-
raise Exception("All submobjects must be of type OpenGLMobject")
|
|
3005
|
+
def __init__(self, *mobjects: OpenGLMobject, **kwargs: Any) -> None:
|
|
2727
3006
|
super().__init__(**kwargs)
|
|
2728
3007
|
self.add(*mobjects)
|
|
2729
3008
|
|
|
2730
3009
|
|
|
2731
3010
|
class OpenGLPoint(OpenGLMobject):
|
|
2732
3011
|
def __init__(
|
|
2733
|
-
self,
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
3012
|
+
self,
|
|
3013
|
+
location: Point3DLike = ORIGIN,
|
|
3014
|
+
artificial_width: float = 1e-6,
|
|
3015
|
+
artificial_height: float = 1e-6,
|
|
3016
|
+
**kwargs: Any,
|
|
3017
|
+
) -> None:
|
|
3018
|
+
self.artificial_width: float = artificial_width
|
|
3019
|
+
self.artificial_height: float = artificial_height
|
|
2737
3020
|
super().__init__(**kwargs)
|
|
2738
3021
|
self.set_location(location)
|
|
2739
3022
|
|
|
2740
|
-
|
|
3023
|
+
@override
|
|
3024
|
+
def get_width(self) -> float:
|
|
2741
3025
|
return self.artificial_width
|
|
2742
3026
|
|
|
2743
|
-
|
|
3027
|
+
@override
|
|
3028
|
+
def get_height(self) -> float:
|
|
2744
3029
|
return self.artificial_height
|
|
2745
3030
|
|
|
2746
|
-
def get_location(self):
|
|
2747
|
-
return self.points[0].copy()
|
|
3031
|
+
def get_location(self) -> Point3D:
|
|
3032
|
+
return cast(Point3D, self.points[0]).copy()
|
|
2748
3033
|
|
|
2749
|
-
|
|
3034
|
+
@override
|
|
3035
|
+
def get_bounding_box_point(self, *args: object, **kwargs: Any) -> Point3D:
|
|
2750
3036
|
return self.get_location()
|
|
2751
3037
|
|
|
2752
|
-
def set_location(self, new_loc):
|
|
3038
|
+
def set_location(self, new_loc: Point3DLike) -> None:
|
|
2753
3039
|
self.set_points(np.array(new_loc, ndmin=2, dtype=float))
|
|
2754
3040
|
|
|
2755
3041
|
|
|
2756
3042
|
class _AnimationBuilder:
|
|
2757
|
-
def __init__(self, mobject):
|
|
2758
|
-
self.mobject = mobject
|
|
3043
|
+
def __init__(self, mobject: OpenGLMobject) -> None:
|
|
3044
|
+
self.mobject: OpenGLMobject = mobject
|
|
2759
3045
|
self.mobject.generate_target()
|
|
2760
3046
|
|
|
2761
|
-
self.overridden_animation = None
|
|
2762
|
-
self.is_chaining = False
|
|
2763
|
-
self.methods = []
|
|
3047
|
+
self.overridden_animation: Animation | None = None
|
|
3048
|
+
self.is_chaining: bool = False
|
|
3049
|
+
self.methods: list[MethodWithArgs] = []
|
|
2764
3050
|
|
|
2765
3051
|
# Whether animation args can be passed
|
|
2766
|
-
self.cannot_pass_args = False
|
|
2767
|
-
self.anim_args = {}
|
|
3052
|
+
self.cannot_pass_args: bool = False
|
|
3053
|
+
self.anim_args: dict[str, object] = {}
|
|
2768
3054
|
|
|
2769
|
-
def __call__(self, **kwargs):
|
|
3055
|
+
def __call__(self, **kwargs: Any) -> Self:
|
|
2770
3056
|
if self.cannot_pass_args:
|
|
2771
3057
|
raise ValueError(
|
|
2772
3058
|
"Animation arguments must be passed before accessing methods and can only be passed once",
|
|
@@ -2777,26 +3063,30 @@ class _AnimationBuilder:
|
|
|
2777
3063
|
|
|
2778
3064
|
return self
|
|
2779
3065
|
|
|
2780
|
-
def __getattr__(self, method_name):
|
|
3066
|
+
def __getattr__(self, method_name: str) -> Callable[..., Self]:
|
|
2781
3067
|
method = getattr(self.mobject.target, method_name)
|
|
2782
3068
|
has_overridden_animation = hasattr(method, "_override_animate")
|
|
2783
3069
|
|
|
2784
3070
|
if (self.is_chaining and has_overridden_animation) or self.overridden_animation:
|
|
2785
3071
|
raise NotImplementedError(
|
|
2786
|
-
"Method chaining is currently not supported for "
|
|
2787
|
-
"overridden animations",
|
|
3072
|
+
"Method chaining is currently not supported for overridden animations",
|
|
2788
3073
|
)
|
|
2789
3074
|
|
|
2790
|
-
|
|
3075
|
+
# NOTE: using `Self` here should not be a problem, because it's equivalent to a `TypeVar` introduced in `__getattr__`.
|
|
3076
|
+
# For this reason, here it's still in scope and can be used (that's why pyright does not flag this as an error).
|
|
3077
|
+
# However, mypy currently does not seem to understand this: hence the `type: ignore` comment.
|
|
3078
|
+
def update_target(*method_args: object, **method_kwargs: object) -> Self: # type: ignore[type-var, misc]
|
|
2791
3079
|
if has_overridden_animation:
|
|
2792
|
-
self.overridden_animation =
|
|
3080
|
+
self.overridden_animation = cast(
|
|
3081
|
+
"Callable[..., Animation]", method._override_animate
|
|
3082
|
+
)(
|
|
2793
3083
|
self.mobject,
|
|
2794
3084
|
*method_args,
|
|
2795
3085
|
anim_args=self.anim_args,
|
|
2796
3086
|
**method_kwargs,
|
|
2797
3087
|
)
|
|
2798
3088
|
else:
|
|
2799
|
-
self.methods.append(
|
|
3089
|
+
self.methods.append(MethodWithArgs(method, method_args, method_kwargs))
|
|
2800
3090
|
method(*method_args, **method_kwargs)
|
|
2801
3091
|
return self
|
|
2802
3092
|
|
|
@@ -2805,13 +3095,12 @@ class _AnimationBuilder:
|
|
|
2805
3095
|
|
|
2806
3096
|
return update_target
|
|
2807
3097
|
|
|
2808
|
-
def build(self):
|
|
3098
|
+
def build(self) -> "Animation": # noqa: UP037
|
|
2809
3099
|
from manim.animation.transform import _MethodAnimation
|
|
2810
3100
|
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
anim = _MethodAnimation(self.mobject, self.methods)
|
|
3101
|
+
# NOTE: To fix this mypy error, we'll need to update `_MethodAnimation` to accept `Mobject | OpenGLMobject` instead of `Mobject`.
|
|
3102
|
+
# Once that is done, the `type: ignore` comment below won't be necessary anymore and mypy will emit a corresponding warning.
|
|
3103
|
+
anim = self.overridden_animation or _MethodAnimation(self.mobject, self.methods) # type: ignore[arg-type]
|
|
2815
3104
|
|
|
2816
3105
|
for attr, value in self.anim_args.items():
|
|
2817
3106
|
setattr(anim, attr, value)
|
|
@@ -2819,7 +3108,15 @@ class _AnimationBuilder:
|
|
|
2819
3108
|
return anim
|
|
2820
3109
|
|
|
2821
3110
|
|
|
2822
|
-
|
|
3111
|
+
_Decorated = TypeVar("_Decorated", bound=Callable[..., "Animation"])
|
|
3112
|
+
|
|
3113
|
+
|
|
3114
|
+
class _OverrideAnimateDecorator(Protocol):
|
|
3115
|
+
# The slash divider on the next line prevents a mypy error in line 3176.
|
|
3116
|
+
def __call__(self, decorated: _Decorated, /) -> _Decorated: ...
|
|
3117
|
+
|
|
3118
|
+
|
|
3119
|
+
def override_animate(method: types.FunctionType) -> _OverrideAnimateDecorator:
|
|
2823
3120
|
r"""Decorator for overriding method animations.
|
|
2824
3121
|
|
|
2825
3122
|
This allows to specify a method (returning an :class:`~.Animation`)
|
|
@@ -2871,8 +3168,8 @@ def override_animate(method):
|
|
|
2871
3168
|
|
|
2872
3169
|
"""
|
|
2873
3170
|
|
|
2874
|
-
def decorator(animation_method):
|
|
2875
|
-
method._override_animate = animation_method
|
|
3171
|
+
def decorator(animation_method: _Decorated) -> _Decorated:
|
|
3172
|
+
method._override_animate = animation_method # type: ignore[attr-defined]
|
|
2876
3173
|
return animation_method
|
|
2877
3174
|
|
|
2878
3175
|
return decorator
|