manim 0.17.0__py3-none-any.whl → 0.19.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- manim/__init__.py +11 -6
- manim/__main__.py +62 -19
- manim/_config/__init__.py +10 -9
- manim/_config/cli_colors.py +26 -9
- manim/_config/default.cfg +1 -3
- manim/_config/logger_utils.py +23 -13
- manim/_config/utils.py +662 -468
- manim/animation/animation.py +164 -18
- manim/animation/changing.py +34 -23
- manim/animation/composition.py +265 -67
- manim/animation/creation.py +208 -26
- manim/animation/fading.py +16 -18
- manim/animation/growing.py +35 -15
- manim/animation/indication.py +150 -76
- manim/animation/movement.py +56 -22
- manim/animation/numbers.py +64 -6
- manim/animation/rotation.py +78 -7
- manim/animation/specialized.py +6 -7
- manim/animation/speedmodifier.py +13 -10
- manim/animation/transform.py +14 -11
- manim/animation/transform_matching_parts.py +3 -4
- manim/animation/updaters/mobject_update_utils.py +152 -30
- manim/animation/updaters/update.py +10 -7
- manim/camera/camera.py +182 -118
- manim/camera/mapping_camera.py +34 -3
- manim/camera/moving_camera.py +95 -74
- manim/camera/multi_camera.py +23 -15
- manim/camera/three_d_camera.py +70 -52
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +76 -44
- manim/cli/checkhealth/checks.py +192 -0
- manim/cli/checkhealth/commands.py +90 -0
- manim/cli/default_group.py +158 -25
- manim/cli/init/commands.py +33 -25
- manim/cli/plugins/commands.py +16 -3
- manim/cli/render/commands.py +72 -60
- manim/cli/render/ease_of_access_options.py +4 -3
- manim/cli/render/global_options.py +59 -17
- manim/cli/render/output_options.py +6 -5
- manim/cli/render/render_options.py +98 -33
- manim/constants.py +109 -59
- manim/data_structures.py +31 -0
- manim/mobject/frame.py +8 -5
- manim/mobject/geometry/__init__.py +1 -0
- manim/mobject/geometry/arc.py +277 -135
- manim/mobject/geometry/boolean_ops.py +32 -31
- manim/mobject/geometry/labeled.py +376 -0
- manim/mobject/geometry/line.py +192 -87
- manim/mobject/geometry/polygram.py +224 -58
- manim/mobject/geometry/shape_matchers.py +61 -25
- manim/mobject/geometry/tips.py +122 -48
- manim/mobject/graph.py +1027 -419
- manim/mobject/graphing/coordinate_systems.py +533 -278
- manim/mobject/graphing/functions.py +53 -32
- manim/mobject/graphing/number_line.py +123 -65
- manim/mobject/graphing/probability.py +88 -62
- manim/mobject/graphing/scale.py +33 -19
- manim/mobject/logo.py +118 -28
- manim/mobject/matrix.py +87 -83
- manim/mobject/mobject.py +912 -442
- manim/mobject/opengl/dot_cloud.py +16 -5
- manim/mobject/opengl/opengl_compatibility.py +4 -2
- manim/mobject/opengl/opengl_geometry.py +254 -153
- manim/mobject/opengl/opengl_image_mobject.py +3 -1
- manim/mobject/opengl/opengl_mobject.py +779 -482
- manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
- manim/mobject/opengl/opengl_surface.py +14 -92
- manim/mobject/opengl/opengl_three_dimensions.py +12 -8
- manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
- manim/mobject/svg/brace.py +173 -41
- manim/mobject/svg/svg_mobject.py +139 -53
- manim/mobject/table.py +61 -68
- manim/mobject/text/code_mobject.py +193 -539
- manim/mobject/text/numbers.py +81 -34
- manim/mobject/text/tex_mobject.py +130 -78
- manim/mobject/text/text_mobject.py +288 -164
- manim/mobject/three_d/polyhedra.py +111 -13
- manim/mobject/three_d/three_d_utils.py +17 -8
- manim/mobject/three_d/three_dimensions.py +239 -106
- manim/mobject/types/image_mobject.py +50 -30
- manim/mobject/types/point_cloud_mobject.py +120 -75
- manim/mobject/types/vectorized_mobject.py +841 -408
- manim/mobject/value_tracker.py +105 -38
- manim/mobject/vector_field.py +50 -31
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +14 -1
- manim/plugins/plugins_flags.py +10 -14
- manim/renderer/cairo_renderer.py +65 -50
- manim/renderer/opengl_renderer.py +89 -69
- manim/renderer/opengl_renderer_window.py +39 -18
- manim/renderer/shader.py +123 -87
- manim/renderer/shader_wrapper.py +44 -28
- manim/renderer/vectorized_mobject_rendering.py +38 -10
- manim/scene/moving_camera_scene.py +32 -3
- manim/scene/scene.py +507 -242
- manim/scene/scene_file_writer.py +371 -220
- manim/scene/section.py +20 -16
- manim/scene/three_d_scene.py +14 -22
- manim/scene/vector_space_scene.py +223 -129
- manim/scene/zoomed_scene.py +46 -41
- manim/typing.py +990 -0
- manim/utils/bezier.py +1823 -371
- manim/utils/caching.py +12 -5
- manim/utils/color/AS2700.py +236 -0
- manim/utils/color/BS381.py +318 -0
- manim/utils/color/DVIPSNAMES.py +96 -0
- manim/utils/color/SVGNAMES.py +179 -0
- manim/utils/color/X11.py +533 -0
- manim/utils/color/XKCD.py +952 -0
- manim/utils/color/__init__.py +61 -0
- manim/utils/color/core.py +1667 -0
- manim/utils/color/manim_colors.py +218 -0
- manim/utils/commands.py +48 -20
- manim/utils/config_ops.py +39 -19
- manim/utils/debug.py +8 -7
- manim/utils/deprecation.py +86 -39
- manim/utils/docbuild/__init__.py +17 -0
- manim/utils/docbuild/autoaliasattr_directive.py +236 -0
- manim/utils/docbuild/autocolor_directive.py +99 -0
- manim/utils/docbuild/manim_directive.py +94 -41
- manim/utils/docbuild/module_parsing.py +245 -0
- manim/utils/exceptions.py +6 -0
- manim/utils/family.py +5 -3
- manim/utils/family_ops.py +17 -4
- manim/utils/file_ops.py +27 -17
- manim/utils/hashing.py +55 -45
- manim/utils/images.py +13 -7
- manim/utils/ipython_magic.py +13 -7
- manim/utils/iterables.py +163 -120
- manim/utils/module_ops.py +66 -24
- manim/utils/opengl.py +77 -24
- manim/utils/parameter_parsing.py +32 -0
- manim/utils/paths.py +30 -33
- manim/utils/polylabel.py +235 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +98 -32
- manim/utils/simple_functions.py +25 -33
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +188 -115
- manim/utils/testing/__init__.py +17 -0
- manim/utils/testing/_frames_testers.py +13 -8
- manim/utils/testing/_show_diff.py +5 -3
- manim/utils/testing/_test_class_makers.py +34 -18
- manim/utils/testing/frames_comparison.py +37 -19
- manim/utils/tex.py +130 -198
- manim/utils/tex_file_writing.py +77 -47
- manim/utils/tex_templates.py +2 -1
- manim/utils/unit.py +6 -5
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
- manim-0.19.1.dist-info/RECORD +220 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
- manim-0.19.1.dist-info/entry_points.txt +3 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
- manim/cli/new/group.py +0 -189
- manim/communitycolors.py +0 -9
- manim/gui/__init__.py +0 -0
- manim/gui/gui.py +0 -82
- manim/plugins/import_plugins.py +0 -43
- manim/utils/color.py +0 -552
- manim-0.17.0.dist-info/RECORD +0 -206
- manim-0.17.0.dist-info/entry_points.txt +0 -4
- /manim/cli/{new → checkhealth}/__init__.py +0 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
manim/utils/iterables.py
CHANGED
|
@@ -19,12 +19,30 @@ __all__ = [
|
|
|
19
19
|
]
|
|
20
20
|
|
|
21
21
|
import itertools as it
|
|
22
|
-
from
|
|
22
|
+
from collections.abc import (
|
|
23
|
+
Callable,
|
|
24
|
+
Collection,
|
|
25
|
+
Generator,
|
|
26
|
+
Hashable,
|
|
27
|
+
Iterable,
|
|
28
|
+
Reversible,
|
|
29
|
+
Sequence,
|
|
30
|
+
)
|
|
31
|
+
from typing import TYPE_CHECKING, TypeVar, overload
|
|
23
32
|
|
|
24
33
|
import numpy as np
|
|
25
34
|
|
|
35
|
+
T = TypeVar("T")
|
|
36
|
+
U = TypeVar("U")
|
|
37
|
+
F = TypeVar("F", np.float64, np.int_)
|
|
38
|
+
H = TypeVar("H", bound=Hashable)
|
|
26
39
|
|
|
27
|
-
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
import numpy.typing as npt
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def adjacent_n_tuples(objects: Sequence[T], n: int) -> zip[tuple[T, ...]]:
|
|
28
46
|
"""Returns the Sequence objects cyclically split into n length tuples.
|
|
29
47
|
|
|
30
48
|
See Also
|
|
@@ -33,18 +51,17 @@ def adjacent_n_tuples(objects: Sequence, n: int) -> zip:
|
|
|
33
51
|
|
|
34
52
|
Examples
|
|
35
53
|
--------
|
|
36
|
-
|
|
54
|
+
.. code-block:: pycon
|
|
37
55
|
|
|
38
|
-
list(adjacent_n_tuples([1, 2, 3, 4], 2))
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# returns [(1, 2, 3), (2, 3, 4), (3, 4, 1), (4, 1, 2)]
|
|
56
|
+
>>> list(adjacent_n_tuples([1, 2, 3, 4], 2))
|
|
57
|
+
[(1, 2), (2, 3), (3, 4), (4, 1)]
|
|
58
|
+
>>> list(adjacent_n_tuples([1, 2, 3, 4], 3))
|
|
59
|
+
[(1, 2, 3), (2, 3, 4), (3, 4, 1), (4, 1, 2)]
|
|
43
60
|
"""
|
|
44
61
|
return zip(*([*objects[k:], *objects[:k]] for k in range(n)))
|
|
45
62
|
|
|
46
63
|
|
|
47
|
-
def adjacent_pairs(objects: Sequence) -> zip:
|
|
64
|
+
def adjacent_pairs(objects: Sequence[T]) -> zip[tuple[T, ...]]:
|
|
48
65
|
"""Alias for ``adjacent_n_tuples(objects, 2)``.
|
|
49
66
|
|
|
50
67
|
See Also
|
|
@@ -53,24 +70,24 @@ def adjacent_pairs(objects: Sequence) -> zip:
|
|
|
53
70
|
|
|
54
71
|
Examples
|
|
55
72
|
--------
|
|
56
|
-
|
|
73
|
+
.. code-block:: pycon
|
|
57
74
|
|
|
58
|
-
list(adjacent_pairs([1, 2, 3, 4]))
|
|
59
|
-
|
|
75
|
+
>>> list(adjacent_pairs([1, 2, 3, 4]))
|
|
76
|
+
[(1, 2), (2, 3), (3, 4), (4, 1)]
|
|
60
77
|
"""
|
|
61
78
|
return adjacent_n_tuples(objects, 2)
|
|
62
79
|
|
|
63
80
|
|
|
64
|
-
def all_elements_are_instances(iterable: Iterable, Class) -> bool:
|
|
81
|
+
def all_elements_are_instances(iterable: Iterable[object], Class: type[object]) -> bool:
|
|
65
82
|
"""Returns ``True`` if all elements of iterable are instances of Class.
|
|
66
83
|
False otherwise.
|
|
67
84
|
"""
|
|
68
|
-
return all(
|
|
85
|
+
return all(isinstance(e, Class) for e in iterable)
|
|
69
86
|
|
|
70
87
|
|
|
71
88
|
def batch_by_property(
|
|
72
|
-
items:
|
|
73
|
-
) -> list[tuple[list,
|
|
89
|
+
items: Iterable[T], property_func: Callable[[T], U]
|
|
90
|
+
) -> list[tuple[list[T], U | None]]:
|
|
74
91
|
"""Takes in a Sequence, and returns a list of tuples, (batch, prop)
|
|
75
92
|
such that all items in a batch have the same output when
|
|
76
93
|
put into the Callable property_func, and such that chaining all these
|
|
@@ -79,13 +96,13 @@ def batch_by_property(
|
|
|
79
96
|
|
|
80
97
|
Examples
|
|
81
98
|
--------
|
|
82
|
-
|
|
99
|
+
.. code-block:: pycon
|
|
83
100
|
|
|
84
|
-
batch_by_property([(1, 2), (3, 4), (5, 6, 7), (8, 9)], len)
|
|
85
|
-
|
|
101
|
+
>>> batch_by_property([(1, 2), (3, 4), (5, 6, 7), (8, 9)], len)
|
|
102
|
+
[([(1, 2), (3, 4)], 2), ([(5, 6, 7)], 3), ([(8, 9)], 2)]
|
|
86
103
|
"""
|
|
87
|
-
batch_prop_pairs = []
|
|
88
|
-
curr_batch = []
|
|
104
|
+
batch_prop_pairs: list[tuple[list[T], U | None]] = []
|
|
105
|
+
curr_batch: list[T] = []
|
|
89
106
|
curr_prop = None
|
|
90
107
|
for item in items:
|
|
91
108
|
prop = property_func(item)
|
|
@@ -103,67 +120,84 @@ def batch_by_property(
|
|
|
103
120
|
return batch_prop_pairs
|
|
104
121
|
|
|
105
122
|
|
|
106
|
-
def concatenate_lists(*list_of_lists: Iterable) -> list:
|
|
123
|
+
def concatenate_lists(*list_of_lists: Iterable[T]) -> list[T]:
|
|
107
124
|
"""Combines the Iterables provided as arguments into one list.
|
|
108
125
|
|
|
109
126
|
Examples
|
|
110
127
|
--------
|
|
111
|
-
|
|
128
|
+
.. code-block:: pycon
|
|
112
129
|
|
|
113
|
-
concatenate_lists([1, 2], [3, 4], [5])
|
|
114
|
-
|
|
130
|
+
>>> concatenate_lists([1, 2], [3, 4], [5])
|
|
131
|
+
[1, 2, 3, 4, 5]
|
|
115
132
|
"""
|
|
116
133
|
return [item for lst in list_of_lists for item in lst]
|
|
117
134
|
|
|
118
135
|
|
|
119
|
-
def list_difference_update(l1: Iterable, l2: Iterable) -> list:
|
|
136
|
+
def list_difference_update(l1: Iterable[T], l2: Iterable[T]) -> list[T]:
|
|
120
137
|
"""Returns a list containing all the elements of l1 not in l2.
|
|
121
138
|
|
|
122
139
|
Examples
|
|
123
140
|
--------
|
|
124
|
-
|
|
141
|
+
.. code-block:: pycon
|
|
125
142
|
|
|
126
|
-
list_difference_update([1, 2, 3, 4], [2, 4])
|
|
127
|
-
|
|
143
|
+
>>> list_difference_update([1, 2, 3, 4], [2, 4])
|
|
144
|
+
[1, 3]
|
|
128
145
|
"""
|
|
129
146
|
return [e for e in l1 if e not in l2]
|
|
130
147
|
|
|
131
148
|
|
|
132
|
-
def list_update(l1: Iterable, l2: Iterable) -> list:
|
|
149
|
+
def list_update(l1: Iterable[T], l2: Iterable[T]) -> list[T]:
|
|
133
150
|
"""Used instead of ``set.update()`` to maintain order,
|
|
134
151
|
making sure duplicates are removed from l1, not l2.
|
|
135
152
|
Removes overlap of l1 and l2 and then concatenates l2 unchanged.
|
|
136
153
|
|
|
137
154
|
Examples
|
|
138
155
|
--------
|
|
139
|
-
|
|
156
|
+
.. code-block:: pycon
|
|
140
157
|
|
|
141
|
-
list_update([1, 2, 3], [2, 4, 4])
|
|
142
|
-
|
|
158
|
+
>>> list_update([1, 2, 3], [2, 4, 4])
|
|
159
|
+
[1, 3, 2, 4, 4]
|
|
143
160
|
"""
|
|
144
161
|
return [e for e in l1 if e not in l2] + list(l2)
|
|
145
162
|
|
|
146
163
|
|
|
147
|
-
|
|
164
|
+
@overload
|
|
165
|
+
def listify(obj: str) -> list[str]: ...
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@overload
|
|
169
|
+
def listify(obj: Iterable[T]) -> list[T]: ...
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@overload
|
|
173
|
+
def listify(obj: T) -> list[T]: ...
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def listify(obj: str | Iterable[T] | T) -> list[str] | list[T]:
|
|
148
177
|
"""Converts obj to a list intelligently.
|
|
149
178
|
|
|
150
179
|
Examples
|
|
151
180
|
--------
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
listify(
|
|
155
|
-
|
|
156
|
-
listify(
|
|
181
|
+
.. code-block:: pycon
|
|
182
|
+
|
|
183
|
+
>>> listify("str")
|
|
184
|
+
['str']
|
|
185
|
+
>>> listify((1, 2))
|
|
186
|
+
[1, 2]
|
|
187
|
+
>>> listify(len)
|
|
188
|
+
[<built-in function len>]
|
|
157
189
|
"""
|
|
158
190
|
if isinstance(obj, str):
|
|
159
191
|
return [obj]
|
|
160
|
-
|
|
192
|
+
if isinstance(obj, Iterable):
|
|
161
193
|
return list(obj)
|
|
162
|
-
|
|
194
|
+
else:
|
|
163
195
|
return [obj]
|
|
164
196
|
|
|
165
197
|
|
|
166
|
-
def make_even(
|
|
198
|
+
def make_even(
|
|
199
|
+
iterable_1: Iterable[T], iterable_2: Iterable[U]
|
|
200
|
+
) -> tuple[list[T], list[U]]:
|
|
167
201
|
"""Extends the shorter of the two iterables with duplicate values until its
|
|
168
202
|
length is equal to the longer iterable (favours earlier elements).
|
|
169
203
|
|
|
@@ -173,13 +207,13 @@ def make_even(iterable_1: Iterable, iterable_2: Iterable) -> tuple[list, list]:
|
|
|
173
207
|
|
|
174
208
|
Examples
|
|
175
209
|
--------
|
|
176
|
-
|
|
210
|
+
.. code-block:: pycon
|
|
177
211
|
|
|
178
|
-
make_even([1, 2], [3, 4, 5, 6])
|
|
212
|
+
>>> make_even([1, 2], [3, 4, 5, 6])
|
|
179
213
|
([1, 1, 2, 2], [3, 4, 5, 6])
|
|
180
214
|
|
|
181
|
-
make_even([1, 2], [3, 4, 5, 6, 7])
|
|
182
|
-
|
|
215
|
+
>>> make_even([1, 2], [3, 4, 5, 6, 7])
|
|
216
|
+
([1, 1, 1, 2, 2], [3, 4, 5, 6, 7])
|
|
183
217
|
"""
|
|
184
218
|
list_1, list_2 = list(iterable_1), list(iterable_2)
|
|
185
219
|
len_list_1 = len(list_1)
|
|
@@ -192,8 +226,8 @@ def make_even(iterable_1: Iterable, iterable_2: Iterable) -> tuple[list, list]:
|
|
|
192
226
|
|
|
193
227
|
|
|
194
228
|
def make_even_by_cycling(
|
|
195
|
-
iterable_1: Collection, iterable_2: Collection
|
|
196
|
-
) -> tuple[list, list]:
|
|
229
|
+
iterable_1: Collection[T], iterable_2: Collection[U]
|
|
230
|
+
) -> tuple[list[T], list[U]]:
|
|
197
231
|
"""Extends the shorter of the two iterables with duplicate values until its
|
|
198
232
|
length is equal to the longer iterable (cycles over shorter iterable).
|
|
199
233
|
|
|
@@ -203,13 +237,13 @@ def make_even_by_cycling(
|
|
|
203
237
|
|
|
204
238
|
Examples
|
|
205
239
|
--------
|
|
206
|
-
|
|
240
|
+
.. code-block:: pycon
|
|
207
241
|
|
|
208
|
-
make_even_by_cycling([1, 2], [3, 4, 5, 6])
|
|
242
|
+
>>> make_even_by_cycling([1, 2], [3, 4, 5, 6])
|
|
209
243
|
([1, 2, 1, 2], [3, 4, 5, 6])
|
|
210
244
|
|
|
211
|
-
make_even_by_cycling([1, 2], [3, 4, 5, 6, 7])
|
|
212
|
-
|
|
245
|
+
>>> make_even_by_cycling([1, 2], [3, 4, 5, 6, 7])
|
|
246
|
+
([1, 2, 1, 2, 1], [3, 4, 5, 6, 7])
|
|
213
247
|
"""
|
|
214
248
|
length = max(len(iterable_1), len(iterable_2))
|
|
215
249
|
cycle1 = it.cycle(iterable_1)
|
|
@@ -220,7 +254,7 @@ def make_even_by_cycling(
|
|
|
220
254
|
)
|
|
221
255
|
|
|
222
256
|
|
|
223
|
-
def remove_list_redundancies(lst: Reversible) -> list:
|
|
257
|
+
def remove_list_redundancies(lst: Reversible[H]) -> list[H]:
|
|
224
258
|
"""Used instead of ``list(set(l))`` to maintain order.
|
|
225
259
|
Keeps the last occurrence of each element.
|
|
226
260
|
"""
|
|
@@ -234,21 +268,21 @@ def remove_list_redundancies(lst: Reversible) -> list:
|
|
|
234
268
|
return reversed_result
|
|
235
269
|
|
|
236
270
|
|
|
237
|
-
def remove_nones(sequence: Iterable) -> list:
|
|
271
|
+
def remove_nones(sequence: Iterable[T | None]) -> list[T]:
|
|
238
272
|
"""Removes elements where bool(x) evaluates to False.
|
|
239
273
|
|
|
240
274
|
Examples
|
|
241
275
|
--------
|
|
242
|
-
|
|
276
|
+
.. code-block:: pycon
|
|
243
277
|
|
|
244
|
-
remove_nones([
|
|
245
|
-
|
|
278
|
+
>>> remove_nones(["m", "", "l", 0, 42, False, True])
|
|
279
|
+
['m', 'l', 42, True]
|
|
246
280
|
"""
|
|
247
281
|
# Note this is redundant with it.chain
|
|
248
282
|
return [x for x in sequence if x]
|
|
249
283
|
|
|
250
284
|
|
|
251
|
-
def resize_array(nparray:
|
|
285
|
+
def resize_array(nparray: npt.NDArray[F], length: int) -> npt.NDArray[F]:
|
|
252
286
|
"""Extends/truncates nparray so that ``len(result) == length``.
|
|
253
287
|
The elements of nparray are cycled to achieve the desired length.
|
|
254
288
|
|
|
@@ -259,7 +293,7 @@ def resize_array(nparray: np.ndarray, length: int) -> np.ndarray:
|
|
|
259
293
|
|
|
260
294
|
Examples
|
|
261
295
|
--------
|
|
262
|
-
|
|
296
|
+
.. code-block:: pycon
|
|
263
297
|
|
|
264
298
|
>>> points = np.array([[1, 2], [3, 4]])
|
|
265
299
|
>>> resize_array(points, 1)
|
|
@@ -277,7 +311,9 @@ def resize_array(nparray: np.ndarray, length: int) -> np.ndarray:
|
|
|
277
311
|
return np.resize(nparray, (length, *nparray.shape[1:]))
|
|
278
312
|
|
|
279
313
|
|
|
280
|
-
def resize_preserving_order(
|
|
314
|
+
def resize_preserving_order(
|
|
315
|
+
nparray: npt.NDArray[np.float64], length: int
|
|
316
|
+
) -> npt.NDArray[np.float64]:
|
|
281
317
|
"""Extends/truncates nparray so that ``len(result) == length``.
|
|
282
318
|
The elements of nparray are duplicated to achieve the desired length
|
|
283
319
|
(favours earlier elements).
|
|
@@ -291,21 +327,19 @@ def resize_preserving_order(nparray: np.ndarray, length: int) -> np.ndarray:
|
|
|
291
327
|
|
|
292
328
|
Examples
|
|
293
329
|
--------
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
resize_preserving_order(np.array([]), 5)
|
|
297
|
-
# np.array([0., 0., 0., 0., 0.])
|
|
330
|
+
.. code-block:: pycon
|
|
298
331
|
|
|
299
|
-
|
|
300
|
-
|
|
332
|
+
>>> resize_preserving_order(np.array([]), 5)
|
|
333
|
+
array([0., 0., 0., 0., 0.])
|
|
301
334
|
|
|
302
|
-
|
|
303
|
-
|
|
335
|
+
>>> nparray = np.array([[1, 2], [3, 4]])
|
|
336
|
+
>>> resize_preserving_order(nparray, 1)
|
|
337
|
+
array([[1, 2]])
|
|
304
338
|
|
|
305
|
-
resize_preserving_order(nparray, 3)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
339
|
+
>>> resize_preserving_order(nparray, 3)
|
|
340
|
+
array([[1, 2],
|
|
341
|
+
[1, 2],
|
|
342
|
+
[3, 4]])
|
|
309
343
|
"""
|
|
310
344
|
if len(nparray) == 0:
|
|
311
345
|
return np.zeros((length, *nparray.shape[1:]))
|
|
@@ -315,7 +349,7 @@ def resize_preserving_order(nparray: np.ndarray, length: int) -> np.ndarray:
|
|
|
315
349
|
return nparray[indices]
|
|
316
350
|
|
|
317
351
|
|
|
318
|
-
def resize_with_interpolation(nparray:
|
|
352
|
+
def resize_with_interpolation(nparray: npt.NDArray[F], length: int) -> npt.NDArray[F]:
|
|
319
353
|
"""Extends/truncates nparray so that ``len(result) == length``.
|
|
320
354
|
New elements are interpolated to achieve the desired length.
|
|
321
355
|
|
|
@@ -329,39 +363,29 @@ def resize_with_interpolation(nparray: np.ndarray, length: int) -> np.ndarray:
|
|
|
329
363
|
|
|
330
364
|
Examples
|
|
331
365
|
--------
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
nparray = np.array([[1, 2],
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
resize_with_interpolation(nparray,
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
# [2.33333333, 3.33333333],
|
|
356
|
-
# [3.66666667, 4.66666667],
|
|
357
|
-
# [5. , 6. ]])
|
|
358
|
-
|
|
359
|
-
nparray = np.array([[1, 2], [3, 4], [1, 2]])
|
|
360
|
-
resize_with_interpolation(nparray, 4)
|
|
361
|
-
# np.array([[1. , 2. ],
|
|
362
|
-
# [2.33333333, 3.33333333],
|
|
363
|
-
# [2.33333333, 3.33333333],
|
|
364
|
-
# [1. , 2. ]])
|
|
366
|
+
.. code-block:: pycon
|
|
367
|
+
|
|
368
|
+
>>> nparray = np.array([[1, 2], [3, 4]])
|
|
369
|
+
>>> resize_with_interpolation(nparray, 1)
|
|
370
|
+
array([[1., 2.]])
|
|
371
|
+
>>> resize_with_interpolation(nparray, 4)
|
|
372
|
+
array([[1. , 2. ],
|
|
373
|
+
[1.66666667, 2.66666667],
|
|
374
|
+
[2.33333333, 3.33333333],
|
|
375
|
+
[3. , 4. ]])
|
|
376
|
+
>>> nparray = np.array([[[1, 2], [3, 4]]])
|
|
377
|
+
>>> nparray = np.array([[1, 2], [3, 4], [5, 6]])
|
|
378
|
+
>>> resize_with_interpolation(nparray, 4)
|
|
379
|
+
array([[1. , 2. ],
|
|
380
|
+
[2.33333333, 3.33333333],
|
|
381
|
+
[3.66666667, 4.66666667],
|
|
382
|
+
[5. , 6. ]])
|
|
383
|
+
>>> nparray = np.array([[1, 2], [3, 4], [1, 2]])
|
|
384
|
+
>>> resize_with_interpolation(nparray, 4)
|
|
385
|
+
array([[1. , 2. ],
|
|
386
|
+
[2.33333333, 3.33333333],
|
|
387
|
+
[2.33333333, 3.33333333],
|
|
388
|
+
[1. , 2. ]])
|
|
365
389
|
"""
|
|
366
390
|
if len(nparray) == length:
|
|
367
391
|
return nparray
|
|
@@ -375,7 +399,7 @@ def resize_with_interpolation(nparray: np.ndarray, length: int) -> np.ndarray:
|
|
|
375
399
|
)
|
|
376
400
|
|
|
377
401
|
|
|
378
|
-
def stretch_array_to_length(nparray:
|
|
402
|
+
def stretch_array_to_length(nparray: npt.NDArray[F], length: int) -> npt.NDArray[F]:
|
|
379
403
|
# todo: is this the same as resize_preserving_order()?
|
|
380
404
|
curr_len = len(nparray)
|
|
381
405
|
if curr_len > length:
|
|
@@ -385,35 +409,54 @@ def stretch_array_to_length(nparray: np.ndarray, length: int) -> np.ndarray:
|
|
|
385
409
|
return nparray[indices.astype(int)]
|
|
386
410
|
|
|
387
411
|
|
|
388
|
-
|
|
412
|
+
@overload
|
|
413
|
+
def tuplify(obj: str) -> tuple[str]: ...
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
@overload
|
|
417
|
+
def tuplify(obj: Iterable[T]) -> tuple[T]: ...
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
@overload
|
|
421
|
+
def tuplify(obj: T) -> tuple[T]: ...
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def tuplify(obj: str | Iterable[T] | T) -> tuple[str] | tuple[T]:
|
|
389
425
|
"""Converts obj to a tuple intelligently.
|
|
390
426
|
|
|
391
427
|
Examples
|
|
392
428
|
--------
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
tuplify(
|
|
396
|
-
|
|
397
|
-
tuplify(
|
|
429
|
+
.. code-block:: pycon
|
|
430
|
+
|
|
431
|
+
>>> tuplify("str")
|
|
432
|
+
('str',)
|
|
433
|
+
>>> tuplify([1, 2])
|
|
434
|
+
(1, 2)
|
|
435
|
+
>>> tuplify(len)
|
|
436
|
+
(<built-in function len>,)
|
|
398
437
|
"""
|
|
399
438
|
if isinstance(obj, str):
|
|
400
439
|
return (obj,)
|
|
401
|
-
|
|
440
|
+
if isinstance(obj, Iterable):
|
|
402
441
|
return tuple(obj)
|
|
403
|
-
|
|
442
|
+
else:
|
|
404
443
|
return (obj,)
|
|
405
444
|
|
|
406
445
|
|
|
407
|
-
def uniq_chain(*args: Iterable) -> Generator:
|
|
446
|
+
def uniq_chain(*args: Iterable[T]) -> Generator[T, None, None]:
|
|
408
447
|
"""Returns a generator that yields all unique elements of the Iterables
|
|
409
448
|
provided via args in the order provided.
|
|
410
449
|
|
|
411
450
|
Examples
|
|
412
451
|
--------
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
uniq_chain([1, 2], [2, 3], [1, 4, 4])
|
|
416
|
-
|
|
452
|
+
.. code-block:: pycon
|
|
453
|
+
|
|
454
|
+
>>> gen = uniq_chain([1, 2], [2, 3], [1, 4, 4])
|
|
455
|
+
>>> from collections.abc import Generator
|
|
456
|
+
>>> isinstance(gen, Generator)
|
|
457
|
+
True
|
|
458
|
+
>>> tuple(gen)
|
|
459
|
+
(1, 2, 3, 4)
|
|
417
460
|
"""
|
|
418
461
|
unique_items = set()
|
|
419
462
|
for x in it.chain(*args):
|
manim/utils/module_ops.py
CHANGED
|
@@ -2,18 +2,29 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import importlib.util
|
|
4
4
|
import inspect
|
|
5
|
-
import os
|
|
6
5
|
import re
|
|
7
6
|
import sys
|
|
8
7
|
import types
|
|
9
8
|
import warnings
|
|
10
9
|
from pathlib import Path
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Literal, overload
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
12
|
+
from manim._config import config, console, logger
|
|
13
|
+
from manim.constants import (
|
|
14
|
+
CHOOSE_NUMBER_MESSAGE,
|
|
15
|
+
INVALID_NUMBER_MESSAGE,
|
|
16
|
+
NO_SCENE_MESSAGE,
|
|
17
|
+
SCENE_NOT_FOUND_MESSAGE,
|
|
18
|
+
)
|
|
19
|
+
from manim.scene.scene_file_writer import SceneFileWriter
|
|
14
20
|
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from manim.scene.scene import Scene
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
__all__ = ["scene_classes_from_file"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_module(file_name: Path) -> types.ModuleType:
|
|
17
28
|
if str(file_name) == "-":
|
|
18
29
|
module = types.ModuleType("input_scenes")
|
|
19
30
|
logger.info(
|
|
@@ -46,19 +57,22 @@ def get_module(file_name: Path):
|
|
|
46
57
|
)
|
|
47
58
|
|
|
48
59
|
spec = importlib.util.spec_from_file_location(module_name, file_name)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
if isinstance(spec, importlib.machinery.ModuleSpec):
|
|
61
|
+
module = importlib.util.module_from_spec(spec)
|
|
62
|
+
sys.modules[module_name] = module
|
|
63
|
+
sys.path.insert(0, str(file_name.parent.absolute()))
|
|
64
|
+
assert spec.loader
|
|
65
|
+
spec.loader.exec_module(module)
|
|
66
|
+
return module
|
|
67
|
+
raise FileNotFoundError(f"{file_name} not found")
|
|
54
68
|
else:
|
|
55
69
|
raise FileNotFoundError(f"{file_name} not found")
|
|
56
70
|
|
|
57
71
|
|
|
58
|
-
def get_scene_classes_from_module(module):
|
|
72
|
+
def get_scene_classes_from_module(module: types.ModuleType) -> list[type[Scene]]:
|
|
59
73
|
from ..scene.scene import Scene
|
|
60
74
|
|
|
61
|
-
def is_child_scene(obj, module):
|
|
75
|
+
def is_child_scene(obj: Any, module: types.ModuleType) -> bool:
|
|
62
76
|
return (
|
|
63
77
|
inspect.isclass(obj)
|
|
64
78
|
and issubclass(obj, Scene)
|
|
@@ -72,9 +86,9 @@ def get_scene_classes_from_module(module):
|
|
|
72
86
|
]
|
|
73
87
|
|
|
74
88
|
|
|
75
|
-
def get_scenes_to_render(scene_classes):
|
|
89
|
+
def get_scenes_to_render(scene_classes: list[type[Scene]]) -> list[type[Scene]]:
|
|
76
90
|
if not scene_classes:
|
|
77
|
-
logger.error(
|
|
91
|
+
logger.error(NO_SCENE_MESSAGE)
|
|
78
92
|
return []
|
|
79
93
|
if config["write_all"]:
|
|
80
94
|
return scene_classes
|
|
@@ -87,7 +101,7 @@ def get_scenes_to_render(scene_classes):
|
|
|
87
101
|
found = True
|
|
88
102
|
break
|
|
89
103
|
if not found and (scene_name != ""):
|
|
90
|
-
logger.error(
|
|
104
|
+
logger.error(SCENE_NOT_FOUND_MESSAGE.format(scene_name))
|
|
91
105
|
if result:
|
|
92
106
|
return result
|
|
93
107
|
if len(scene_classes) == 1:
|
|
@@ -96,7 +110,7 @@ def get_scenes_to_render(scene_classes):
|
|
|
96
110
|
return prompt_user_for_choice(scene_classes)
|
|
97
111
|
|
|
98
112
|
|
|
99
|
-
def prompt_user_for_choice(scene_classes):
|
|
113
|
+
def prompt_user_for_choice(scene_classes: list[type[Scene]]) -> list[type[Scene]]:
|
|
100
114
|
num_to_class = {}
|
|
101
115
|
SceneFileWriter.force_output_as_scene_name = True
|
|
102
116
|
for count, scene_class in enumerate(scene_classes, 1):
|
|
@@ -105,16 +119,22 @@ def prompt_user_for_choice(scene_classes):
|
|
|
105
119
|
num_to_class[count] = scene_class
|
|
106
120
|
try:
|
|
107
121
|
user_input = console.input(
|
|
108
|
-
f"[log.message] {
|
|
122
|
+
f"[log.message] {CHOOSE_NUMBER_MESSAGE} [/log.message]",
|
|
109
123
|
)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
124
|
+
|
|
125
|
+
if user_input == "*":
|
|
126
|
+
selected_scenes_classes = scene_classes
|
|
127
|
+
else:
|
|
128
|
+
selected_scenes_classes = [
|
|
129
|
+
num_to_class[int(num_str)]
|
|
130
|
+
for num_str in re.split(r"\s*,\s*", user_input.strip())
|
|
131
|
+
]
|
|
132
|
+
config["scene_names"] = [
|
|
133
|
+
scene_class.__name__ for scene_class in selected_scenes_classes
|
|
113
134
|
]
|
|
114
|
-
|
|
115
|
-
return scene_classes
|
|
135
|
+
return selected_scenes_classes
|
|
116
136
|
except KeyError:
|
|
117
|
-
logger.error(
|
|
137
|
+
logger.error(INVALID_NUMBER_MESSAGE)
|
|
118
138
|
sys.exit(2)
|
|
119
139
|
except EOFError:
|
|
120
140
|
sys.exit(1)
|
|
@@ -123,9 +143,31 @@ def prompt_user_for_choice(scene_classes):
|
|
|
123
143
|
sys.exit(1)
|
|
124
144
|
|
|
125
145
|
|
|
146
|
+
@overload
|
|
147
|
+
def scene_classes_from_file(
|
|
148
|
+
file_path: Path, require_single_scene: bool, full_list: Literal[True]
|
|
149
|
+
) -> list[type[Scene]]: ...
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@overload
|
|
153
|
+
def scene_classes_from_file(
|
|
154
|
+
file_path: Path,
|
|
155
|
+
require_single_scene: Literal[True],
|
|
156
|
+
full_list: Literal[False] = False,
|
|
157
|
+
) -> type[Scene]: ...
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@overload
|
|
161
|
+
def scene_classes_from_file(
|
|
162
|
+
file_path: Path,
|
|
163
|
+
require_single_scene: Literal[False] = False,
|
|
164
|
+
full_list: Literal[False] = False,
|
|
165
|
+
) -> list[type[Scene]]: ...
|
|
166
|
+
|
|
167
|
+
|
|
126
168
|
def scene_classes_from_file(
|
|
127
|
-
file_path: Path, require_single_scene=False, full_list=False
|
|
128
|
-
):
|
|
169
|
+
file_path: Path, require_single_scene: bool = False, full_list: bool = False
|
|
170
|
+
) -> type[Scene] | list[type[Scene]]:
|
|
129
171
|
module = get_module(file_path)
|
|
130
172
|
all_scene_classes = get_scene_classes_from_module(module)
|
|
131
173
|
if full_list:
|