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