manim 0.18.1__py3-none-any.whl → 0.19.0__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/__main__.py +45 -12
- manim/_config/__init__.py +2 -2
- manim/_config/cli_colors.py +8 -4
- manim/_config/default.cfg +0 -2
- manim/_config/logger_utils.py +5 -0
- manim/_config/utils.py +29 -38
- manim/animation/animation.py +148 -8
- manim/animation/composition.py +16 -13
- manim/animation/creation.py +184 -8
- manim/animation/fading.py +5 -8
- manim/animation/indication.py +93 -26
- manim/animation/movement.py +21 -3
- manim/animation/rotation.py +2 -1
- manim/animation/specialized.py +3 -5
- manim/animation/speedmodifier.py +3 -3
- manim/animation/transform.py +4 -5
- manim/animation/updaters/mobject_update_utils.py +17 -14
- manim/camera/camera.py +2 -2
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +52 -36
- manim/cli/checkhealth/checks.py +92 -76
- manim/cli/checkhealth/commands.py +12 -5
- manim/cli/default_group.py +148 -24
- manim/cli/init/commands.py +28 -23
- manim/cli/plugins/commands.py +13 -3
- manim/cli/render/commands.py +47 -42
- manim/cli/render/global_options.py +43 -9
- manim/cli/render/render_options.py +84 -19
- manim/constants.py +11 -4
- manim/mobject/frame.py +0 -1
- manim/mobject/geometry/arc.py +109 -75
- manim/mobject/geometry/boolean_ops.py +20 -17
- manim/mobject/geometry/labeled.py +300 -77
- manim/mobject/geometry/line.py +120 -60
- manim/mobject/geometry/polygram.py +109 -25
- manim/mobject/geometry/shape_matchers.py +35 -15
- manim/mobject/geometry/tips.py +36 -27
- manim/mobject/graph.py +48 -40
- manim/mobject/graphing/coordinate_systems.py +110 -45
- manim/mobject/graphing/functions.py +16 -10
- manim/mobject/graphing/number_line.py +23 -9
- manim/mobject/graphing/probability.py +2 -10
- manim/mobject/graphing/scale.py +6 -5
- manim/mobject/matrix.py +17 -19
- manim/mobject/mobject.py +149 -103
- manim/mobject/opengl/opengl_geometry.py +4 -8
- manim/mobject/opengl/opengl_mobject.py +506 -343
- manim/mobject/opengl/opengl_point_cloud_mobject.py +3 -7
- manim/mobject/opengl/opengl_surface.py +1 -2
- manim/mobject/opengl/opengl_vectorized_mobject.py +27 -65
- manim/mobject/svg/brace.py +61 -13
- manim/mobject/svg/svg_mobject.py +2 -1
- manim/mobject/table.py +11 -12
- manim/mobject/text/code_mobject.py +186 -550
- manim/mobject/text/numbers.py +7 -7
- manim/mobject/text/tex_mobject.py +22 -13
- manim/mobject/text/text_mobject.py +29 -20
- manim/mobject/three_d/polyhedra.py +98 -1
- manim/mobject/three_d/three_dimensions.py +59 -31
- manim/mobject/types/image_mobject.py +37 -23
- manim/mobject/types/point_cloud_mobject.py +103 -67
- manim/mobject/types/vectorized_mobject.py +387 -214
- manim/mobject/value_tracker.py +2 -1
- manim/mobject/vector_field.py +2 -4
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +2 -3
- manim/plugins/plugins_flags.py +3 -3
- manim/renderer/cairo_renderer.py +11 -11
- manim/renderer/opengl_renderer.py +19 -20
- manim/renderer/shader.py +2 -3
- manim/renderer/shader_wrapper.py +3 -2
- manim/scene/moving_camera_scene.py +23 -0
- manim/scene/scene.py +72 -41
- manim/scene/scene_file_writer.py +313 -164
- manim/scene/section.py +15 -15
- manim/scene/three_d_scene.py +8 -15
- manim/scene/vector_space_scene.py +3 -6
- manim/typing.py +326 -66
- manim/utils/bezier.py +1658 -381
- manim/utils/caching.py +11 -5
- manim/utils/color/AS2700.py +2 -0
- manim/utils/color/BS381.py +2 -0
- manim/utils/color/DVIPSNAMES.py +96 -0
- manim/utils/color/SVGNAMES.py +179 -0
- manim/utils/color/X11.py +3 -0
- manim/utils/color/XKCD.py +2 -0
- manim/utils/color/__init__.py +8 -5
- manim/utils/color/core.py +818 -301
- manim/utils/color/manim_colors.py +7 -9
- manim/utils/commands.py +40 -19
- manim/utils/config_ops.py +18 -13
- manim/utils/debug.py +8 -6
- manim/utils/deprecation.py +92 -43
- manim/utils/docbuild/autoaliasattr_directive.py +45 -8
- manim/utils/docbuild/autocolor_directive.py +12 -13
- manim/utils/docbuild/manim_directive.py +35 -29
- manim/utils/docbuild/module_parsing.py +74 -27
- manim/utils/family.py +3 -3
- manim/utils/family_ops.py +12 -4
- manim/utils/file_ops.py +22 -16
- manim/utils/hashing.py +7 -7
- manim/utils/images.py +10 -4
- manim/utils/ipython_magic.py +12 -8
- manim/utils/iterables.py +161 -119
- manim/utils/module_ops.py +55 -19
- manim/utils/opengl.py +68 -23
- manim/utils/parameter_parsing.py +3 -2
- manim/utils/paths.py +11 -5
- manim/utils/polylabel.py +168 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +69 -32
- manim/utils/simple_functions.py +24 -15
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +48 -37
- manim/utils/testing/_frames_testers.py +13 -8
- manim/utils/testing/_show_diff.py +5 -3
- manim/utils/testing/_test_class_makers.py +33 -18
- manim/utils/testing/frames_comparison.py +20 -14
- manim/utils/tex.py +4 -2
- manim/utils/tex_file_writing.py +45 -45
- manim/utils/tex_templates.py +1 -1
- manim/utils/unit.py +6 -5
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/METADATA +16 -9
- manim-0.19.0.dist-info/RECORD +221 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
- manim-0.18.1.dist-info/RECORD +0 -217
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE.community +0 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
manim/mobject/mobject.py
CHANGED
|
@@ -14,9 +14,10 @@ import random
|
|
|
14
14
|
import sys
|
|
15
15
|
import types
|
|
16
16
|
import warnings
|
|
17
|
+
from collections.abc import Iterable
|
|
17
18
|
from functools import partialmethod, reduce
|
|
18
19
|
from pathlib import Path
|
|
19
|
-
from typing import TYPE_CHECKING
|
|
20
|
+
from typing import TYPE_CHECKING
|
|
20
21
|
|
|
21
22
|
import numpy as np
|
|
22
23
|
|
|
@@ -39,17 +40,19 @@ from ..utils.paths import straight_path
|
|
|
39
40
|
from ..utils.space_ops import angle_between_vectors, normalize, rotation_matrix
|
|
40
41
|
|
|
41
42
|
if TYPE_CHECKING:
|
|
43
|
+
from typing import Any, Callable, Literal
|
|
44
|
+
|
|
42
45
|
from typing_extensions import Self, TypeAlias
|
|
43
46
|
|
|
44
47
|
from manim.typing import (
|
|
45
48
|
FunctionOverride,
|
|
46
|
-
Image,
|
|
47
|
-
ManimFloat,
|
|
48
|
-
ManimInt,
|
|
49
49
|
MappingFunction,
|
|
50
|
+
MultiMappingFunction,
|
|
50
51
|
PathFuncType,
|
|
52
|
+
PixelArray,
|
|
51
53
|
Point3D,
|
|
52
|
-
|
|
54
|
+
Point3DLike,
|
|
55
|
+
Point3DLike_Array,
|
|
53
56
|
Vector3D,
|
|
54
57
|
)
|
|
55
58
|
|
|
@@ -115,6 +118,63 @@ class Mobject:
|
|
|
115
118
|
self.generate_points()
|
|
116
119
|
self.init_colors()
|
|
117
120
|
|
|
121
|
+
def _assert_valid_submobjects(self, submobjects: Iterable[Mobject]) -> Self:
|
|
122
|
+
"""Check that all submobjects are actually instances of
|
|
123
|
+
:class:`Mobject`, and that none of them is ``self`` (a
|
|
124
|
+
:class:`Mobject` cannot contain itself).
|
|
125
|
+
|
|
126
|
+
This is an auxiliary function called when adding Mobjects to the
|
|
127
|
+
:attr:`submobjects` list.
|
|
128
|
+
|
|
129
|
+
This function is intended to be overridden by subclasses such as
|
|
130
|
+
:class:`VMobject`, which should assert that only other VMobjects
|
|
131
|
+
may be added into it.
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
submobjects
|
|
136
|
+
The list containing values to validate.
|
|
137
|
+
|
|
138
|
+
Returns
|
|
139
|
+
-------
|
|
140
|
+
:class:`Mobject`
|
|
141
|
+
The Mobject itself.
|
|
142
|
+
|
|
143
|
+
Raises
|
|
144
|
+
------
|
|
145
|
+
TypeError
|
|
146
|
+
If any of the values in `submobjects` is not a :class:`Mobject`.
|
|
147
|
+
ValueError
|
|
148
|
+
If there was an attempt to add a :class:`Mobject` as its own
|
|
149
|
+
submobject.
|
|
150
|
+
"""
|
|
151
|
+
return self._assert_valid_submobjects_internal(submobjects, Mobject)
|
|
152
|
+
|
|
153
|
+
def _assert_valid_submobjects_internal(
|
|
154
|
+
self, submobjects: list[Mobject], mob_class: type[Mobject]
|
|
155
|
+
) -> Self:
|
|
156
|
+
for i, submob in enumerate(submobjects):
|
|
157
|
+
if not isinstance(submob, mob_class):
|
|
158
|
+
error_message = (
|
|
159
|
+
f"Only values of type {mob_class.__name__} can be added "
|
|
160
|
+
f"as submobjects of {type(self).__name__}, but the value "
|
|
161
|
+
f"{submob} (at index {i}) is of type "
|
|
162
|
+
f"{type(submob).__name__}."
|
|
163
|
+
)
|
|
164
|
+
# Intended for subclasses such as VMobject, which
|
|
165
|
+
# cannot have regular Mobjects as submobjects
|
|
166
|
+
if isinstance(submob, Mobject):
|
|
167
|
+
error_message += (
|
|
168
|
+
" You can try adding this value into a Group instead."
|
|
169
|
+
)
|
|
170
|
+
raise TypeError(error_message)
|
|
171
|
+
if submob is self:
|
|
172
|
+
raise ValueError(
|
|
173
|
+
f"Cannot add {type(self).__name__} as a submobject of "
|
|
174
|
+
f"itself (at index {i})."
|
|
175
|
+
)
|
|
176
|
+
return self
|
|
177
|
+
|
|
118
178
|
@classmethod
|
|
119
179
|
def animation_override_for(
|
|
120
180
|
cls,
|
|
@@ -258,7 +318,9 @@ class Mobject:
|
|
|
258
318
|
|
|
259
319
|
::
|
|
260
320
|
|
|
261
|
-
self.play(
|
|
321
|
+
self.play(
|
|
322
|
+
my_mobject.animate.shift(RIGHT), my_mobject.animate.rotate(PI)
|
|
323
|
+
)
|
|
262
324
|
|
|
263
325
|
make use of method chaining.
|
|
264
326
|
|
|
@@ -348,14 +410,14 @@ class Mobject:
|
|
|
348
410
|
"""Sets :attr:`points` to be an empty array."""
|
|
349
411
|
self.points = np.zeros((0, self.dim))
|
|
350
412
|
|
|
351
|
-
def init_colors(self) ->
|
|
413
|
+
def init_colors(self) -> object:
|
|
352
414
|
"""Initializes the colors.
|
|
353
415
|
|
|
354
416
|
Gets called upon creation. This is an empty method that can be implemented by
|
|
355
417
|
subclasses.
|
|
356
418
|
"""
|
|
357
419
|
|
|
358
|
-
def generate_points(self) ->
|
|
420
|
+
def generate_points(self) -> object:
|
|
359
421
|
"""Initializes :attr:`points` and therefore the shape.
|
|
360
422
|
|
|
361
423
|
Gets called upon creation. This is an empty method that can be implemented by
|
|
@@ -413,12 +475,19 @@ class Mobject:
|
|
|
413
475
|
>>> len(outer.submobjects)
|
|
414
476
|
1
|
|
415
477
|
|
|
478
|
+
Only Mobjects can be added::
|
|
479
|
+
|
|
480
|
+
>>> outer.add(3)
|
|
481
|
+
Traceback (most recent call last):
|
|
482
|
+
...
|
|
483
|
+
TypeError: Only values of type Mobject can be added as submobjects of Mobject, but the value 3 (at index 0) is of type int.
|
|
484
|
+
|
|
416
485
|
Adding an object to itself raises an error::
|
|
417
486
|
|
|
418
487
|
>>> outer.add(outer)
|
|
419
488
|
Traceback (most recent call last):
|
|
420
489
|
...
|
|
421
|
-
ValueError: Mobject
|
|
490
|
+
ValueError: Cannot add Mobject as a submobject of itself (at index 0).
|
|
422
491
|
|
|
423
492
|
A given mobject cannot be added as a submobject
|
|
424
493
|
twice to some parent::
|
|
@@ -432,12 +501,7 @@ class Mobject:
|
|
|
432
501
|
[child]
|
|
433
502
|
|
|
434
503
|
"""
|
|
435
|
-
|
|
436
|
-
if not isinstance(m, Mobject):
|
|
437
|
-
raise TypeError("All submobjects must be of type Mobject")
|
|
438
|
-
if m is self:
|
|
439
|
-
raise ValueError("Mobject cannot contain self")
|
|
440
|
-
|
|
504
|
+
self._assert_valid_submobjects(mobjects)
|
|
441
505
|
unique_mobjects = remove_list_redundancies(mobjects)
|
|
442
506
|
if len(mobjects) != len(unique_mobjects):
|
|
443
507
|
logger.warning(
|
|
@@ -463,10 +527,7 @@ class Mobject:
|
|
|
463
527
|
mobject
|
|
464
528
|
The mobject to be inserted.
|
|
465
529
|
"""
|
|
466
|
-
|
|
467
|
-
raise TypeError("All submobjects must be of type Mobject")
|
|
468
|
-
if mobject is self:
|
|
469
|
-
raise ValueError("Mobject cannot contain self")
|
|
530
|
+
self._assert_valid_submobjects([mobject])
|
|
470
531
|
self.submobjects.insert(index, mobject)
|
|
471
532
|
|
|
472
533
|
def __add__(self, mobject: Mobject):
|
|
@@ -519,13 +580,7 @@ class Mobject:
|
|
|
519
580
|
:meth:`add`
|
|
520
581
|
|
|
521
582
|
"""
|
|
522
|
-
|
|
523
|
-
raise ValueError("A mobject shouldn't contain itself")
|
|
524
|
-
|
|
525
|
-
for mobject in mobjects:
|
|
526
|
-
if not isinstance(mobject, Mobject):
|
|
527
|
-
raise TypeError("All submobjects must be of type Mobject")
|
|
528
|
-
|
|
583
|
+
self._assert_valid_submobjects(mobjects)
|
|
529
584
|
self.remove(*mobjects)
|
|
530
585
|
# dict.fromkeys() removes duplicates while maintaining order
|
|
531
586
|
self.submobjects = list(dict.fromkeys(mobjects)) + self.submobjects
|
|
@@ -615,7 +670,6 @@ class Mobject:
|
|
|
615
670
|
>>> mob.foo
|
|
616
671
|
0
|
|
617
672
|
"""
|
|
618
|
-
|
|
619
673
|
for attr, value in kwargs.items():
|
|
620
674
|
setattr(self, attr, value)
|
|
621
675
|
|
|
@@ -697,7 +751,6 @@ class Mobject:
|
|
|
697
751
|
:meth:`length_over_dim`
|
|
698
752
|
|
|
699
753
|
"""
|
|
700
|
-
|
|
701
754
|
# Get the length across the X dimension
|
|
702
755
|
return self.length_over_dim(0)
|
|
703
756
|
|
|
@@ -734,7 +787,6 @@ class Mobject:
|
|
|
734
787
|
:meth:`length_over_dim`
|
|
735
788
|
|
|
736
789
|
"""
|
|
737
|
-
|
|
738
790
|
# Get the length across the Y dimension
|
|
739
791
|
return self.length_over_dim(1)
|
|
740
792
|
|
|
@@ -755,7 +807,6 @@ class Mobject:
|
|
|
755
807
|
:meth:`length_over_dim`
|
|
756
808
|
|
|
757
809
|
"""
|
|
758
|
-
|
|
759
810
|
# Get the length across the Z dimension
|
|
760
811
|
return self.length_over_dim(2)
|
|
761
812
|
|
|
@@ -767,14 +818,14 @@ class Mobject:
|
|
|
767
818
|
def get_array_attrs(self) -> list[Literal["points"]]:
|
|
768
819
|
return ["points"]
|
|
769
820
|
|
|
770
|
-
def apply_over_attr_arrays(self, func:
|
|
821
|
+
def apply_over_attr_arrays(self, func: MultiMappingFunction) -> Self:
|
|
771
822
|
for attr in self.get_array_attrs():
|
|
772
823
|
setattr(self, attr, func(getattr(self, attr)))
|
|
773
824
|
return self
|
|
774
825
|
|
|
775
826
|
# Displaying
|
|
776
827
|
|
|
777
|
-
def get_image(self, camera=None) ->
|
|
828
|
+
def get_image(self, camera=None) -> PixelArray:
|
|
778
829
|
if camera is None:
|
|
779
830
|
from ..camera.camera import Camera
|
|
780
831
|
|
|
@@ -787,7 +838,8 @@ class Mobject:
|
|
|
787
838
|
|
|
788
839
|
def save_image(self, name: str | None = None) -> None:
|
|
789
840
|
"""Saves an image of only this :class:`Mobject` at its position to a png
|
|
790
|
-
file.
|
|
841
|
+
file.
|
|
842
|
+
"""
|
|
791
843
|
self.get_image().save(
|
|
792
844
|
Path(config.get_dir("video_dir")).joinpath((name or str(self)) + ".png"),
|
|
793
845
|
)
|
|
@@ -978,7 +1030,6 @@ class Mobject:
|
|
|
978
1030
|
:meth:`remove_updater`
|
|
979
1031
|
:class:`~.UpdateFromFunc`
|
|
980
1032
|
"""
|
|
981
|
-
|
|
982
1033
|
if index is None:
|
|
983
1034
|
self.updaters.append(update_function)
|
|
984
1035
|
else:
|
|
@@ -1068,7 +1119,6 @@ class Mobject:
|
|
|
1068
1119
|
:meth:`clear_updaters`
|
|
1069
1120
|
|
|
1070
1121
|
"""
|
|
1071
|
-
|
|
1072
1122
|
self.clear_updaters()
|
|
1073
1123
|
for updater in mobject.get_updaters():
|
|
1074
1124
|
self.add_updater(updater)
|
|
@@ -1094,7 +1144,6 @@ class Mobject:
|
|
|
1094
1144
|
:meth:`add_updater`
|
|
1095
1145
|
|
|
1096
1146
|
"""
|
|
1097
|
-
|
|
1098
1147
|
self.updating_suspended = True
|
|
1099
1148
|
if recursive:
|
|
1100
1149
|
for submob in self.submobjects:
|
|
@@ -1169,7 +1218,6 @@ class Mobject:
|
|
|
1169
1218
|
--------
|
|
1170
1219
|
:meth:`move_to`
|
|
1171
1220
|
"""
|
|
1172
|
-
|
|
1173
1221
|
total_vector = reduce(op.add, vectors)
|
|
1174
1222
|
for mob in self.family_members_with_points():
|
|
1175
1223
|
mob.points = mob.points.astype("float")
|
|
@@ -1231,7 +1279,7 @@ class Mobject:
|
|
|
1231
1279
|
self,
|
|
1232
1280
|
angle: float,
|
|
1233
1281
|
axis: Vector3D = OUT,
|
|
1234
|
-
about_point:
|
|
1282
|
+
about_point: Point3DLike | None = None,
|
|
1235
1283
|
**kwargs,
|
|
1236
1284
|
) -> Self:
|
|
1237
1285
|
"""Rotates the :class:`~.Mobject` about a certain point."""
|
|
@@ -1261,7 +1309,7 @@ class Mobject:
|
|
|
1261
1309
|
return self.rotate(TAU / 2, axis, **kwargs)
|
|
1262
1310
|
|
|
1263
1311
|
def stretch(self, factor: float, dim: int, **kwargs) -> Self:
|
|
1264
|
-
def func(points):
|
|
1312
|
+
def func(points: Point3D_Array) -> Point3D_Array:
|
|
1265
1313
|
points[:, dim] *= factor
|
|
1266
1314
|
return points
|
|
1267
1315
|
|
|
@@ -1272,9 +1320,12 @@ class Mobject:
|
|
|
1272
1320
|
# Default to applying matrix about the origin, not mobjects center
|
|
1273
1321
|
if len(kwargs) == 0:
|
|
1274
1322
|
kwargs["about_point"] = ORIGIN
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1323
|
+
|
|
1324
|
+
def multi_mapping_function(points: Point3D_Array) -> Point3D_Array:
|
|
1325
|
+
result: Point3D_Array = np.apply_along_axis(function, 1, points)
|
|
1326
|
+
return result
|
|
1327
|
+
|
|
1328
|
+
self.apply_points_function_about_point(multi_mapping_function, **kwargs)
|
|
1278
1329
|
return self
|
|
1279
1330
|
|
|
1280
1331
|
def apply_function_to_position(self, function: MappingFunction) -> Self:
|
|
@@ -1353,11 +1404,12 @@ class Mobject:
|
|
|
1353
1404
|
# Note, much of these are now redundant with default behavior of
|
|
1354
1405
|
# above methods
|
|
1355
1406
|
|
|
1407
|
+
# TODO: name is inconsistent with OpenGLMobject.apply_points_function()
|
|
1356
1408
|
def apply_points_function_about_point(
|
|
1357
1409
|
self,
|
|
1358
|
-
func:
|
|
1359
|
-
about_point:
|
|
1360
|
-
about_edge=None,
|
|
1410
|
+
func: MultiMappingFunction,
|
|
1411
|
+
about_point: Point3DLike | None = None,
|
|
1412
|
+
about_edge: Vector3D | None = None,
|
|
1361
1413
|
) -> Self:
|
|
1362
1414
|
if about_point is None:
|
|
1363
1415
|
if about_edge is None:
|
|
@@ -1454,7 +1506,7 @@ class Mobject:
|
|
|
1454
1506
|
tex_top.to_edge(UP)
|
|
1455
1507
|
tex_side = Tex("I am moving to the side!")
|
|
1456
1508
|
c = Circle().shift(2*DOWN)
|
|
1457
|
-
self.add(tex_top, tex_side)
|
|
1509
|
+
self.add(tex_top, tex_side, c)
|
|
1458
1510
|
tex_side.to_edge(LEFT)
|
|
1459
1511
|
c.to_edge(RIGHT, buff=0)
|
|
1460
1512
|
|
|
@@ -1463,7 +1515,7 @@ class Mobject:
|
|
|
1463
1515
|
|
|
1464
1516
|
def next_to(
|
|
1465
1517
|
self,
|
|
1466
|
-
mobject_or_point: Mobject |
|
|
1518
|
+
mobject_or_point: Mobject | Point3DLike,
|
|
1467
1519
|
direction: Vector3D = RIGHT,
|
|
1468
1520
|
buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
|
|
1469
1521
|
aligned_edge: Vector3D = ORIGIN,
|
|
@@ -1528,11 +1580,9 @@ class Mobject:
|
|
|
1528
1580
|
return True
|
|
1529
1581
|
if self.get_bottom()[1] > config["frame_y_radius"]:
|
|
1530
1582
|
return True
|
|
1531
|
-
|
|
1532
|
-
return True
|
|
1533
|
-
return False
|
|
1583
|
+
return self.get_top()[1] < -config["frame_y_radius"]
|
|
1534
1584
|
|
|
1535
|
-
def stretch_about_point(self, factor: float, dim: int, point:
|
|
1585
|
+
def stretch_about_point(self, factor: float, dim: int, point: Point3DLike) -> Self:
|
|
1536
1586
|
return self.stretch(factor, dim, about_point=point)
|
|
1537
1587
|
|
|
1538
1588
|
def rescale_to_fit(
|
|
@@ -1562,15 +1612,14 @@ class Mobject:
|
|
|
1562
1612
|
>>> from manim import *
|
|
1563
1613
|
>>> sq = Square()
|
|
1564
1614
|
>>> sq.height
|
|
1565
|
-
2.0
|
|
1615
|
+
np.float64(2.0)
|
|
1566
1616
|
>>> sq.scale_to_fit_width(5)
|
|
1567
1617
|
Square
|
|
1568
1618
|
>>> sq.width
|
|
1569
|
-
5.0
|
|
1619
|
+
np.float64(5.0)
|
|
1570
1620
|
>>> sq.height
|
|
1571
|
-
5.0
|
|
1621
|
+
np.float64(5.0)
|
|
1572
1622
|
"""
|
|
1573
|
-
|
|
1574
1623
|
return self.rescale_to_fit(width, 0, stretch=False, **kwargs)
|
|
1575
1624
|
|
|
1576
1625
|
def stretch_to_fit_width(self, width: float, **kwargs) -> Self:
|
|
@@ -1588,15 +1637,14 @@ class Mobject:
|
|
|
1588
1637
|
>>> from manim import *
|
|
1589
1638
|
>>> sq = Square()
|
|
1590
1639
|
>>> sq.height
|
|
1591
|
-
2.0
|
|
1640
|
+
np.float64(2.0)
|
|
1592
1641
|
>>> sq.stretch_to_fit_width(5)
|
|
1593
1642
|
Square
|
|
1594
1643
|
>>> sq.width
|
|
1595
|
-
5.0
|
|
1644
|
+
np.float64(5.0)
|
|
1596
1645
|
>>> sq.height
|
|
1597
|
-
2.0
|
|
1646
|
+
np.float64(2.0)
|
|
1598
1647
|
"""
|
|
1599
|
-
|
|
1600
1648
|
return self.rescale_to_fit(width, 0, stretch=True, **kwargs)
|
|
1601
1649
|
|
|
1602
1650
|
def scale_to_fit_height(self, height: float, **kwargs) -> Self:
|
|
@@ -1614,15 +1662,14 @@ class Mobject:
|
|
|
1614
1662
|
>>> from manim import *
|
|
1615
1663
|
>>> sq = Square()
|
|
1616
1664
|
>>> sq.width
|
|
1617
|
-
2.0
|
|
1665
|
+
np.float64(2.0)
|
|
1618
1666
|
>>> sq.scale_to_fit_height(5)
|
|
1619
1667
|
Square
|
|
1620
1668
|
>>> sq.height
|
|
1621
|
-
5.0
|
|
1669
|
+
np.float64(5.0)
|
|
1622
1670
|
>>> sq.width
|
|
1623
|
-
5.0
|
|
1671
|
+
np.float64(5.0)
|
|
1624
1672
|
"""
|
|
1625
|
-
|
|
1626
1673
|
return self.rescale_to_fit(height, 1, stretch=False, **kwargs)
|
|
1627
1674
|
|
|
1628
1675
|
def stretch_to_fit_height(self, height: float, **kwargs) -> Self:
|
|
@@ -1640,25 +1687,22 @@ class Mobject:
|
|
|
1640
1687
|
>>> from manim import *
|
|
1641
1688
|
>>> sq = Square()
|
|
1642
1689
|
>>> sq.width
|
|
1643
|
-
2.0
|
|
1690
|
+
np.float64(2.0)
|
|
1644
1691
|
>>> sq.stretch_to_fit_height(5)
|
|
1645
1692
|
Square
|
|
1646
1693
|
>>> sq.height
|
|
1647
|
-
5.0
|
|
1694
|
+
np.float64(5.0)
|
|
1648
1695
|
>>> sq.width
|
|
1649
|
-
2.0
|
|
1696
|
+
np.float64(2.0)
|
|
1650
1697
|
"""
|
|
1651
|
-
|
|
1652
1698
|
return self.rescale_to_fit(height, 1, stretch=True, **kwargs)
|
|
1653
1699
|
|
|
1654
1700
|
def scale_to_fit_depth(self, depth: float, **kwargs) -> Self:
|
|
1655
1701
|
"""Scales the :class:`~.Mobject` to fit a depth while keeping width/height proportional."""
|
|
1656
|
-
|
|
1657
1702
|
return self.rescale_to_fit(depth, 2, stretch=False, **kwargs)
|
|
1658
1703
|
|
|
1659
1704
|
def stretch_to_fit_depth(self, depth: float, **kwargs) -> Self:
|
|
1660
1705
|
"""Stretches the :class:`~.Mobject` to fit a depth, not keeping width/height proportional."""
|
|
1661
|
-
|
|
1662
1706
|
return self.rescale_to_fit(depth, 2, stretch=True, **kwargs)
|
|
1663
1707
|
|
|
1664
1708
|
def set_coord(self, value, dim: int, direction: Vector3D = ORIGIN) -> Self:
|
|
@@ -1688,7 +1732,7 @@ class Mobject:
|
|
|
1688
1732
|
|
|
1689
1733
|
def move_to(
|
|
1690
1734
|
self,
|
|
1691
|
-
point_or_mobject:
|
|
1735
|
+
point_or_mobject: Point3DLike | Mobject,
|
|
1692
1736
|
aligned_edge: Vector3D = ORIGIN,
|
|
1693
1737
|
coor_mask: Vector3D = np.array([1, 1, 1]),
|
|
1694
1738
|
) -> Self:
|
|
@@ -1730,13 +1774,16 @@ class Mobject:
|
|
|
1730
1774
|
self.scale((length + buff) / length)
|
|
1731
1775
|
return self
|
|
1732
1776
|
|
|
1733
|
-
def put_start_and_end_on(self, start:
|
|
1777
|
+
def put_start_and_end_on(self, start: Point3DLike, end: Point3DLike) -> Self:
|
|
1734
1778
|
curr_start, curr_end = self.get_start_and_end()
|
|
1735
1779
|
curr_vect = curr_end - curr_start
|
|
1736
1780
|
if np.all(curr_vect == 0):
|
|
1737
|
-
self.points
|
|
1781
|
+
# TODO: this looks broken. It makes self.points a Point3D instead
|
|
1782
|
+
# of a Point3D_Array. However, modifying this breaks some tests
|
|
1783
|
+
# where this is currently expected.
|
|
1784
|
+
self.points = np.array(start)
|
|
1738
1785
|
return self
|
|
1739
|
-
target_vect = np.
|
|
1786
|
+
target_vect = np.asarray(end) - np.asarray(start)
|
|
1740
1787
|
axis = (
|
|
1741
1788
|
normalize(np.cross(curr_vect, target_vect))
|
|
1742
1789
|
if np.linalg.norm(np.cross(curr_vect, target_vect)) != 0
|
|
@@ -1785,7 +1832,6 @@ class Mobject:
|
|
|
1785
1832
|
:class:`~.BackgroundRectangle`
|
|
1786
1833
|
|
|
1787
1834
|
"""
|
|
1788
|
-
|
|
1789
1835
|
# TODO, this does not behave well when the mobject has points,
|
|
1790
1836
|
# since it gets displayed on top
|
|
1791
1837
|
from manim.mobject.geometry.shape_matchers import BackgroundRectangle
|
|
@@ -1838,7 +1884,7 @@ class Mobject:
|
|
|
1838
1884
|
|
|
1839
1885
|
def set_colors_by_radial_gradient(
|
|
1840
1886
|
self,
|
|
1841
|
-
center:
|
|
1887
|
+
center: Point3DLike | None = None,
|
|
1842
1888
|
radius: float = 1,
|
|
1843
1889
|
inner_color: ParsableManimColor = WHITE,
|
|
1844
1890
|
outer_color: ParsableManimColor = BLACK,
|
|
@@ -1866,7 +1912,7 @@ class Mobject:
|
|
|
1866
1912
|
|
|
1867
1913
|
def set_submobject_colors_by_radial_gradient(
|
|
1868
1914
|
self,
|
|
1869
|
-
center:
|
|
1915
|
+
center: Point3DLike | None = None,
|
|
1870
1916
|
radius: float = 1,
|
|
1871
1917
|
inner_color: ParsableManimColor = WHITE,
|
|
1872
1918
|
outer_color: ParsableManimColor = BLACK,
|
|
@@ -1937,14 +1983,15 @@ class Mobject:
|
|
|
1937
1983
|
|
|
1938
1984
|
def reduce_across_dimension(self, reduce_func: Callable, dim: int):
|
|
1939
1985
|
"""Find the min or max value from a dimension across all points in this and submobjects."""
|
|
1940
|
-
assert dim >= 0
|
|
1986
|
+
assert dim >= 0
|
|
1987
|
+
assert dim <= 2
|
|
1941
1988
|
if len(self.submobjects) == 0 and len(self.points) == 0:
|
|
1942
1989
|
# If we have no points and no submobjects, return 0 (e.g. center)
|
|
1943
1990
|
return 0
|
|
1944
1991
|
|
|
1945
1992
|
# If we do not have points (but do have submobjects)
|
|
1946
1993
|
# use only the points from those.
|
|
1947
|
-
if len(self.points) == 0:
|
|
1994
|
+
if len(self.points) == 0: # noqa: SIM108
|
|
1948
1995
|
rv = None
|
|
1949
1996
|
else:
|
|
1950
1997
|
# Otherwise, be sure to include our own points
|
|
@@ -1953,10 +2000,7 @@ class Mobject:
|
|
|
1953
2000
|
# smallest dimension they have and compare it to the return value.
|
|
1954
2001
|
for mobj in self.submobjects:
|
|
1955
2002
|
value = mobj.reduce_across_dimension(reduce_func, dim)
|
|
1956
|
-
if rv is None
|
|
1957
|
-
rv = value
|
|
1958
|
-
else:
|
|
1959
|
-
rv = reduce_func([value, rv])
|
|
2003
|
+
rv = value if rv is None else reduce_func([value, rv])
|
|
1960
2004
|
return rv
|
|
1961
2005
|
|
|
1962
2006
|
def nonempty_submobjects(self) -> list[Self]:
|
|
@@ -1994,11 +2038,14 @@ class Mobject:
|
|
|
1994
2038
|
return len(self.points)
|
|
1995
2039
|
|
|
1996
2040
|
def get_extremum_along_dim(
|
|
1997
|
-
self, points:
|
|
1998
|
-
) ->
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2041
|
+
self, points: Point3DLike_Array | None = None, dim: int = 0, key: int = 0
|
|
2042
|
+
) -> float:
|
|
2043
|
+
np_points: Point3D_Array = (
|
|
2044
|
+
self.get_points_defining_boundary()
|
|
2045
|
+
if points is None
|
|
2046
|
+
else np.asarray(points)
|
|
2047
|
+
)
|
|
2048
|
+
values = np_points[:, dim]
|
|
2002
2049
|
if key < 0:
|
|
2003
2050
|
return np.min(values)
|
|
2004
2051
|
elif key == 0:
|
|
@@ -2013,7 +2060,7 @@ class Mobject:
|
|
|
2013
2060
|
|
|
2014
2061
|
::
|
|
2015
2062
|
|
|
2016
|
-
sample = Arc(start_angle=PI/7, angle
|
|
2063
|
+
sample = Arc(start_angle=PI / 7, angle=PI / 5)
|
|
2017
2064
|
|
|
2018
2065
|
# These are all equivalent
|
|
2019
2066
|
max_y_1 = sample.get_top()[1]
|
|
@@ -2113,15 +2160,15 @@ class Mobject:
|
|
|
2113
2160
|
"""Meant to generalize ``get_x``, ``get_y`` and ``get_z``"""
|
|
2114
2161
|
return self.get_extremum_along_dim(dim=dim, key=direction[dim])
|
|
2115
2162
|
|
|
2116
|
-
def get_x(self, direction: Vector3D = ORIGIN) ->
|
|
2163
|
+
def get_x(self, direction: Vector3D = ORIGIN) -> float:
|
|
2117
2164
|
"""Returns x Point3D of the center of the :class:`~.Mobject` as ``float``"""
|
|
2118
2165
|
return self.get_coord(0, direction)
|
|
2119
2166
|
|
|
2120
|
-
def get_y(self, direction: Vector3D = ORIGIN) ->
|
|
2167
|
+
def get_y(self, direction: Vector3D = ORIGIN) -> float:
|
|
2121
2168
|
"""Returns y Point3D of the center of the :class:`~.Mobject` as ``float``"""
|
|
2122
2169
|
return self.get_coord(1, direction)
|
|
2123
2170
|
|
|
2124
|
-
def get_z(self, direction: Vector3D = ORIGIN) ->
|
|
2171
|
+
def get_z(self, direction: Vector3D = ORIGIN) -> float:
|
|
2125
2172
|
"""Returns z Point3D of the center of the :class:`~.Mobject` as ``float``"""
|
|
2126
2173
|
return self.get_coord(2, direction)
|
|
2127
2174
|
|
|
@@ -2142,7 +2189,7 @@ class Mobject:
|
|
|
2142
2189
|
def point_from_proportion(self, alpha: float) -> Point3D:
|
|
2143
2190
|
raise NotImplementedError("Please override in a child class.")
|
|
2144
2191
|
|
|
2145
|
-
def proportion_from_point(self, point:
|
|
2192
|
+
def proportion_from_point(self, point: Point3DLike) -> float:
|
|
2146
2193
|
raise NotImplementedError("Please override in a child class.")
|
|
2147
2194
|
|
|
2148
2195
|
def get_pieces(self, n_pieces: float) -> Group:
|
|
@@ -2215,7 +2262,7 @@ class Mobject:
|
|
|
2215
2262
|
|
|
2216
2263
|
def align_to(
|
|
2217
2264
|
self,
|
|
2218
|
-
mobject_or_point: Mobject |
|
|
2265
|
+
mobject_or_point: Mobject | Point3DLike,
|
|
2219
2266
|
direction: Vector3D = ORIGIN,
|
|
2220
2267
|
) -> Self:
|
|
2221
2268
|
"""Aligns mobject to another :class:`~.Mobject` in a certain direction.
|
|
@@ -2437,10 +2484,10 @@ class Mobject:
|
|
|
2437
2484
|
buff_x = buff_y = buff
|
|
2438
2485
|
|
|
2439
2486
|
# Initialize alignments correctly
|
|
2440
|
-
def init_alignments(alignments, num, mapping, name,
|
|
2487
|
+
def init_alignments(alignments, num, mapping, name, dir_):
|
|
2441
2488
|
if alignments is None:
|
|
2442
2489
|
# Use cell_alignment as fallback
|
|
2443
|
-
return [cell_alignment *
|
|
2490
|
+
return [cell_alignment * dir_] * num
|
|
2444
2491
|
if len(alignments) != num:
|
|
2445
2492
|
raise ValueError(f"{name}_alignments has a mismatching size.")
|
|
2446
2493
|
alignments = list(alignments)
|
|
@@ -2542,13 +2589,13 @@ class Mobject:
|
|
|
2542
2589
|
|
|
2543
2590
|
def sort(
|
|
2544
2591
|
self,
|
|
2545
|
-
point_to_num_func: Callable[[
|
|
2546
|
-
submob_func: Callable[[Mobject],
|
|
2592
|
+
point_to_num_func: Callable[[Point3DLike], float] = lambda p: p[0],
|
|
2593
|
+
submob_func: Callable[[Mobject], Any] | None = None,
|
|
2547
2594
|
) -> Self:
|
|
2548
2595
|
"""Sorts the list of :attr:`submobjects` by a function defined by ``submob_func``."""
|
|
2549
2596
|
if submob_func is None:
|
|
2550
2597
|
|
|
2551
|
-
def submob_func(m: Mobject):
|
|
2598
|
+
def submob_func(m: Mobject) -> float:
|
|
2552
2599
|
return point_to_num_func(m.get_center())
|
|
2553
2600
|
|
|
2554
2601
|
self.submobjects.sort(key=submob_func)
|
|
@@ -2835,7 +2882,7 @@ class Mobject:
|
|
|
2835
2882
|
|
|
2836
2883
|
>>> result = rect.copy().become(circ, stretch=True)
|
|
2837
2884
|
>>> result.height, result.width
|
|
2838
|
-
(2.0, 4.0)
|
|
2885
|
+
(np.float64(2.0), np.float64(4.0))
|
|
2839
2886
|
>>> ellipse_points = np.array(result.get_anchors())
|
|
2840
2887
|
>>> ellipse_eq = np.sum(ellipse_points**2 * [1/4, 1, 0], axis=1)
|
|
2841
2888
|
>>> np.allclose(ellipse_eq, 1)
|
|
@@ -2849,14 +2896,14 @@ class Mobject:
|
|
|
2849
2896
|
|
|
2850
2897
|
>>> result = rect.copy().become(circ, match_height=True)
|
|
2851
2898
|
>>> result.height, result.width
|
|
2852
|
-
(2.0, 2.0)
|
|
2899
|
+
(np.float64(2.0), np.float64(2.0))
|
|
2853
2900
|
>>> circle_points = np.array(result.get_anchors())
|
|
2854
2901
|
>>> circle_eq = np.sum(circle_points**2, axis=1)
|
|
2855
2902
|
>>> np.allclose(circle_eq, 1)
|
|
2856
2903
|
True
|
|
2857
2904
|
>>> result = rect.copy().become(circ, match_width=True)
|
|
2858
2905
|
>>> result.height, result.width
|
|
2859
|
-
(4.0, 4.0)
|
|
2906
|
+
(np.float64(4.0), np.float64(4.0))
|
|
2860
2907
|
>>> circle_points = np.array(result.get_anchors())
|
|
2861
2908
|
>>> circle_eq = np.sum(circle_points**2, axis=1)
|
|
2862
2909
|
>>> np.allclose(circle_eq, 2**2)
|
|
@@ -3025,8 +3072,7 @@ class _AnimationBuilder:
|
|
|
3025
3072
|
|
|
3026
3073
|
if (self.is_chaining and has_overridden_animation) or self.overridden_animation:
|
|
3027
3074
|
raise NotImplementedError(
|
|
3028
|
-
"Method chaining is currently not supported for "
|
|
3029
|
-
"overridden animations",
|
|
3075
|
+
"Method chaining is currently not supported for overridden animations",
|
|
3030
3076
|
)
|
|
3031
3077
|
|
|
3032
3078
|
def update_target(*method_args, **method_kwargs):
|
|
@@ -187,7 +187,8 @@ class OpenGLTipableVMobject(OpenGLVMobject):
|
|
|
187
187
|
|
|
188
188
|
def get_tip(self):
|
|
189
189
|
"""Returns the TipableVMobject instance's (first) tip,
|
|
190
|
-
otherwise throws an exception.
|
|
190
|
+
otherwise throws an exception.
|
|
191
|
+
"""
|
|
191
192
|
tips = self.get_tips()
|
|
192
193
|
if len(tips) == 0:
|
|
193
194
|
raise Exception("tip not found")
|
|
@@ -463,10 +464,7 @@ class OpenGLLine(OpenGLTipableVMobject):
|
|
|
463
464
|
if buff == 0:
|
|
464
465
|
return
|
|
465
466
|
#
|
|
466
|
-
if self.path_arc == 0
|
|
467
|
-
length = self.get_length()
|
|
468
|
-
else:
|
|
469
|
-
length = self.get_arc_length()
|
|
467
|
+
length = self.get_length() if self.path_arc == 0 else self.get_arc_length()
|
|
470
468
|
#
|
|
471
469
|
if length < 2 * buff:
|
|
472
470
|
return
|
|
@@ -519,9 +517,7 @@ class OpenGLLine(OpenGLTipableVMobject):
|
|
|
519
517
|
return angle_of_vector(self.get_vector())
|
|
520
518
|
|
|
521
519
|
def get_projection(self, point):
|
|
522
|
-
"""
|
|
523
|
-
Return projection of a point onto the line
|
|
524
|
-
"""
|
|
520
|
+
"""Return projection of a point onto the line"""
|
|
525
521
|
unit_vect = self.get_unit_vector()
|
|
526
522
|
start = self.get_start()
|
|
527
523
|
return start + np.dot(point - start, unit_vect) * unit_vect
|