manim 0.17.3__py3-none-any.whl → 0.18.0.post0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of manim might be problematic. Click here for more details.
- manim/__init__.py +1 -0
- manim/__main__.py +2 -0
- manim/_config/__init__.py +0 -1
- manim/_config/logger_utils.py +1 -0
- manim/_config/utils.py +14 -5
- manim/animation/changing.py +9 -5
- manim/animation/creation.py +8 -3
- manim/animation/indication.py +4 -4
- manim/animation/speedmodifier.py +2 -4
- manim/animation/updaters/mobject_update_utils.py +134 -16
- manim/camera/camera.py +31 -17
- manim/cli/checkhealth/__init__.py +0 -0
- manim/cli/checkhealth/checks.py +173 -0
- manim/cli/checkhealth/commands.py +81 -0
- manim/cli/render/global_options.py +6 -0
- manim/constants.py +58 -54
- manim/mobject/geometry/__init__.py +1 -0
- manim/mobject/geometry/arc.py +126 -91
- manim/mobject/geometry/boolean_ops.py +6 -10
- manim/mobject/geometry/labeled.py +155 -0
- manim/mobject/geometry/line.py +66 -50
- manim/mobject/geometry/polygram.py +23 -15
- manim/mobject/geometry/shape_matchers.py +24 -15
- manim/mobject/geometry/tips.py +62 -40
- manim/mobject/graph.py +3 -4
- manim/mobject/graphing/coordinate_systems.py +190 -139
- manim/mobject/graphing/number_line.py +5 -2
- manim/mobject/graphing/probability.py +4 -3
- manim/mobject/graphing/scale.py +7 -7
- manim/mobject/logo.py +108 -22
- manim/mobject/matrix.py +33 -37
- manim/mobject/mobject.py +327 -260
- manim/mobject/opengl/opengl_image_mobject.py +1 -1
- manim/mobject/opengl/opengl_mobject.py +18 -12
- manim/mobject/opengl/opengl_point_cloud_mobject.py +1 -1
- manim/mobject/opengl/opengl_surface.py +1 -1
- manim/mobject/opengl/opengl_vectorized_mobject.py +21 -17
- manim/mobject/svg/brace.py +3 -1
- manim/mobject/svg/svg_mobject.py +9 -11
- manim/mobject/table.py +50 -54
- manim/mobject/text/numbers.py +48 -6
- manim/mobject/text/tex_mobject.py +8 -12
- manim/mobject/text/text_mobject.py +32 -24
- manim/mobject/three_d/three_d_utils.py +13 -8
- manim/mobject/three_d/three_dimensions.py +61 -43
- manim/mobject/types/image_mobject.py +5 -4
- manim/mobject/types/point_cloud_mobject.py +8 -6
- manim/mobject/types/vectorized_mobject.py +385 -258
- manim/mobject/vector_field.py +19 -11
- manim/plugins/import_plugins.py +1 -1
- manim/plugins/plugins_flags.py +1 -6
- manim/renderer/shader.py +2 -2
- manim/scene/scene.py +15 -7
- manim/scene/scene_file_writer.py +1 -2
- manim/scene/three_d_scene.py +1 -1
- manim/scene/vector_space_scene.py +17 -7
- manim/typing.py +133 -0
- manim/utils/bezier.py +267 -83
- manim/utils/color/AS2700.py +234 -0
- manim/utils/color/BS381.py +315 -0
- manim/utils/color/X11.py +530 -0
- manim/utils/color/XKCD.py +949 -0
- manim/utils/color/__init__.py +58 -0
- manim/utils/color/core.py +1036 -0
- manim/utils/color/manim_colors.py +220 -0
- manim/utils/docbuild/autocolor_directive.py +92 -0
- manim/utils/docbuild/manim_directive.py +40 -6
- manim/utils/file_ops.py +1 -1
- manim/utils/hashing.py +1 -1
- manim/utils/iterables.py +1 -1
- manim/utils/rate_functions.py +33 -0
- manim/utils/simple_functions.py +0 -18
- manim/utils/space_ops.py +55 -42
- manim/utils/testing/frames_comparison.py +9 -0
- manim/utils/tex.py +2 -0
- manim/utils/tex_file_writing.py +29 -2
- {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/METADATA +14 -14
- {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/RECORD +82 -71
- {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/WHEEL +1 -1
- manim/communitycolors.py +0 -9
- manim/utils/color.py +0 -552
- {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/LICENSE +0 -0
- {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/LICENSE.community +0 -0
- {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/entry_points.txt +0 -0
|
@@ -14,12 +14,21 @@ __all__ = [
|
|
|
14
14
|
|
|
15
15
|
import itertools as it
|
|
16
16
|
import sys
|
|
17
|
-
import
|
|
18
|
-
|
|
17
|
+
from typing import (
|
|
18
|
+
TYPE_CHECKING,
|
|
19
|
+
Callable,
|
|
20
|
+
Generator,
|
|
21
|
+
Hashable,
|
|
22
|
+
Iterable,
|
|
23
|
+
Literal,
|
|
24
|
+
Mapping,
|
|
25
|
+
Sequence,
|
|
26
|
+
)
|
|
19
27
|
|
|
20
|
-
import colour
|
|
21
28
|
import numpy as np
|
|
29
|
+
import numpy.typing as npt
|
|
22
30
|
from PIL.Image import Image
|
|
31
|
+
from typing_extensions import Self
|
|
23
32
|
|
|
24
33
|
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
|
25
34
|
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
|
|
@@ -38,11 +47,25 @@ from ...utils.bezier import (
|
|
|
38
47
|
partial_bezier_points,
|
|
39
48
|
proportions_along_bezier_curve_for_point,
|
|
40
49
|
)
|
|
41
|
-
from ...utils.color import BLACK, WHITE,
|
|
42
|
-
from ...utils.deprecation import deprecated
|
|
50
|
+
from ...utils.color import BLACK, WHITE, ManimColor, ParsableManimColor
|
|
43
51
|
from ...utils.iterables import make_even, resize_array, stretch_array_to_length, tuplify
|
|
44
52
|
from ...utils.space_ops import rotate_vector, shoelace_direction
|
|
45
53
|
|
|
54
|
+
if TYPE_CHECKING:
|
|
55
|
+
from manim.typing import (
|
|
56
|
+
BezierPoints,
|
|
57
|
+
CubicBezierPoints,
|
|
58
|
+
ManimFloat,
|
|
59
|
+
MappingFunction,
|
|
60
|
+
Point2D,
|
|
61
|
+
Point3D,
|
|
62
|
+
Point3D_Array,
|
|
63
|
+
QuadraticBezierPoints,
|
|
64
|
+
RGBA_Array_Float,
|
|
65
|
+
Vector3,
|
|
66
|
+
Zeros,
|
|
67
|
+
)
|
|
68
|
+
|
|
46
69
|
# TODO
|
|
47
70
|
# - Change cubic curve groups to have 4 points instead of 3
|
|
48
71
|
# - Change sub_path idea accordingly
|
|
@@ -81,68 +104,78 @@ class VMobject(Mobject):
|
|
|
81
104
|
|
|
82
105
|
def __init__(
|
|
83
106
|
self,
|
|
84
|
-
fill_color=None,
|
|
85
|
-
fill_opacity=0.0,
|
|
86
|
-
stroke_color=None,
|
|
87
|
-
stroke_opacity=1.0,
|
|
88
|
-
stroke_width=DEFAULT_STROKE_WIDTH,
|
|
89
|
-
background_stroke_color=BLACK,
|
|
90
|
-
background_stroke_opacity=1.0,
|
|
91
|
-
background_stroke_width=0,
|
|
92
|
-
sheen_factor=0.0,
|
|
107
|
+
fill_color: ParsableManimColor | None = None,
|
|
108
|
+
fill_opacity: float = 0.0,
|
|
109
|
+
stroke_color: ParsableManimColor | None = None,
|
|
110
|
+
stroke_opacity: float = 1.0,
|
|
111
|
+
stroke_width: float = DEFAULT_STROKE_WIDTH,
|
|
112
|
+
background_stroke_color: ParsableManimColor | None = BLACK,
|
|
113
|
+
background_stroke_opacity: float = 1.0,
|
|
114
|
+
background_stroke_width: float = 0,
|
|
115
|
+
sheen_factor: float = 0.0,
|
|
93
116
|
joint_type: LineJointType | None = None,
|
|
94
|
-
sheen_direction=UL,
|
|
95
|
-
close_new_points=False,
|
|
96
|
-
pre_function_handle_to_anchor_scale_factor=0.01,
|
|
97
|
-
make_smooth_after_applying_functions=False,
|
|
98
|
-
background_image=None,
|
|
99
|
-
shade_in_3d=False,
|
|
117
|
+
sheen_direction: Vector3 = UL,
|
|
118
|
+
close_new_points: bool = False,
|
|
119
|
+
pre_function_handle_to_anchor_scale_factor: float = 0.01,
|
|
120
|
+
make_smooth_after_applying_functions: bool = False,
|
|
121
|
+
background_image: Image | str | None = None,
|
|
122
|
+
shade_in_3d: bool = False,
|
|
100
123
|
# TODO, do we care about accounting for varying zoom levels?
|
|
101
|
-
tolerance_for_point_equality=1e-6,
|
|
102
|
-
n_points_per_cubic_curve=4,
|
|
124
|
+
tolerance_for_point_equality: float = 1e-6,
|
|
125
|
+
n_points_per_cubic_curve: int = 4,
|
|
103
126
|
**kwargs,
|
|
104
127
|
):
|
|
105
128
|
self.fill_opacity = fill_opacity
|
|
106
129
|
self.stroke_opacity = stroke_opacity
|
|
107
130
|
self.stroke_width = stroke_width
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
self.
|
|
115
|
-
self.
|
|
116
|
-
|
|
117
|
-
|
|
131
|
+
if background_stroke_color is not None:
|
|
132
|
+
self.background_stroke_color: ManimColor = ManimColor(
|
|
133
|
+
background_stroke_color
|
|
134
|
+
)
|
|
135
|
+
self.background_stroke_opacity: float = background_stroke_opacity
|
|
136
|
+
self.background_stroke_width: float = background_stroke_width
|
|
137
|
+
self.sheen_factor: float = sheen_factor
|
|
138
|
+
self.joint_type: LineJointType = (
|
|
139
|
+
LineJointType.AUTO if joint_type is None else joint_type
|
|
140
|
+
)
|
|
141
|
+
self.sheen_direction: Vector3 = sheen_direction
|
|
142
|
+
self.close_new_points: bool = close_new_points
|
|
143
|
+
self.pre_function_handle_to_anchor_scale_factor: float = (
|
|
118
144
|
pre_function_handle_to_anchor_scale_factor
|
|
119
145
|
)
|
|
120
|
-
self.make_smooth_after_applying_functions =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
self.
|
|
124
|
-
self.
|
|
146
|
+
self.make_smooth_after_applying_functions: bool = (
|
|
147
|
+
make_smooth_after_applying_functions
|
|
148
|
+
)
|
|
149
|
+
self.background_image: Image | str | None = background_image
|
|
150
|
+
self.shade_in_3d: bool = shade_in_3d
|
|
151
|
+
self.tolerance_for_point_equality: float = tolerance_for_point_equality
|
|
152
|
+
self.n_points_per_cubic_curve: int = n_points_per_cubic_curve
|
|
125
153
|
super().__init__(**kwargs)
|
|
154
|
+
self.submobjects: list[VMobject]
|
|
126
155
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
156
|
+
# TODO: Find where color overwrites are happening and remove the color doubling
|
|
157
|
+
# if "color" in kwargs:
|
|
158
|
+
# fill_color = kwargs["color"]
|
|
159
|
+
# stroke_color = kwargs["color"]
|
|
160
|
+
if fill_color is not None:
|
|
161
|
+
self.fill_color = ManimColor.parse(fill_color)
|
|
162
|
+
if stroke_color is not None:
|
|
163
|
+
self.stroke_color = ManimColor.parse(stroke_color)
|
|
131
164
|
|
|
132
165
|
# OpenGL compatibility
|
|
133
166
|
@property
|
|
134
|
-
def n_points_per_curve(self):
|
|
167
|
+
def n_points_per_curve(self) -> int:
|
|
135
168
|
return self.n_points_per_cubic_curve
|
|
136
169
|
|
|
137
|
-
def get_group_class(self):
|
|
170
|
+
def get_group_class(self) -> type[VGroup]:
|
|
138
171
|
return VGroup
|
|
139
172
|
|
|
140
173
|
@staticmethod
|
|
141
|
-
def get_mobject_type_class():
|
|
174
|
+
def get_mobject_type_class() -> type[VMobject]:
|
|
142
175
|
return VMobject
|
|
143
176
|
|
|
144
177
|
# Colors
|
|
145
|
-
def init_colors(self, propagate_colors=True):
|
|
178
|
+
def init_colors(self, propagate_colors: bool = True) -> Self:
|
|
146
179
|
self.set_fill(
|
|
147
180
|
color=self.fill_color,
|
|
148
181
|
opacity=self.fill_opacity,
|
|
@@ -172,7 +205,9 @@ class VMobject(Mobject):
|
|
|
172
205
|
|
|
173
206
|
return self
|
|
174
207
|
|
|
175
|
-
def generate_rgbas_array(
|
|
208
|
+
def generate_rgbas_array(
|
|
209
|
+
self, color: ManimColor | list[ManimColor], opacity: float | Iterable[float]
|
|
210
|
+
) -> RGBA_Array_Float:
|
|
176
211
|
"""
|
|
177
212
|
First arg can be either a color, or a tuple/list of colors.
|
|
178
213
|
Likewise, opacity can either be a float, or a tuple of floats.
|
|
@@ -180,10 +215,14 @@ class VMobject(Mobject):
|
|
|
180
215
|
one color was passed in, a second slightly light color
|
|
181
216
|
will automatically be added for the gradient
|
|
182
217
|
"""
|
|
183
|
-
colors = [
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
218
|
+
colors: list[ManimColor] = [
|
|
219
|
+
ManimColor(c) if (c is not None) else BLACK for c in tuplify(color)
|
|
220
|
+
]
|
|
221
|
+
opacities: list[float] = [
|
|
222
|
+
o if (o is not None) else 0.0 for o in tuplify(opacity)
|
|
223
|
+
]
|
|
224
|
+
rgbas: npt.NDArray[RGBA_Array_Float] = np.array(
|
|
225
|
+
[c.to_rgba_with_alpha(o) for c, o in zip(*make_even(colors, opacities))],
|
|
187
226
|
)
|
|
188
227
|
|
|
189
228
|
sheen_factor = self.get_sheen_factor()
|
|
@@ -194,7 +233,12 @@ class VMobject(Mobject):
|
|
|
194
233
|
rgbas = np.append(rgbas, light_rgbas, axis=0)
|
|
195
234
|
return rgbas
|
|
196
235
|
|
|
197
|
-
def update_rgbas_array(
|
|
236
|
+
def update_rgbas_array(
|
|
237
|
+
self,
|
|
238
|
+
array_name: str,
|
|
239
|
+
color: ManimColor | None = None,
|
|
240
|
+
opacity: float | None = None,
|
|
241
|
+
) -> Self:
|
|
198
242
|
rgbas = self.generate_rgbas_array(color, opacity)
|
|
199
243
|
if not hasattr(self, array_name):
|
|
200
244
|
setattr(self, array_name, rgbas)
|
|
@@ -217,10 +261,10 @@ class VMobject(Mobject):
|
|
|
217
261
|
|
|
218
262
|
def set_fill(
|
|
219
263
|
self,
|
|
220
|
-
color:
|
|
264
|
+
color: ParsableManimColor | None = None,
|
|
221
265
|
opacity: float | None = None,
|
|
222
266
|
family: bool = True,
|
|
223
|
-
):
|
|
267
|
+
) -> Self:
|
|
224
268
|
"""Set the fill color and fill opacity of a :class:`VMobject`.
|
|
225
269
|
|
|
226
270
|
Parameters
|
|
@@ -260,18 +304,19 @@ class VMobject(Mobject):
|
|
|
260
304
|
for submobject in self.submobjects:
|
|
261
305
|
submobject.set_fill(color, opacity, family)
|
|
262
306
|
self.update_rgbas_array("fill_rgbas", color, opacity)
|
|
307
|
+
self.fill_rgbas: RGBA_Array_Float
|
|
263
308
|
if opacity is not None:
|
|
264
309
|
self.fill_opacity = opacity
|
|
265
310
|
return self
|
|
266
311
|
|
|
267
312
|
def set_stroke(
|
|
268
313
|
self,
|
|
269
|
-
color=None,
|
|
270
|
-
width=None,
|
|
271
|
-
opacity=None,
|
|
314
|
+
color: ParsableManimColor = None,
|
|
315
|
+
width: float | None = None,
|
|
316
|
+
opacity: float | None = None,
|
|
272
317
|
background=False,
|
|
273
|
-
family=True,
|
|
274
|
-
):
|
|
318
|
+
family: bool = True,
|
|
319
|
+
) -> Self:
|
|
275
320
|
if family:
|
|
276
321
|
for submobject in self.submobjects:
|
|
277
322
|
submobject.set_stroke(color, width, opacity, background, family)
|
|
@@ -289,29 +334,32 @@ class VMobject(Mobject):
|
|
|
289
334
|
if opacity is not None:
|
|
290
335
|
setattr(self, opacity_name, opacity)
|
|
291
336
|
if color is not None and background:
|
|
292
|
-
|
|
337
|
+
if isinstance(color, (list, tuple)):
|
|
338
|
+
self.background_stroke_color = color
|
|
339
|
+
else:
|
|
340
|
+
self.background_stroke_color = ManimColor(color)
|
|
293
341
|
return self
|
|
294
342
|
|
|
295
|
-
def set_background_stroke(self, **kwargs):
|
|
343
|
+
def set_background_stroke(self, **kwargs) -> Self:
|
|
296
344
|
kwargs["background"] = True
|
|
297
345
|
self.set_stroke(**kwargs)
|
|
298
346
|
return self
|
|
299
347
|
|
|
300
348
|
def set_style(
|
|
301
349
|
self,
|
|
302
|
-
fill_color=None,
|
|
303
|
-
fill_opacity=None,
|
|
304
|
-
stroke_color=None,
|
|
305
|
-
stroke_width=None,
|
|
306
|
-
stroke_opacity=None,
|
|
307
|
-
background_stroke_color=None,
|
|
308
|
-
background_stroke_width=None,
|
|
309
|
-
background_stroke_opacity=None,
|
|
310
|
-
sheen_factor=None,
|
|
311
|
-
sheen_direction=None,
|
|
312
|
-
background_image=None,
|
|
313
|
-
family=True,
|
|
314
|
-
):
|
|
350
|
+
fill_color: ParsableManimColor | None = None,
|
|
351
|
+
fill_opacity: float | None = None,
|
|
352
|
+
stroke_color: ParsableManimColor | None = None,
|
|
353
|
+
stroke_width: float | None = None,
|
|
354
|
+
stroke_opacity: float | None = None,
|
|
355
|
+
background_stroke_color: ParsableManimColor | None = None,
|
|
356
|
+
background_stroke_width: float | None = None,
|
|
357
|
+
background_stroke_opacity: float | None = None,
|
|
358
|
+
sheen_factor: float | None = None,
|
|
359
|
+
sheen_direction: Vector3 | None = None,
|
|
360
|
+
background_image: Image | str | None = None,
|
|
361
|
+
family: bool = True,
|
|
362
|
+
) -> Self:
|
|
315
363
|
self.set_fill(color=fill_color, opacity=fill_opacity, family=family)
|
|
316
364
|
self.set_stroke(
|
|
317
365
|
color=stroke_color,
|
|
@@ -335,16 +383,17 @@ class VMobject(Mobject):
|
|
|
335
383
|
self.color_using_background_image(background_image)
|
|
336
384
|
return self
|
|
337
385
|
|
|
338
|
-
def get_style(self, simple=False):
|
|
386
|
+
def get_style(self, simple: bool = False) -> dict:
|
|
339
387
|
ret = {
|
|
340
388
|
"stroke_opacity": self.get_stroke_opacity(),
|
|
341
389
|
"stroke_width": self.get_stroke_width(),
|
|
342
390
|
}
|
|
343
391
|
|
|
392
|
+
# TODO: FIX COLORS HERE
|
|
344
393
|
if simple:
|
|
345
|
-
ret["fill_color"] =
|
|
394
|
+
ret["fill_color"] = self.get_fill_color()
|
|
346
395
|
ret["fill_opacity"] = self.get_fill_opacity()
|
|
347
|
-
ret["stroke_color"] =
|
|
396
|
+
ret["stroke_color"] = self.get_stroke_color()
|
|
348
397
|
else:
|
|
349
398
|
ret["fill_color"] = self.get_fill_colors()
|
|
350
399
|
ret["fill_opacity"] = self.get_fill_opacities()
|
|
@@ -358,7 +407,7 @@ class VMobject(Mobject):
|
|
|
358
407
|
|
|
359
408
|
return ret
|
|
360
409
|
|
|
361
|
-
def match_style(self, vmobject, family=True):
|
|
410
|
+
def match_style(self, vmobject: VMobject, family: bool = True) -> Self:
|
|
362
411
|
self.set_style(**vmobject.get_style(), family=False)
|
|
363
412
|
|
|
364
413
|
if family:
|
|
@@ -373,18 +422,18 @@ class VMobject(Mobject):
|
|
|
373
422
|
sm1.match_style(sm2)
|
|
374
423
|
return self
|
|
375
424
|
|
|
376
|
-
def set_color(self, color, family=True):
|
|
425
|
+
def set_color(self, color: ParsableManimColor, family: bool = True) -> Self:
|
|
377
426
|
self.set_fill(color, family=family)
|
|
378
427
|
self.set_stroke(color, family=family)
|
|
379
428
|
return self
|
|
380
429
|
|
|
381
|
-
def set_opacity(self, opacity, family=True):
|
|
430
|
+
def set_opacity(self, opacity: float, family: bool = True) -> Self:
|
|
382
431
|
self.set_fill(opacity=opacity, family=family)
|
|
383
432
|
self.set_stroke(opacity=opacity, family=family)
|
|
384
433
|
self.set_stroke(opacity=opacity, family=family, background=True)
|
|
385
434
|
return self
|
|
386
435
|
|
|
387
|
-
def fade(self, darkness=0.5, family=True):
|
|
436
|
+
def fade(self, darkness: float = 0.5, family: bool = True) -> Self:
|
|
388
437
|
factor = 1.0 - darkness
|
|
389
438
|
self.set_fill(opacity=factor * self.get_fill_opacity(), family=False)
|
|
390
439
|
self.set_stroke(opacity=factor * self.get_stroke_opacity(), family=False)
|
|
@@ -395,13 +444,13 @@ class VMobject(Mobject):
|
|
|
395
444
|
super().fade(darkness, family)
|
|
396
445
|
return self
|
|
397
446
|
|
|
398
|
-
def get_fill_rgbas(self):
|
|
447
|
+
def get_fill_rgbas(self) -> RGBA_Array_Float | Zeros:
|
|
399
448
|
try:
|
|
400
449
|
return self.fill_rgbas
|
|
401
450
|
except AttributeError:
|
|
402
451
|
return np.zeros((1, 4))
|
|
403
452
|
|
|
404
|
-
def get_fill_color(self):
|
|
453
|
+
def get_fill_color(self) -> ManimColor:
|
|
405
454
|
"""
|
|
406
455
|
If there are multiple colors (for gradient)
|
|
407
456
|
this returns the first one
|
|
@@ -410,66 +459,71 @@ class VMobject(Mobject):
|
|
|
410
459
|
|
|
411
460
|
fill_color = property(get_fill_color, set_fill)
|
|
412
461
|
|
|
413
|
-
def get_fill_opacity(self):
|
|
462
|
+
def get_fill_opacity(self) -> ManimFloat:
|
|
414
463
|
"""
|
|
415
464
|
If there are multiple opacities, this returns the
|
|
416
465
|
first
|
|
417
466
|
"""
|
|
418
467
|
return self.get_fill_opacities()[0]
|
|
419
468
|
|
|
420
|
-
|
|
469
|
+
# TODO: Does this just do a copy?
|
|
470
|
+
# TODO: I have the feeling that this function should not return None, does that have any usage ?
|
|
471
|
+
def get_fill_colors(self) -> list[ManimColor | None]:
|
|
421
472
|
return [
|
|
422
|
-
|
|
473
|
+
ManimColor(rgba[:3]) if rgba.any() else None
|
|
423
474
|
for rgba in self.get_fill_rgbas()
|
|
424
475
|
]
|
|
425
476
|
|
|
426
|
-
def get_fill_opacities(self):
|
|
477
|
+
def get_fill_opacities(self) -> npt.NDArray[ManimFloat]:
|
|
427
478
|
return self.get_fill_rgbas()[:, 3]
|
|
428
479
|
|
|
429
|
-
def get_stroke_rgbas(self, background=False):
|
|
480
|
+
def get_stroke_rgbas(self, background: bool = False) -> RGBA_Array_float | Zeros:
|
|
430
481
|
try:
|
|
431
482
|
if background:
|
|
483
|
+
self.background_stroke_rgbas: RGBA_Array_Float
|
|
432
484
|
rgbas = self.background_stroke_rgbas
|
|
433
485
|
else:
|
|
486
|
+
self.stroke_rgbas: RGBA_Array_Float
|
|
434
487
|
rgbas = self.stroke_rgbas
|
|
435
488
|
return rgbas
|
|
436
489
|
except AttributeError:
|
|
437
490
|
return np.zeros((1, 4))
|
|
438
491
|
|
|
439
|
-
def get_stroke_color(self, background=False):
|
|
492
|
+
def get_stroke_color(self, background: bool = False) -> ManimColor | None:
|
|
440
493
|
return self.get_stroke_colors(background)[0]
|
|
441
494
|
|
|
442
495
|
stroke_color = property(get_stroke_color, set_stroke)
|
|
443
496
|
|
|
444
|
-
def get_stroke_width(self, background=False):
|
|
497
|
+
def get_stroke_width(self, background: bool = False) -> float:
|
|
445
498
|
if background:
|
|
499
|
+
self.background_stroke_width: float
|
|
446
500
|
width = self.background_stroke_width
|
|
447
501
|
else:
|
|
448
502
|
width = self.stroke_width
|
|
449
503
|
if isinstance(width, str):
|
|
450
504
|
width = int(width)
|
|
451
|
-
return max(0, width)
|
|
505
|
+
return max(0.0, width)
|
|
452
506
|
|
|
453
|
-
def get_stroke_opacity(self, background=False):
|
|
507
|
+
def get_stroke_opacity(self, background: bool = False) -> ManimFloat:
|
|
454
508
|
return self.get_stroke_opacities(background)[0]
|
|
455
509
|
|
|
456
|
-
def get_stroke_colors(self, background=False):
|
|
510
|
+
def get_stroke_colors(self, background: bool = False) -> list[ManimColor | None]:
|
|
457
511
|
return [
|
|
458
|
-
|
|
512
|
+
ManimColor(rgba[:3]) if rgba.any() else None
|
|
459
513
|
for rgba in self.get_stroke_rgbas(background)
|
|
460
514
|
]
|
|
461
515
|
|
|
462
|
-
def get_stroke_opacities(self, background=False):
|
|
516
|
+
def get_stroke_opacities(self, background: bool = False) -> npt.NDArray[ManimFloat]:
|
|
463
517
|
return self.get_stroke_rgbas(background)[:, 3]
|
|
464
518
|
|
|
465
|
-
def get_color(self):
|
|
519
|
+
def get_color(self) -> ManimColor:
|
|
466
520
|
if np.all(self.get_fill_opacities() == 0):
|
|
467
521
|
return self.get_stroke_color()
|
|
468
522
|
return self.get_fill_color()
|
|
469
523
|
|
|
470
524
|
color = property(get_color, set_color)
|
|
471
525
|
|
|
472
|
-
def set_sheen_direction(self, direction:
|
|
526
|
+
def set_sheen_direction(self, direction: Vector3, family: bool = True) -> Self:
|
|
473
527
|
"""Sets the direction of the applied sheen.
|
|
474
528
|
|
|
475
529
|
Parameters
|
|
@@ -494,10 +548,12 @@ class VMobject(Mobject):
|
|
|
494
548
|
for submob in self.get_family():
|
|
495
549
|
submob.sheen_direction = direction
|
|
496
550
|
else:
|
|
497
|
-
self.sheen_direction = direction
|
|
551
|
+
self.sheen_direction: Vector3 = direction
|
|
498
552
|
return self
|
|
499
553
|
|
|
500
|
-
def rotate_sheen_direction(
|
|
554
|
+
def rotate_sheen_direction(
|
|
555
|
+
self, angle: float, axis: Vector3 = OUT, family: bool = True
|
|
556
|
+
) -> Self:
|
|
501
557
|
"""Rotates the direction of the applied sheen.
|
|
502
558
|
|
|
503
559
|
Parameters
|
|
@@ -528,7 +584,9 @@ class VMobject(Mobject):
|
|
|
528
584
|
self.sheen_direction = rotate_vector(self.sheen_direction, angle, axis)
|
|
529
585
|
return self
|
|
530
586
|
|
|
531
|
-
def set_sheen(
|
|
587
|
+
def set_sheen(
|
|
588
|
+
self, factor: float, direction: Vector3 | None = None, family: bool = True
|
|
589
|
+
) -> Self:
|
|
532
590
|
"""Applies a color gradient from a direction.
|
|
533
591
|
|
|
534
592
|
Parameters
|
|
@@ -554,7 +612,7 @@ class VMobject(Mobject):
|
|
|
554
612
|
if family:
|
|
555
613
|
for submob in self.submobjects:
|
|
556
614
|
submob.set_sheen(factor, direction, family)
|
|
557
|
-
self.sheen_factor = factor
|
|
615
|
+
self.sheen_factor: float = factor
|
|
558
616
|
if direction is not None:
|
|
559
617
|
# family set to false because recursion will
|
|
560
618
|
# already be handled above
|
|
@@ -565,13 +623,13 @@ class VMobject(Mobject):
|
|
|
565
623
|
self.set_fill(self.get_fill_color(), family=family)
|
|
566
624
|
return self
|
|
567
625
|
|
|
568
|
-
def get_sheen_direction(self):
|
|
626
|
+
def get_sheen_direction(self) -> Vector3:
|
|
569
627
|
return np.array(self.sheen_direction)
|
|
570
628
|
|
|
571
|
-
def get_sheen_factor(self):
|
|
629
|
+
def get_sheen_factor(self) -> float:
|
|
572
630
|
return self.sheen_factor
|
|
573
631
|
|
|
574
|
-
def get_gradient_start_and_end_points(self):
|
|
632
|
+
def get_gradient_start_and_end_points(self) -> tuple[Point3D, Point3D]:
|
|
575
633
|
if self.shade_in_3d:
|
|
576
634
|
return get_3d_vmob_gradient_start_and_end_points(self)
|
|
577
635
|
else:
|
|
@@ -583,8 +641,8 @@ class VMobject(Mobject):
|
|
|
583
641
|
offset = np.dot(bases, direction)
|
|
584
642
|
return (c - offset, c + offset)
|
|
585
643
|
|
|
586
|
-
def color_using_background_image(self, background_image: Image | str):
|
|
587
|
-
self.background_image = background_image
|
|
644
|
+
def color_using_background_image(self, background_image: Image | str) -> Self:
|
|
645
|
+
self.background_image: Image | str = background_image
|
|
588
646
|
self.set_color(WHITE)
|
|
589
647
|
for submob in self.submobjects:
|
|
590
648
|
submob.color_using_background_image(background_image)
|
|
@@ -593,26 +651,28 @@ class VMobject(Mobject):
|
|
|
593
651
|
def get_background_image(self) -> Image | str:
|
|
594
652
|
return self.background_image
|
|
595
653
|
|
|
596
|
-
def match_background_image(self, vmobject):
|
|
654
|
+
def match_background_image(self, vmobject: VMobject) -> Self:
|
|
597
655
|
self.color_using_background_image(vmobject.get_background_image())
|
|
598
656
|
return self
|
|
599
657
|
|
|
600
|
-
def set_shade_in_3d(
|
|
658
|
+
def set_shade_in_3d(
|
|
659
|
+
self, value: bool = True, z_index_as_group: bool = False
|
|
660
|
+
) -> Self:
|
|
601
661
|
for submob in self.get_family():
|
|
602
662
|
submob.shade_in_3d = value
|
|
603
663
|
if z_index_as_group:
|
|
604
664
|
submob.z_index_group = self
|
|
605
665
|
return self
|
|
606
666
|
|
|
607
|
-
def set_points(self, points):
|
|
608
|
-
self.points = np.array(points)
|
|
667
|
+
def set_points(self, points: Point3D_Array) -> Self:
|
|
668
|
+
self.points: Point3D_Array = np.array(points)
|
|
609
669
|
return self
|
|
610
670
|
|
|
611
671
|
def resize_points(
|
|
612
672
|
self,
|
|
613
673
|
new_length: int,
|
|
614
|
-
resize_func: Callable[[
|
|
615
|
-
):
|
|
674
|
+
resize_func: Callable[[Point3D, int], Point3D] = resize_array,
|
|
675
|
+
) -> Self:
|
|
616
676
|
"""Resize the array of anchor points and handles to have
|
|
617
677
|
the specified size.
|
|
618
678
|
|
|
@@ -631,11 +691,11 @@ class VMobject(Mobject):
|
|
|
631
691
|
|
|
632
692
|
def set_anchors_and_handles(
|
|
633
693
|
self,
|
|
634
|
-
anchors1:
|
|
635
|
-
handles1:
|
|
636
|
-
handles2:
|
|
637
|
-
anchors2:
|
|
638
|
-
):
|
|
694
|
+
anchors1: CubicBezierPoints,
|
|
695
|
+
handles1: CubicBezierPoints,
|
|
696
|
+
handles2: CubicBezierPoints,
|
|
697
|
+
anchors2: CubicBezierPoints,
|
|
698
|
+
) -> Self:
|
|
639
699
|
"""Given two sets of anchors and handles, process them to set them as anchors
|
|
640
700
|
and handles of the VMobject.
|
|
641
701
|
|
|
@@ -663,17 +723,17 @@ class VMobject(Mobject):
|
|
|
663
723
|
self.points[index::nppcc] = array
|
|
664
724
|
return self
|
|
665
725
|
|
|
666
|
-
def clear_points(self):
|
|
726
|
+
def clear_points(self) -> None:
|
|
667
727
|
self.points = np.zeros((0, self.dim))
|
|
668
728
|
|
|
669
|
-
def append_points(self, new_points):
|
|
729
|
+
def append_points(self, new_points: Point3D_Array) -> Self:
|
|
670
730
|
# TODO, check that number new points is a multiple of 4?
|
|
671
731
|
# or else that if len(self.points) % 4 == 1, then
|
|
672
732
|
# len(new_points) % 4 == 3?
|
|
673
733
|
self.points = np.append(self.points, new_points, axis=0)
|
|
674
734
|
return self
|
|
675
735
|
|
|
676
|
-
def start_new_path(self, point):
|
|
736
|
+
def start_new_path(self, point: Point3D) -> Self:
|
|
677
737
|
if len(self.points) % 4 != 0:
|
|
678
738
|
# close the open path by appending the last
|
|
679
739
|
# start anchor sufficiently often
|
|
@@ -685,23 +745,24 @@ class VMobject(Mobject):
|
|
|
685
745
|
|
|
686
746
|
def add_cubic_bezier_curve(
|
|
687
747
|
self,
|
|
688
|
-
anchor1:
|
|
689
|
-
handle1:
|
|
690
|
-
handle2:
|
|
691
|
-
anchor2,
|
|
748
|
+
anchor1: CubicBezierPoints,
|
|
749
|
+
handle1: CubicBezierPoints,
|
|
750
|
+
handle2: CubicBezierPoints,
|
|
751
|
+
anchor2: CubicBezierPoints,
|
|
692
752
|
) -> None:
|
|
693
753
|
# TODO, check the len(self.points) % 4 == 0?
|
|
694
754
|
self.append_points([anchor1, handle1, handle2, anchor2])
|
|
695
755
|
|
|
696
|
-
|
|
756
|
+
# what type is curves?
|
|
757
|
+
def add_cubic_bezier_curves(self, curves) -> None:
|
|
697
758
|
self.append_points(curves.flatten())
|
|
698
759
|
|
|
699
760
|
def add_cubic_bezier_curve_to(
|
|
700
761
|
self,
|
|
701
|
-
handle1:
|
|
702
|
-
handle2:
|
|
703
|
-
anchor:
|
|
704
|
-
):
|
|
762
|
+
handle1: CubicBezierPoints,
|
|
763
|
+
handle2: CubicBezierPoints,
|
|
764
|
+
anchor: CubicBezierPoints,
|
|
765
|
+
) -> Self:
|
|
705
766
|
"""Add cubic bezier curve to the path.
|
|
706
767
|
|
|
707
768
|
NOTE : the first anchor is not a parameter as by default the end of the last sub-path!
|
|
@@ -730,9 +791,9 @@ class VMobject(Mobject):
|
|
|
730
791
|
|
|
731
792
|
def add_quadratic_bezier_curve_to(
|
|
732
793
|
self,
|
|
733
|
-
handle:
|
|
734
|
-
anchor:
|
|
735
|
-
):
|
|
794
|
+
handle: QuadraticBezierPoints,
|
|
795
|
+
anchor: QuadraticBezierPoints,
|
|
796
|
+
) -> Self:
|
|
736
797
|
"""Add Quadratic bezier curve to the path.
|
|
737
798
|
|
|
738
799
|
Returns
|
|
@@ -754,7 +815,7 @@ class VMobject(Mobject):
|
|
|
754
815
|
)
|
|
755
816
|
return self
|
|
756
817
|
|
|
757
|
-
def add_line_to(self, point:
|
|
818
|
+
def add_line_to(self, point: Point3D) -> Self:
|
|
758
819
|
"""Add a straight line from the last point of VMobject to the given point.
|
|
759
820
|
|
|
760
821
|
Parameters
|
|
@@ -777,7 +838,7 @@ class VMobject(Mobject):
|
|
|
777
838
|
)
|
|
778
839
|
return self
|
|
779
840
|
|
|
780
|
-
def add_smooth_curve_to(self, *points:
|
|
841
|
+
def add_smooth_curve_to(self, *points: Point3D) -> Self:
|
|
781
842
|
"""Creates a smooth curve from given points and add it to the VMobject. If two points are passed in, the first is interpreted
|
|
782
843
|
as a handle, the second as an anchor.
|
|
783
844
|
|
|
@@ -820,28 +881,28 @@ class VMobject(Mobject):
|
|
|
820
881
|
self.append_points([last_a2, handle1, handle2, new_anchor])
|
|
821
882
|
return self
|
|
822
883
|
|
|
823
|
-
def has_new_path_started(self):
|
|
884
|
+
def has_new_path_started(self) -> bool:
|
|
824
885
|
nppcc = self.n_points_per_cubic_curve # 4
|
|
825
886
|
# A new path starting is defined by a control point which is not part of a bezier subcurve.
|
|
826
887
|
return len(self.points) % nppcc == 1
|
|
827
888
|
|
|
828
|
-
def get_last_point(self):
|
|
889
|
+
def get_last_point(self) -> Point3D:
|
|
829
890
|
return self.points[-1]
|
|
830
891
|
|
|
831
|
-
def is_closed(self):
|
|
892
|
+
def is_closed(self) -> bool:
|
|
832
893
|
# TODO use consider_points_equals_2d ?
|
|
833
894
|
return self.consider_points_equals(self.points[0], self.points[-1])
|
|
834
895
|
|
|
835
|
-
def close_path(self):
|
|
896
|
+
def close_path(self) -> None:
|
|
836
897
|
if not self.is_closed():
|
|
837
898
|
self.add_line_to(self.get_subpaths()[-1][0])
|
|
838
899
|
|
|
839
|
-
def add_points_as_corners(self, points:
|
|
900
|
+
def add_points_as_corners(self, points: Iterable[Point3D]) -> Iterable[Point3D]:
|
|
840
901
|
for point in points:
|
|
841
902
|
self.add_line_to(point)
|
|
842
903
|
return points
|
|
843
904
|
|
|
844
|
-
def set_points_as_corners(self, points:
|
|
905
|
+
def set_points_as_corners(self, points: Point3D_Array) -> Self:
|
|
845
906
|
"""Given an array of points, set them as corner of the vmobject.
|
|
846
907
|
|
|
847
908
|
To achieve that, this algorithm sets handles aligned with the anchors such that the resultant bezier curve will be the segment
|
|
@@ -866,12 +927,12 @@ class VMobject(Mobject):
|
|
|
866
927
|
)
|
|
867
928
|
return self
|
|
868
929
|
|
|
869
|
-
def set_points_smoothly(self, points):
|
|
930
|
+
def set_points_smoothly(self, points: Point3D_Array) -> Self:
|
|
870
931
|
self.set_points_as_corners(points)
|
|
871
932
|
self.make_smooth()
|
|
872
933
|
return self
|
|
873
934
|
|
|
874
|
-
def change_anchor_mode(self, mode:
|
|
935
|
+
def change_anchor_mode(self, mode: Literal["jagged", "smooth"]) -> Self:
|
|
875
936
|
"""Changes the anchor mode of the bezier curves. This will modify the handles.
|
|
876
937
|
|
|
877
938
|
There can be only two modes, "jagged", and "smooth".
|
|
@@ -881,7 +942,7 @@ class VMobject(Mobject):
|
|
|
881
942
|
:class:`VMobject`
|
|
882
943
|
``self``
|
|
883
944
|
"""
|
|
884
|
-
assert mode in ["jagged", "smooth"]
|
|
945
|
+
assert mode in ["jagged", "smooth"], 'mode must be either "jagged" or "smooth"'
|
|
885
946
|
nppcc = self.n_points_per_cubic_curve
|
|
886
947
|
for submob in self.family_members_with_points():
|
|
887
948
|
subpaths = submob.get_subpaths()
|
|
@@ -893,7 +954,7 @@ class VMobject(Mobject):
|
|
|
893
954
|
anchors = np.append(subpath[::nppcc], subpath[-1:], 0)
|
|
894
955
|
if mode == "smooth":
|
|
895
956
|
h1, h2 = get_smooth_handle_points(anchors)
|
|
896
|
-
|
|
957
|
+
else: # mode == "jagged"
|
|
897
958
|
# The following will make the handles aligned with the anchors, thus making the bezier curve a segment
|
|
898
959
|
a1 = anchors[:-1]
|
|
899
960
|
a2 = anchors[1:]
|
|
@@ -905,18 +966,18 @@ class VMobject(Mobject):
|
|
|
905
966
|
submob.append_points(new_subpath)
|
|
906
967
|
return self
|
|
907
968
|
|
|
908
|
-
def make_smooth(self):
|
|
969
|
+
def make_smooth(self) -> Self:
|
|
909
970
|
return self.change_anchor_mode("smooth")
|
|
910
971
|
|
|
911
|
-
def make_jagged(self):
|
|
972
|
+
def make_jagged(self) -> Self:
|
|
912
973
|
return self.change_anchor_mode("jagged")
|
|
913
974
|
|
|
914
|
-
def add_subpath(self, points:
|
|
975
|
+
def add_subpath(self, points: Point3D_Array) -> Self:
|
|
915
976
|
assert len(points) % 4 == 0
|
|
916
|
-
self.points = np.append(self.points, points, axis=0)
|
|
977
|
+
self.points: Point3D_Array = np.append(self.points, points, axis=0)
|
|
917
978
|
return self
|
|
918
979
|
|
|
919
|
-
def append_vectorized_mobject(self, vectorized_mobject):
|
|
980
|
+
def append_vectorized_mobject(self, vectorized_mobject: VMobject) -> None:
|
|
920
981
|
new_points = list(vectorized_mobject.points)
|
|
921
982
|
|
|
922
983
|
if self.has_new_path_started():
|
|
@@ -925,7 +986,7 @@ class VMobject(Mobject):
|
|
|
925
986
|
self.points = self.points[:-1]
|
|
926
987
|
self.append_points(new_points)
|
|
927
988
|
|
|
928
|
-
def apply_function(self, function):
|
|
989
|
+
def apply_function(self, function: MappingFunction) -> Self:
|
|
929
990
|
factor = self.pre_function_handle_to_anchor_scale_factor
|
|
930
991
|
self.scale_handle_to_anchor_distances(factor)
|
|
931
992
|
super().apply_function(function)
|
|
@@ -937,15 +998,15 @@ class VMobject(Mobject):
|
|
|
937
998
|
def rotate(
|
|
938
999
|
self,
|
|
939
1000
|
angle: float,
|
|
940
|
-
axis:
|
|
941
|
-
about_point:
|
|
1001
|
+
axis: Vector3 = OUT,
|
|
1002
|
+
about_point: Point3D | None = None,
|
|
942
1003
|
**kwargs,
|
|
943
|
-
):
|
|
1004
|
+
) -> Self:
|
|
944
1005
|
self.rotate_sheen_direction(angle, axis)
|
|
945
1006
|
super().rotate(angle, axis, about_point, **kwargs)
|
|
946
1007
|
return self
|
|
947
1008
|
|
|
948
|
-
def scale_handle_to_anchor_distances(self, factor: float):
|
|
1009
|
+
def scale_handle_to_anchor_distances(self, factor: float) -> Self:
|
|
949
1010
|
"""If the distance between a given handle point H and its associated
|
|
950
1011
|
anchor point A is d, then it changes H to be a distances factor*d
|
|
951
1012
|
away from A, but so that the line from A to H doesn't change.
|
|
@@ -977,10 +1038,10 @@ class VMobject(Mobject):
|
|
|
977
1038
|
return self
|
|
978
1039
|
|
|
979
1040
|
#
|
|
980
|
-
def consider_points_equals(self, p0, p1):
|
|
1041
|
+
def consider_points_equals(self, p0: Point3D, p1: Point3D) -> bool:
|
|
981
1042
|
return np.allclose(p0, p1, atol=self.tolerance_for_point_equality)
|
|
982
1043
|
|
|
983
|
-
def consider_points_equals_2d(self, p0:
|
|
1044
|
+
def consider_points_equals_2d(self, p0: Point2D, p1: Point2D) -> bool:
|
|
984
1045
|
"""Determine if two points are close enough to be considered equal.
|
|
985
1046
|
|
|
986
1047
|
This uses the algorithm from np.isclose(), but expanded here for the
|
|
@@ -1006,10 +1067,14 @@ class VMobject(Mobject):
|
|
|
1006
1067
|
return True
|
|
1007
1068
|
|
|
1008
1069
|
# Information about line
|
|
1009
|
-
def get_cubic_bezier_tuples_from_points(
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1070
|
+
def get_cubic_bezier_tuples_from_points(
|
|
1071
|
+
self, points: Point3D_Array
|
|
1072
|
+
) -> npt.NDArray[Point3D_Array]:
|
|
1073
|
+
return np.array(self.gen_cubic_bezier_tuples_from_points(points))
|
|
1074
|
+
|
|
1075
|
+
def gen_cubic_bezier_tuples_from_points(
|
|
1076
|
+
self, points: Point3D_Array
|
|
1077
|
+
) -> tuple[Point3D_Array]:
|
|
1013
1078
|
"""Returns the bezier tuples from an array of points.
|
|
1014
1079
|
|
|
1015
1080
|
self.points is a list of the anchors and handles of the bezier curves of the mobject (ie [anchor1, handle1, handle2, anchor2, anchor3 ..])
|
|
@@ -1024,23 +1089,23 @@ class VMobject(Mobject):
|
|
|
1024
1089
|
|
|
1025
1090
|
Returns
|
|
1026
1091
|
-------
|
|
1027
|
-
|
|
1092
|
+
tuple
|
|
1028
1093
|
Bezier control points.
|
|
1029
1094
|
"""
|
|
1030
1095
|
nppcc = self.n_points_per_cubic_curve
|
|
1031
1096
|
remainder = len(points) % nppcc
|
|
1032
1097
|
points = points[: len(points) - remainder]
|
|
1033
1098
|
# Basically take every nppcc element.
|
|
1034
|
-
return (points[i : i + nppcc] for i in range(0, len(points), nppcc))
|
|
1099
|
+
return tuple(points[i : i + nppcc] for i in range(0, len(points), nppcc))
|
|
1035
1100
|
|
|
1036
|
-
def get_cubic_bezier_tuples(self):
|
|
1101
|
+
def get_cubic_bezier_tuples(self) -> npt.NDArray[Point3D_Array]:
|
|
1037
1102
|
return self.get_cubic_bezier_tuples_from_points(self.points)
|
|
1038
1103
|
|
|
1039
1104
|
def _gen_subpaths_from_points(
|
|
1040
1105
|
self,
|
|
1041
|
-
points:
|
|
1042
|
-
filter_func:
|
|
1043
|
-
) ->
|
|
1106
|
+
points: Point3D_Array,
|
|
1107
|
+
filter_func: Callable[[int], bool],
|
|
1108
|
+
) -> Generator[Point3D_Array]:
|
|
1044
1109
|
"""Given an array of points defining the bezier curves of the vmobject, return subpaths formed by these points.
|
|
1045
1110
|
Here, Two bezier curves form a path if at least two of their anchors are evaluated True by the relation defined by filter_func.
|
|
1046
1111
|
|
|
@@ -1058,7 +1123,7 @@ class VMobject(Mobject):
|
|
|
1058
1123
|
|
|
1059
1124
|
Returns
|
|
1060
1125
|
-------
|
|
1061
|
-
|
|
1126
|
+
Generator[Point3D_Array]
|
|
1062
1127
|
subpaths formed by the points.
|
|
1063
1128
|
"""
|
|
1064
1129
|
nppcc = self.n_points_per_cubic_curve
|
|
@@ -1070,7 +1135,7 @@ class VMobject(Mobject):
|
|
|
1070
1135
|
if (i2 - i1) >= nppcc
|
|
1071
1136
|
)
|
|
1072
1137
|
|
|
1073
|
-
def get_subpaths_from_points(self, points):
|
|
1138
|
+
def get_subpaths_from_points(self, points: Point3D_Array) -> list[Point3D_Array]:
|
|
1074
1139
|
return list(
|
|
1075
1140
|
self._gen_subpaths_from_points(
|
|
1076
1141
|
points,
|
|
@@ -1078,25 +1143,27 @@ class VMobject(Mobject):
|
|
|
1078
1143
|
),
|
|
1079
1144
|
)
|
|
1080
1145
|
|
|
1081
|
-
def gen_subpaths_from_points_2d(
|
|
1146
|
+
def gen_subpaths_from_points_2d(
|
|
1147
|
+
self, points: Point3D_Array
|
|
1148
|
+
) -> Generator[Point3D_Array]:
|
|
1082
1149
|
return self._gen_subpaths_from_points(
|
|
1083
1150
|
points,
|
|
1084
1151
|
lambda n: not self.consider_points_equals_2d(points[n - 1], points[n]),
|
|
1085
1152
|
)
|
|
1086
1153
|
|
|
1087
|
-
def get_subpaths(self) ->
|
|
1154
|
+
def get_subpaths(self) -> list[Point3D_Array]:
|
|
1088
1155
|
"""Returns subpaths formed by the curves of the VMobject.
|
|
1089
1156
|
|
|
1090
1157
|
Subpaths are ranges of curves with each pair of consecutive curves having their end/start points coincident.
|
|
1091
1158
|
|
|
1092
1159
|
Returns
|
|
1093
1160
|
-------
|
|
1094
|
-
|
|
1161
|
+
list[Point3D_Array]
|
|
1095
1162
|
subpaths.
|
|
1096
1163
|
"""
|
|
1097
1164
|
return self.get_subpaths_from_points(self.points)
|
|
1098
1165
|
|
|
1099
|
-
def get_nth_curve_points(self, n: int) ->
|
|
1166
|
+
def get_nth_curve_points(self, n: int) -> Point3D_Array:
|
|
1100
1167
|
"""Returns the points defining the nth curve of the vmobject.
|
|
1101
1168
|
|
|
1102
1169
|
Parameters
|
|
@@ -1106,14 +1173,14 @@ class VMobject(Mobject):
|
|
|
1106
1173
|
|
|
1107
1174
|
Returns
|
|
1108
1175
|
-------
|
|
1109
|
-
|
|
1110
|
-
points
|
|
1176
|
+
Point3D_Array
|
|
1177
|
+
points defining the nth bezier curve (anchors, handles)
|
|
1111
1178
|
"""
|
|
1112
1179
|
assert n < self.get_num_curves()
|
|
1113
1180
|
nppcc = self.n_points_per_cubic_curve
|
|
1114
1181
|
return self.points[nppcc * n : nppcc * (n + 1)]
|
|
1115
1182
|
|
|
1116
|
-
def get_nth_curve_function(self, n: int) ->
|
|
1183
|
+
def get_nth_curve_function(self, n: int) -> Callable[[float], Point3D]:
|
|
1117
1184
|
"""Returns the expression of the nth curve.
|
|
1118
1185
|
|
|
1119
1186
|
Parameters
|
|
@@ -1123,7 +1190,7 @@ class VMobject(Mobject):
|
|
|
1123
1190
|
|
|
1124
1191
|
Returns
|
|
1125
1192
|
-------
|
|
1126
|
-
|
|
1193
|
+
Callable[float, Point3D]
|
|
1127
1194
|
expression of the nth bezier curve.
|
|
1128
1195
|
"""
|
|
1129
1196
|
return bezier(self.get_nth_curve_points(n))
|
|
@@ -1132,7 +1199,7 @@ class VMobject(Mobject):
|
|
|
1132
1199
|
self,
|
|
1133
1200
|
n: int,
|
|
1134
1201
|
sample_points: int | None = None,
|
|
1135
|
-
) ->
|
|
1202
|
+
) -> npt.NDArray[ManimFloat]:
|
|
1136
1203
|
"""Returns the array of short line lengths used for length approximation.
|
|
1137
1204
|
|
|
1138
1205
|
Parameters
|
|
@@ -1144,7 +1211,6 @@ class VMobject(Mobject):
|
|
|
1144
1211
|
|
|
1145
1212
|
Returns
|
|
1146
1213
|
-------
|
|
1147
|
-
np.ndarray
|
|
1148
1214
|
The short length-pieces of the nth curve.
|
|
1149
1215
|
"""
|
|
1150
1216
|
if sample_points is None:
|
|
@@ -1185,7 +1251,7 @@ class VMobject(Mobject):
|
|
|
1185
1251
|
self,
|
|
1186
1252
|
n: int,
|
|
1187
1253
|
sample_points: int | None = None,
|
|
1188
|
-
) -> tuple[
|
|
1254
|
+
) -> tuple[Callable[[float], Point3D], float]:
|
|
1189
1255
|
"""Returns the expression of the nth curve along with its (approximate) length.
|
|
1190
1256
|
|
|
1191
1257
|
Parameters
|
|
@@ -1197,7 +1263,7 @@ class VMobject(Mobject):
|
|
|
1197
1263
|
|
|
1198
1264
|
Returns
|
|
1199
1265
|
-------
|
|
1200
|
-
curve :
|
|
1266
|
+
curve : Callable[[float], Point3D]
|
|
1201
1267
|
The function for the nth curve.
|
|
1202
1268
|
length : :class:`float`
|
|
1203
1269
|
The length of the nth curve.
|
|
@@ -1215,19 +1281,19 @@ class VMobject(Mobject):
|
|
|
1215
1281
|
Returns
|
|
1216
1282
|
-------
|
|
1217
1283
|
int
|
|
1218
|
-
number of curves
|
|
1284
|
+
number of curves of the vmobject.
|
|
1219
1285
|
"""
|
|
1220
1286
|
nppcc = self.n_points_per_cubic_curve
|
|
1221
1287
|
return len(self.points) // nppcc
|
|
1222
1288
|
|
|
1223
1289
|
def get_curve_functions(
|
|
1224
1290
|
self,
|
|
1225
|
-
) ->
|
|
1291
|
+
) -> Generator[Callable[[float], Point3D]]:
|
|
1226
1292
|
"""Gets the functions for the curves of the mobject.
|
|
1227
1293
|
|
|
1228
1294
|
Returns
|
|
1229
1295
|
-------
|
|
1230
|
-
|
|
1296
|
+
Generator[Callable[[float], Point3D]]
|
|
1231
1297
|
The functions for the curves.
|
|
1232
1298
|
"""
|
|
1233
1299
|
|
|
@@ -1238,7 +1304,7 @@ class VMobject(Mobject):
|
|
|
1238
1304
|
|
|
1239
1305
|
def get_curve_functions_with_lengths(
|
|
1240
1306
|
self, **kwargs
|
|
1241
|
-
) ->
|
|
1307
|
+
) -> Generator[tuple[Callable[[float], Point3D], float]]:
|
|
1242
1308
|
"""Gets the functions and lengths of the curves for the mobject.
|
|
1243
1309
|
|
|
1244
1310
|
Parameters
|
|
@@ -1248,7 +1314,7 @@ class VMobject(Mobject):
|
|
|
1248
1314
|
|
|
1249
1315
|
Returns
|
|
1250
1316
|
-------
|
|
1251
|
-
|
|
1317
|
+
Generator[tuple[Callable[[float], Point3D], float]]
|
|
1252
1318
|
The functions and lengths of the curves.
|
|
1253
1319
|
"""
|
|
1254
1320
|
|
|
@@ -1257,7 +1323,7 @@ class VMobject(Mobject):
|
|
|
1257
1323
|
for n in range(num_curves):
|
|
1258
1324
|
yield self.get_nth_curve_function_with_length(n, **kwargs)
|
|
1259
1325
|
|
|
1260
|
-
def point_from_proportion(self, alpha: float) ->
|
|
1326
|
+
def point_from_proportion(self, alpha: float) -> Point3D:
|
|
1261
1327
|
"""Gets the point at a proportion along the path of the :class:`VMobject`.
|
|
1262
1328
|
|
|
1263
1329
|
Parameters
|
|
@@ -1303,7 +1369,7 @@ class VMobject(Mobject):
|
|
|
1303
1369
|
|
|
1304
1370
|
def proportion_from_point(
|
|
1305
1371
|
self,
|
|
1306
|
-
point:
|
|
1372
|
+
point: Iterable[float | int],
|
|
1307
1373
|
) -> float:
|
|
1308
1374
|
"""Returns the proportion along the path of the :class:`VMobject`
|
|
1309
1375
|
a particular given point is at.
|
|
@@ -1357,7 +1423,7 @@ class VMobject(Mobject):
|
|
|
1357
1423
|
|
|
1358
1424
|
return alpha
|
|
1359
1425
|
|
|
1360
|
-
def get_anchors_and_handles(self) ->
|
|
1426
|
+
def get_anchors_and_handles(self) -> list[Point3D_Array]:
|
|
1361
1427
|
"""Returns anchors1, handles1, handles2, anchors2,
|
|
1362
1428
|
where (anchors1[i], handles1[i], handles2[i], anchors2[i])
|
|
1363
1429
|
will be four points defining a cubic bezier curve
|
|
@@ -1365,50 +1431,52 @@ class VMobject(Mobject):
|
|
|
1365
1431
|
|
|
1366
1432
|
Returns
|
|
1367
1433
|
-------
|
|
1368
|
-
|
|
1434
|
+
`list[Point3D_Array]`
|
|
1369
1435
|
Iterable of the anchors and handles.
|
|
1370
1436
|
"""
|
|
1371
1437
|
nppcc = self.n_points_per_cubic_curve
|
|
1372
1438
|
return [self.points[i::nppcc] for i in range(nppcc)]
|
|
1373
1439
|
|
|
1374
|
-
def get_start_anchors(self) ->
|
|
1440
|
+
def get_start_anchors(self) -> Point3D_Array:
|
|
1375
1441
|
"""Returns the start anchors of the bezier curves.
|
|
1376
1442
|
|
|
1377
1443
|
Returns
|
|
1378
1444
|
-------
|
|
1379
|
-
|
|
1445
|
+
Point3D_Array
|
|
1380
1446
|
Starting anchors
|
|
1381
1447
|
"""
|
|
1382
|
-
return self.points[
|
|
1448
|
+
return self.points[:: self.n_points_per_cubic_curve]
|
|
1383
1449
|
|
|
1384
|
-
def get_end_anchors(self) ->
|
|
1450
|
+
def get_end_anchors(self) -> Point3D_Array:
|
|
1385
1451
|
"""Return the end anchors of the bezier curves.
|
|
1386
1452
|
|
|
1387
1453
|
Returns
|
|
1388
1454
|
-------
|
|
1389
|
-
|
|
1455
|
+
Point3D_Array
|
|
1390
1456
|
Starting anchors
|
|
1391
1457
|
"""
|
|
1392
1458
|
nppcc = self.n_points_per_cubic_curve
|
|
1393
1459
|
return self.points[nppcc - 1 :: nppcc]
|
|
1394
1460
|
|
|
1395
|
-
def get_anchors(self) ->
|
|
1461
|
+
def get_anchors(self) -> Point3D_Array:
|
|
1396
1462
|
"""Returns the anchors of the curves forming the VMobject.
|
|
1397
1463
|
|
|
1398
1464
|
Returns
|
|
1399
1465
|
-------
|
|
1400
|
-
|
|
1466
|
+
Point3D_Array
|
|
1401
1467
|
The anchors.
|
|
1402
1468
|
"""
|
|
1403
1469
|
if self.points.shape[0] == 1:
|
|
1404
1470
|
return self.points
|
|
1405
1471
|
return np.array(
|
|
1406
|
-
|
|
1472
|
+
tuple(it.chain(*zip(self.get_start_anchors(), self.get_end_anchors()))),
|
|
1407
1473
|
)
|
|
1408
1474
|
|
|
1409
|
-
def get_points_defining_boundary(self):
|
|
1475
|
+
def get_points_defining_boundary(self) -> Point3D_Array:
|
|
1410
1476
|
# Probably returns all anchors, but this is weird regarding the name of the method.
|
|
1411
|
-
return np.array(
|
|
1477
|
+
return np.array(
|
|
1478
|
+
tuple(it.chain(*(sm.get_anchors() for sm in self.get_family())))
|
|
1479
|
+
)
|
|
1412
1480
|
|
|
1413
1481
|
def get_arc_length(self, sample_points_per_curve: int | None = None) -> float:
|
|
1414
1482
|
"""Return the approximated length of the whole curve.
|
|
@@ -1432,7 +1500,7 @@ class VMobject(Mobject):
|
|
|
1432
1500
|
)
|
|
1433
1501
|
|
|
1434
1502
|
# Alignment
|
|
1435
|
-
def align_points(self, vmobject: VMobject):
|
|
1503
|
+
def align_points(self, vmobject: VMobject) -> Self:
|
|
1436
1504
|
"""Adds points to self and vmobject so that they both have the same number of subpaths, with
|
|
1437
1505
|
corresponding subpaths each containing the same number of points.
|
|
1438
1506
|
|
|
@@ -1503,7 +1571,7 @@ class VMobject(Mobject):
|
|
|
1503
1571
|
vmobject.set_points(new_path2)
|
|
1504
1572
|
return self
|
|
1505
1573
|
|
|
1506
|
-
def insert_n_curves(self, n: int):
|
|
1574
|
+
def insert_n_curves(self, n: int) -> Self:
|
|
1507
1575
|
"""Inserts n curves to the bezier curves of the vmobject.
|
|
1508
1576
|
|
|
1509
1577
|
Parameters
|
|
@@ -1527,7 +1595,9 @@ class VMobject(Mobject):
|
|
|
1527
1595
|
self.append_points([new_path_point])
|
|
1528
1596
|
return self
|
|
1529
1597
|
|
|
1530
|
-
def insert_n_curves_to_point_list(
|
|
1598
|
+
def insert_n_curves_to_point_list(
|
|
1599
|
+
self, n: int, points: Point3D_Array
|
|
1600
|
+
) -> npt.NDArray[BezierPoints]:
|
|
1531
1601
|
"""Given an array of k points defining a bezier curves (anchors and handles), returns points defining exactly k + n bezier curves.
|
|
1532
1602
|
|
|
1533
1603
|
Parameters
|
|
@@ -1539,7 +1609,6 @@ class VMobject(Mobject):
|
|
|
1539
1609
|
|
|
1540
1610
|
Returns
|
|
1541
1611
|
-------
|
|
1542
|
-
np.ndarray
|
|
1543
1612
|
Points generated.
|
|
1544
1613
|
"""
|
|
1545
1614
|
|
|
@@ -1582,7 +1651,7 @@ class VMobject(Mobject):
|
|
|
1582
1651
|
)
|
|
1583
1652
|
return new_points
|
|
1584
1653
|
|
|
1585
|
-
def align_rgbas(self, vmobject):
|
|
1654
|
+
def align_rgbas(self, vmobject: VMobject) -> Self:
|
|
1586
1655
|
attrs = ["fill_rgbas", "stroke_rgbas", "background_stroke_rgbas"]
|
|
1587
1656
|
for attr in attrs:
|
|
1588
1657
|
a1 = getattr(self, attr)
|
|
@@ -1595,14 +1664,16 @@ class VMobject(Mobject):
|
|
|
1595
1664
|
setattr(self, attr, new_a1)
|
|
1596
1665
|
return self
|
|
1597
1666
|
|
|
1598
|
-
def get_point_mobject(self, center=None):
|
|
1667
|
+
def get_point_mobject(self, center: Point3D | None = None) -> VectorizedPoint:
|
|
1599
1668
|
if center is None:
|
|
1600
1669
|
center = self.get_center()
|
|
1601
1670
|
point = VectorizedPoint(center)
|
|
1602
1671
|
point.match_style(self)
|
|
1603
1672
|
return point
|
|
1604
1673
|
|
|
1605
|
-
def interpolate_color(
|
|
1674
|
+
def interpolate_color(
|
|
1675
|
+
self, mobject1: VMobject, mobject2: VMobject, alpha: float
|
|
1676
|
+
) -> None:
|
|
1606
1677
|
attrs = [
|
|
1607
1678
|
"fill_rgbas",
|
|
1608
1679
|
"stroke_rgbas",
|
|
@@ -1626,7 +1697,7 @@ class VMobject(Mobject):
|
|
|
1626
1697
|
vmobject: VMobject,
|
|
1627
1698
|
a: float,
|
|
1628
1699
|
b: float,
|
|
1629
|
-
):
|
|
1700
|
+
) -> Self:
|
|
1630
1701
|
"""Given two bounds a and b, transforms the points of the self vmobject into the points of the vmobject
|
|
1631
1702
|
passed as parameter with respect to the bounds. Points here stand for control points of the bezier curves (anchors and handles)
|
|
1632
1703
|
|
|
@@ -1683,7 +1754,7 @@ class VMobject(Mobject):
|
|
|
1683
1754
|
)
|
|
1684
1755
|
return self
|
|
1685
1756
|
|
|
1686
|
-
def get_subcurve(self, a: float, b: float) ->
|
|
1757
|
+
def get_subcurve(self, a: float, b: float) -> Self:
|
|
1687
1758
|
"""Returns the subcurve of the VMobject between the interval [a, b].
|
|
1688
1759
|
The curve is a VMobject itself.
|
|
1689
1760
|
|
|
@@ -1711,7 +1782,7 @@ class VMobject(Mobject):
|
|
|
1711
1782
|
vmob.pointwise_become_partial(self, a, b)
|
|
1712
1783
|
return vmob
|
|
1713
1784
|
|
|
1714
|
-
def get_direction(self):
|
|
1785
|
+
def get_direction(self) -> Literal["CW", "CCW"]:
|
|
1715
1786
|
"""Uses :func:`~.space_ops.shoelace_direction` to calculate the direction.
|
|
1716
1787
|
The direction of points determines in which direction the
|
|
1717
1788
|
object is drawn, clockwise or counterclockwise.
|
|
@@ -1731,7 +1802,7 @@ class VMobject(Mobject):
|
|
|
1731
1802
|
"""
|
|
1732
1803
|
return shoelace_direction(self.get_start_anchors())
|
|
1733
1804
|
|
|
1734
|
-
def reverse_direction(self):
|
|
1805
|
+
def reverse_direction(self) -> Self:
|
|
1735
1806
|
"""Reverts the point direction by inverting the point order.
|
|
1736
1807
|
|
|
1737
1808
|
Returns
|
|
@@ -1756,7 +1827,7 @@ class VMobject(Mobject):
|
|
|
1756
1827
|
self.points = self.points[::-1]
|
|
1757
1828
|
return self
|
|
1758
1829
|
|
|
1759
|
-
def force_direction(self, target_direction:
|
|
1830
|
+
def force_direction(self, target_direction: Literal["CW", "CCW"]) -> Self:
|
|
1760
1831
|
"""Makes sure that points are either directed clockwise or
|
|
1761
1832
|
counterclockwise.
|
|
1762
1833
|
|
|
@@ -1835,21 +1906,16 @@ class VGroup(VMobject, metaclass=ConvertToOpenGL):
|
|
|
1835
1906
|
super().__init__(**kwargs)
|
|
1836
1907
|
self.add(*vmobjects)
|
|
1837
1908
|
|
|
1838
|
-
def __repr__(self):
|
|
1839
|
-
return (
|
|
1840
|
-
self.__class__.__name__
|
|
1841
|
-
+ "("
|
|
1842
|
-
+ ", ".join(str(mob) for mob in self.submobjects)
|
|
1843
|
-
+ ")"
|
|
1844
|
-
)
|
|
1909
|
+
def __repr__(self) -> str:
|
|
1910
|
+
return f'{self.__class__.__name__}({", ".join(str(mob) for mob in self.submobjects)})'
|
|
1845
1911
|
|
|
1846
|
-
def __str__(self):
|
|
1912
|
+
def __str__(self) -> str:
|
|
1847
1913
|
return (
|
|
1848
1914
|
f"{self.__class__.__name__} of {len(self.submobjects)} "
|
|
1849
1915
|
f"submobject{'s' if len(self.submobjects) > 0 else ''}"
|
|
1850
1916
|
)
|
|
1851
1917
|
|
|
1852
|
-
def add(self, *vmobjects: VMobject):
|
|
1918
|
+
def add(self, *vmobjects: VMobject) -> Self:
|
|
1853
1919
|
"""Checks if all passed elements are an instance of VMobject and then add them to submobjects
|
|
1854
1920
|
|
|
1855
1921
|
Parameters
|
|
@@ -1901,21 +1967,21 @@ class VGroup(VMobject, metaclass=ConvertToOpenGL):
|
|
|
1901
1967
|
raise TypeError("All submobjects must be of type VMobject")
|
|
1902
1968
|
return super().add(*vmobjects)
|
|
1903
1969
|
|
|
1904
|
-
def __add__(self, vmobject):
|
|
1970
|
+
def __add__(self, vmobject: VMobject) -> Self:
|
|
1905
1971
|
return VGroup(*self.submobjects, vmobject)
|
|
1906
1972
|
|
|
1907
|
-
def __iadd__(self, vmobject):
|
|
1973
|
+
def __iadd__(self, vmobject: VMobject) -> Self:
|
|
1908
1974
|
return self.add(vmobject)
|
|
1909
1975
|
|
|
1910
|
-
def __sub__(self, vmobject):
|
|
1976
|
+
def __sub__(self, vmobject: VMobject) -> Self:
|
|
1911
1977
|
copy = VGroup(*self.submobjects)
|
|
1912
1978
|
copy.remove(vmobject)
|
|
1913
1979
|
return copy
|
|
1914
1980
|
|
|
1915
|
-
def __isub__(self, vmobject):
|
|
1981
|
+
def __isub__(self, vmobject: VMobject) -> Self:
|
|
1916
1982
|
return self.remove(vmobject)
|
|
1917
1983
|
|
|
1918
|
-
def __setitem__(self, key: int, value: VMobject |
|
|
1984
|
+
def __setitem__(self, key: int, value: VMobject | Sequence[VMobject]) -> None:
|
|
1919
1985
|
"""Override the [] operator for item assignment.
|
|
1920
1986
|
|
|
1921
1987
|
Parameters
|
|
@@ -2042,27 +2108,25 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2042
2108
|
def __init__(
|
|
2043
2109
|
self,
|
|
2044
2110
|
mapping_or_iterable: (
|
|
2045
|
-
|
|
2046
|
-
| typing.Iterable[tuple[typing.Hashable, VMobject]]
|
|
2111
|
+
Mapping[Hashable, VMobject] | Iterable[tuple[Hashable, VMobject]]
|
|
2047
2112
|
) = {},
|
|
2048
2113
|
show_keys: bool = False,
|
|
2049
2114
|
**kwargs,
|
|
2050
|
-
):
|
|
2115
|
+
) -> None:
|
|
2051
2116
|
super().__init__(**kwargs)
|
|
2052
2117
|
self.show_keys = show_keys
|
|
2053
2118
|
self.submob_dict = {}
|
|
2054
2119
|
self.add(mapping_or_iterable)
|
|
2055
2120
|
|
|
2056
|
-
def __repr__(self):
|
|
2057
|
-
return __class__.__name__
|
|
2121
|
+
def __repr__(self) -> str:
|
|
2122
|
+
return f"{self.__class__.__name__}({repr(self.submob_dict)})"
|
|
2058
2123
|
|
|
2059
2124
|
def add(
|
|
2060
2125
|
self,
|
|
2061
2126
|
mapping_or_iterable: (
|
|
2062
|
-
|
|
2063
|
-
| typing.Iterable[tuple[typing.Hashable, VMobject]]
|
|
2127
|
+
Mapping[Hashable, VMobject] | Iterable[tuple[Hashable, VMobject]]
|
|
2064
2128
|
),
|
|
2065
|
-
):
|
|
2129
|
+
) -> Self:
|
|
2066
2130
|
"""Adds the key-value pairs to the :class:`VDict` object.
|
|
2067
2131
|
|
|
2068
2132
|
Also, it internally adds the value to the `submobjects` :class:`list`
|
|
@@ -2090,7 +2154,7 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2090
2154
|
|
|
2091
2155
|
return self
|
|
2092
2156
|
|
|
2093
|
-
def remove(self, key:
|
|
2157
|
+
def remove(self, key: Hashable) -> Self:
|
|
2094
2158
|
"""Removes the mobject from the :class:`VDict` object having the key `key`
|
|
2095
2159
|
|
|
2096
2160
|
Also, it internally removes the mobject from the `submobjects` :class:`list`
|
|
@@ -2118,7 +2182,7 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2118
2182
|
del self.submob_dict[key]
|
|
2119
2183
|
return self
|
|
2120
2184
|
|
|
2121
|
-
def __getitem__(self, key:
|
|
2185
|
+
def __getitem__(self, key: Hashable):
|
|
2122
2186
|
"""Override the [] operator for item retrieval.
|
|
2123
2187
|
|
|
2124
2188
|
Parameters
|
|
@@ -2140,7 +2204,7 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2140
2204
|
submob = self.submob_dict[key]
|
|
2141
2205
|
return submob
|
|
2142
2206
|
|
|
2143
|
-
def __setitem__(self, key:
|
|
2207
|
+
def __setitem__(self, key: Hashable, value: VMobject) -> None:
|
|
2144
2208
|
"""Override the [] operator for item assignment.
|
|
2145
2209
|
|
|
2146
2210
|
Parameters
|
|
@@ -2165,7 +2229,7 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2165
2229
|
self.remove(key)
|
|
2166
2230
|
self.add([(key, value)])
|
|
2167
2231
|
|
|
2168
|
-
def __delitem__(self, key:
|
|
2232
|
+
def __delitem__(self, key: Hashable):
|
|
2169
2233
|
"""Override the del operator for deleting an item.
|
|
2170
2234
|
|
|
2171
2235
|
Parameters
|
|
@@ -2197,7 +2261,7 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2197
2261
|
"""
|
|
2198
2262
|
del self.submob_dict[key]
|
|
2199
2263
|
|
|
2200
|
-
def __contains__(self, key:
|
|
2264
|
+
def __contains__(self, key: Hashable):
|
|
2201
2265
|
"""Override the in operator.
|
|
2202
2266
|
|
|
2203
2267
|
Parameters
|
|
@@ -2221,7 +2285,7 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2221
2285
|
"""
|
|
2222
2286
|
return key in self.submob_dict
|
|
2223
2287
|
|
|
2224
|
-
def get_all_submobjects(self):
|
|
2288
|
+
def get_all_submobjects(self) -> list[list]:
|
|
2225
2289
|
"""To get all the submobjects associated with a particular :class:`VDict` object
|
|
2226
2290
|
|
|
2227
2291
|
Returns
|
|
@@ -2239,7 +2303,7 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2239
2303
|
submobjects = self.submob_dict.values()
|
|
2240
2304
|
return submobjects
|
|
2241
2305
|
|
|
2242
|
-
def add_key_value_pair(self, key:
|
|
2306
|
+
def add_key_value_pair(self, key: Hashable, value: VMobject) -> None:
|
|
2243
2307
|
"""A utility function used by :meth:`add` to add the key-value pair
|
|
2244
2308
|
to :attr:`submob_dict`. Not really meant to be used externally.
|
|
2245
2309
|
|
|
@@ -2284,14 +2348,14 @@ class VDict(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2284
2348
|
class VectorizedPoint(VMobject, metaclass=ConvertToOpenGL):
|
|
2285
2349
|
def __init__(
|
|
2286
2350
|
self,
|
|
2287
|
-
location=ORIGIN,
|
|
2288
|
-
color=BLACK,
|
|
2289
|
-
fill_opacity=0,
|
|
2290
|
-
stroke_width=0,
|
|
2291
|
-
artificial_width=0.01,
|
|
2292
|
-
artificial_height=0.01,
|
|
2351
|
+
location: Point3D = ORIGIN,
|
|
2352
|
+
color: ManimColor = BLACK,
|
|
2353
|
+
fill_opacity: float = 0,
|
|
2354
|
+
stroke_width: float = 0,
|
|
2355
|
+
artificial_width: float = 0.01,
|
|
2356
|
+
artificial_height: float = 0.01,
|
|
2293
2357
|
**kwargs,
|
|
2294
|
-
):
|
|
2358
|
+
) -> None:
|
|
2295
2359
|
self.artificial_width = artificial_width
|
|
2296
2360
|
self.artificial_height = artificial_height
|
|
2297
2361
|
super().__init__(
|
|
@@ -2305,17 +2369,17 @@ class VectorizedPoint(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2305
2369
|
basecls = OpenGLVMobject if config.renderer == RendererType.OPENGL else VMobject
|
|
2306
2370
|
|
|
2307
2371
|
@basecls.width.getter
|
|
2308
|
-
def width(self):
|
|
2372
|
+
def width(self) -> float:
|
|
2309
2373
|
return self.artificial_width
|
|
2310
2374
|
|
|
2311
2375
|
@basecls.height.getter
|
|
2312
|
-
def height(self):
|
|
2376
|
+
def height(self) -> float:
|
|
2313
2377
|
return self.artificial_height
|
|
2314
2378
|
|
|
2315
|
-
def get_location(self):
|
|
2379
|
+
def get_location(self) -> Point3D:
|
|
2316
2380
|
return np.array(self.points[0])
|
|
2317
2381
|
|
|
2318
|
-
def set_location(self, new_loc):
|
|
2382
|
+
def set_location(self, new_loc: Point3D):
|
|
2319
2383
|
self.set_points(np.array([new_loc]))
|
|
2320
2384
|
|
|
2321
2385
|
|
|
@@ -2336,7 +2400,7 @@ class CurvesAsSubmobjects(VGroup):
|
|
|
2336
2400
|
|
|
2337
2401
|
"""
|
|
2338
2402
|
|
|
2339
|
-
def __init__(self, vmobject, **kwargs):
|
|
2403
|
+
def __init__(self, vmobject: VMobject, **kwargs) -> None:
|
|
2340
2404
|
super().__init__(**kwargs)
|
|
2341
2405
|
tuples = vmobject.get_cubic_bezier_tuples()
|
|
2342
2406
|
for tup in tuples:
|
|
@@ -2345,6 +2409,69 @@ class CurvesAsSubmobjects(VGroup):
|
|
|
2345
2409
|
part.match_style(vmobject)
|
|
2346
2410
|
self.add(part)
|
|
2347
2411
|
|
|
2412
|
+
def point_from_proportion(self, alpha: float) -> Point3D:
|
|
2413
|
+
"""Gets the point at a proportion along the path of the :class:`CurvesAsSubmobjects`.
|
|
2414
|
+
|
|
2415
|
+
Parameters
|
|
2416
|
+
----------
|
|
2417
|
+
alpha
|
|
2418
|
+
The proportion along the the path of the :class:`CurvesAsSubmobjects`.
|
|
2419
|
+
|
|
2420
|
+
Returns
|
|
2421
|
+
-------
|
|
2422
|
+
:class:`numpy.ndarray`
|
|
2423
|
+
The point on the :class:`CurvesAsSubmobjects`.
|
|
2424
|
+
|
|
2425
|
+
Raises
|
|
2426
|
+
------
|
|
2427
|
+
:exc:`ValueError`
|
|
2428
|
+
If ``alpha`` is not between 0 and 1.
|
|
2429
|
+
:exc:`Exception`
|
|
2430
|
+
If the :class:`CurvesAsSubmobjects` has no submobjects, or no submobject has points.
|
|
2431
|
+
"""
|
|
2432
|
+
if alpha < 0 or alpha > 1:
|
|
2433
|
+
raise ValueError(f"Alpha {alpha} not between 0 and 1.")
|
|
2434
|
+
|
|
2435
|
+
self._throw_error_if_no_submobjects()
|
|
2436
|
+
submobjs_with_pts = self._get_submobjects_with_points()
|
|
2437
|
+
|
|
2438
|
+
if alpha == 1:
|
|
2439
|
+
return submobjs_with_pts[-1].points[-1]
|
|
2440
|
+
|
|
2441
|
+
submobjs_arc_lengths = tuple(
|
|
2442
|
+
part.get_arc_length() for part in submobjs_with_pts
|
|
2443
|
+
)
|
|
2444
|
+
|
|
2445
|
+
total_length = sum(submobjs_arc_lengths)
|
|
2446
|
+
target_length = alpha * total_length
|
|
2447
|
+
current_length = 0
|
|
2448
|
+
|
|
2449
|
+
for i, part in enumerate(submobjs_with_pts):
|
|
2450
|
+
part_length = submobjs_arc_lengths[i]
|
|
2451
|
+
if current_length + part_length >= target_length:
|
|
2452
|
+
residue = (target_length - current_length) / part_length
|
|
2453
|
+
return part.point_from_proportion(residue)
|
|
2454
|
+
|
|
2455
|
+
current_length += part_length
|
|
2456
|
+
|
|
2457
|
+
def _throw_error_if_no_submobjects(self):
|
|
2458
|
+
if len(self.submobjects) == 0:
|
|
2459
|
+
caller_name = sys._getframe(1).f_code.co_name
|
|
2460
|
+
raise Exception(
|
|
2461
|
+
f"Cannot call CurvesAsSubmobjects.{caller_name} for a CurvesAsSubmobject with no submobjects"
|
|
2462
|
+
)
|
|
2463
|
+
|
|
2464
|
+
def _get_submobjects_with_points(self):
|
|
2465
|
+
submobjs_with_pts = tuple(
|
|
2466
|
+
part for part in self.submobjects if len(part.points) > 0
|
|
2467
|
+
)
|
|
2468
|
+
if len(submobjs_with_pts) == 0:
|
|
2469
|
+
caller_name = sys._getframe(1).f_code.co_name
|
|
2470
|
+
raise Exception(
|
|
2471
|
+
f"Cannot call CurvesAsSubmobjects.{caller_name} for a CurvesAsSubmobject whose submobjects have no points"
|
|
2472
|
+
)
|
|
2473
|
+
return submobjs_with_pts
|
|
2474
|
+
|
|
2348
2475
|
|
|
2349
2476
|
class DashedVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
2350
2477
|
"""A :class:`VMobject` composed of dashes instead of lines.
|
|
@@ -2402,14 +2529,14 @@ class DashedVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
2402
2529
|
|
|
2403
2530
|
def __init__(
|
|
2404
2531
|
self,
|
|
2405
|
-
vmobject,
|
|
2406
|
-
num_dashes=15,
|
|
2407
|
-
dashed_ratio=0.5,
|
|
2408
|
-
dash_offset=0,
|
|
2409
|
-
color=WHITE,
|
|
2410
|
-
equal_lengths=True,
|
|
2532
|
+
vmobject: VMobject,
|
|
2533
|
+
num_dashes: int = 15,
|
|
2534
|
+
dashed_ratio: float = 0.5,
|
|
2535
|
+
dash_offset: float = 0,
|
|
2536
|
+
color: ManimColor = WHITE,
|
|
2537
|
+
equal_lengths: bool = True,
|
|
2411
2538
|
**kwargs,
|
|
2412
|
-
):
|
|
2539
|
+
) -> None:
|
|
2413
2540
|
self.dashed_ratio = dashed_ratio
|
|
2414
2541
|
self.num_dashes = num_dashes
|
|
2415
2542
|
super().__init__(color=color, **kwargs)
|