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/deprecation.py
CHANGED
|
@@ -6,15 +6,17 @@ __all__ = ["deprecated", "deprecated_params"]
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
import inspect
|
|
9
|
+
import logging
|
|
9
10
|
import re
|
|
10
|
-
from
|
|
11
|
+
from collections.abc import Callable, Iterable
|
|
12
|
+
from typing import Any, TypeVar, overload
|
|
11
13
|
|
|
12
14
|
from decorator import decorate, decorator
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
logger = logging.getLogger("manim")
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
def _get_callable_info(
|
|
19
|
+
def _get_callable_info(callable_: Callable[..., Any], /) -> tuple[str, str]:
|
|
18
20
|
"""Returns type and name of a callable.
|
|
19
21
|
|
|
20
22
|
Parameters
|
|
@@ -28,8 +30,8 @@ def _get_callable_info(callable: Callable) -> tuple[str, str]:
|
|
|
28
30
|
The type and name of the callable. Type can can be one of "class", "method" (for
|
|
29
31
|
functions defined in classes) or "function"). For methods, name is Class.method.
|
|
30
32
|
"""
|
|
31
|
-
what = type(
|
|
32
|
-
name =
|
|
33
|
+
what = type(callable_).__name__
|
|
34
|
+
name = callable_.__qualname__
|
|
33
35
|
if what == "function" and "." in name:
|
|
34
36
|
what = "method"
|
|
35
37
|
elif what != "function":
|
|
@@ -38,9 +40,9 @@ def _get_callable_info(callable: Callable) -> tuple[str, str]:
|
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
def _deprecation_text_component(
|
|
41
|
-
since: str | None,
|
|
42
|
-
until: str | None,
|
|
43
|
-
message: str,
|
|
43
|
+
since: str | None = None,
|
|
44
|
+
until: str | None = None,
|
|
45
|
+
message: str | None = None,
|
|
44
46
|
) -> str:
|
|
45
47
|
"""Generates a text component used in deprecation messages.
|
|
46
48
|
|
|
@@ -68,13 +70,37 @@ def _deprecation_text_component(
|
|
|
68
70
|
return f"deprecated {since}and {until}.{msg}"
|
|
69
71
|
|
|
70
72
|
|
|
73
|
+
# TODO: Use ParamSpec to type decorated functions when Python 3.9 is out of life
|
|
74
|
+
T = TypeVar("T")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@overload
|
|
78
|
+
def deprecated(
|
|
79
|
+
func: Callable[..., T],
|
|
80
|
+
since: str | None = None,
|
|
81
|
+
until: str | None = None,
|
|
82
|
+
replacement: str | None = None,
|
|
83
|
+
message: str | None = "",
|
|
84
|
+
) -> Callable[..., T]: ...
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@overload
|
|
88
|
+
def deprecated(
|
|
89
|
+
func: None = None,
|
|
90
|
+
since: str | None = None,
|
|
91
|
+
until: str | None = None,
|
|
92
|
+
replacement: str | None = None,
|
|
93
|
+
message: str | None = "",
|
|
94
|
+
) -> Callable[[Callable[..., T]], Callable[..., T]]: ...
|
|
95
|
+
|
|
96
|
+
|
|
71
97
|
def deprecated(
|
|
72
|
-
func: Callable = None,
|
|
98
|
+
func: Callable[..., T] | None = None,
|
|
73
99
|
since: str | None = None,
|
|
74
100
|
until: str | None = None,
|
|
75
101
|
replacement: str | None = None,
|
|
76
102
|
message: str | None = "",
|
|
77
|
-
) -> Callable:
|
|
103
|
+
) -> Callable[..., T] | Callable[[Callable[..., T]], Callable[..., T]]:
|
|
78
104
|
"""Decorator to mark a callable as deprecated.
|
|
79
105
|
|
|
80
106
|
The decorated callable will cause a warning when used. The docstring of the
|
|
@@ -104,10 +130,12 @@ def deprecated(
|
|
|
104
130
|
|
|
105
131
|
from manim.utils.deprecation import deprecated
|
|
106
132
|
|
|
133
|
+
|
|
107
134
|
@deprecated
|
|
108
135
|
def foo(**kwargs):
|
|
109
136
|
pass
|
|
110
137
|
|
|
138
|
+
|
|
111
139
|
@deprecated
|
|
112
140
|
class Bar:
|
|
113
141
|
def __init__(self):
|
|
@@ -117,6 +145,7 @@ def deprecated(
|
|
|
117
145
|
def baz(self):
|
|
118
146
|
pass
|
|
119
147
|
|
|
148
|
+
|
|
120
149
|
foo()
|
|
121
150
|
# WARNING The function foo has been deprecated and may be removed in a later version.
|
|
122
151
|
|
|
@@ -130,15 +159,14 @@ def deprecated(
|
|
|
130
159
|
|
|
131
160
|
from manim.utils.deprecation import deprecated
|
|
132
161
|
|
|
162
|
+
|
|
133
163
|
@deprecated(
|
|
134
|
-
since="v0.2",
|
|
135
|
-
until="v0.4",
|
|
136
|
-
replacement="bar",
|
|
137
|
-
message="It is cooler."
|
|
164
|
+
since="v0.2", until="v0.4", replacement="bar", message="It is cooler."
|
|
138
165
|
)
|
|
139
166
|
def foo():
|
|
140
167
|
pass
|
|
141
168
|
|
|
169
|
+
|
|
142
170
|
foo()
|
|
143
171
|
# WARNING The function foo has been deprecated since v0.2 and is expected to be removed after v0.4. Use bar instead. It is cooler.
|
|
144
172
|
|
|
@@ -146,10 +174,12 @@ def deprecated(
|
|
|
146
174
|
|
|
147
175
|
from manim.utils.deprecation import deprecated
|
|
148
176
|
|
|
177
|
+
|
|
149
178
|
@deprecated(since="05/01/2021", until="06/01/2021")
|
|
150
179
|
def foo():
|
|
151
180
|
pass
|
|
152
181
|
|
|
182
|
+
|
|
153
183
|
foo()
|
|
154
184
|
# WARNING The function foo has been deprecated since 05/01/2021 and is expected to be removed after 06/01/2021.
|
|
155
185
|
|
|
@@ -183,7 +213,7 @@ def deprecated(
|
|
|
183
213
|
deprecated = _deprecation_text_component(since, until, msg)
|
|
184
214
|
return f"The {what} {name} has been {deprecated}"
|
|
185
215
|
|
|
186
|
-
def deprecate_docs(func: Callable):
|
|
216
|
+
def deprecate_docs(func: Callable) -> None:
|
|
187
217
|
"""Adjust docstring to indicate the deprecation.
|
|
188
218
|
|
|
189
219
|
Parameters
|
|
@@ -195,7 +225,7 @@ def deprecated(
|
|
|
195
225
|
doc_string = func.__doc__ or ""
|
|
196
226
|
func.__doc__ = f"{doc_string}\n\n.. attention:: Deprecated\n {warning}"
|
|
197
227
|
|
|
198
|
-
def deprecate(func: Callable, *args, **kwargs):
|
|
228
|
+
def deprecate(func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
|
|
199
229
|
"""The actual decorator used to extend the callables behavior.
|
|
200
230
|
|
|
201
231
|
Logs a warning message.
|
|
@@ -232,10 +262,10 @@ def deprecated_params(
|
|
|
232
262
|
params: str | Iterable[str] | None = None,
|
|
233
263
|
since: str | None = None,
|
|
234
264
|
until: str | None = None,
|
|
235
|
-
message: str
|
|
265
|
+
message: str = "",
|
|
236
266
|
redirections: None
|
|
237
267
|
| (Iterable[tuple[str, str] | Callable[..., dict[str, Any]]]) = None,
|
|
238
|
-
) -> Callable:
|
|
268
|
+
) -> Callable[..., T]:
|
|
239
269
|
"""Decorator to mark parameters of a callable as deprecated.
|
|
240
270
|
|
|
241
271
|
It can also be used to automatically redirect deprecated parameter values to their
|
|
@@ -285,10 +315,12 @@ def deprecated_params(
|
|
|
285
315
|
|
|
286
316
|
from manim.utils.deprecation import deprecated_params
|
|
287
317
|
|
|
318
|
+
|
|
288
319
|
@deprecated_params(params="a, b, c")
|
|
289
320
|
def foo(**kwargs):
|
|
290
321
|
pass
|
|
291
322
|
|
|
323
|
+
|
|
292
324
|
foo(x=2, y=3, z=4)
|
|
293
325
|
# No warning
|
|
294
326
|
|
|
@@ -299,15 +331,17 @@ def deprecated_params(
|
|
|
299
331
|
|
|
300
332
|
from manim.utils.deprecation import deprecated_params
|
|
301
333
|
|
|
334
|
+
|
|
302
335
|
@deprecated_params(
|
|
303
336
|
params="a, b, c",
|
|
304
337
|
since="v0.2",
|
|
305
338
|
until="v0.4",
|
|
306
|
-
message="The letters x, y, z are cooler."
|
|
339
|
+
message="The letters x, y, z are cooler.",
|
|
307
340
|
)
|
|
308
341
|
def foo(**kwargs):
|
|
309
342
|
pass
|
|
310
343
|
|
|
344
|
+
|
|
311
345
|
foo(a=2)
|
|
312
346
|
# WARNING The parameter a of method foo has been deprecated since v0.2 and is expected to be removed after v0.4. The letters x, y, z are cooler.
|
|
313
347
|
|
|
@@ -315,14 +349,18 @@ def deprecated_params(
|
|
|
315
349
|
|
|
316
350
|
from manim.utils.deprecation import deprecated_params
|
|
317
351
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
352
|
+
|
|
353
|
+
@deprecated_params(
|
|
354
|
+
redirections=[
|
|
355
|
+
# Two ways to redirect one parameter to another:
|
|
356
|
+
("old_param", "new_param"),
|
|
357
|
+
lambda old_param2: {"new_param22": old_param2},
|
|
358
|
+
]
|
|
359
|
+
)
|
|
323
360
|
def foo(**kwargs):
|
|
324
361
|
return kwargs
|
|
325
362
|
|
|
363
|
+
|
|
326
364
|
foo(x=1, old_param=2)
|
|
327
365
|
# WARNING The parameter old_param of method foo has been deprecated and may be removed in a later version.
|
|
328
366
|
# returns {"x": 1, "new_param": 2}
|
|
@@ -331,12 +369,14 @@ def deprecated_params(
|
|
|
331
369
|
|
|
332
370
|
from manim.utils.deprecation import deprecated_params
|
|
333
371
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
372
|
+
|
|
373
|
+
@deprecated_params(
|
|
374
|
+
redirections=[lambda runtime_in_ms: {"run_time": runtime_in_ms / 1000}]
|
|
375
|
+
)
|
|
337
376
|
def foo(**kwargs):
|
|
338
377
|
return kwargs
|
|
339
378
|
|
|
379
|
+
|
|
340
380
|
foo(runtime_in_ms=500)
|
|
341
381
|
# WARNING The parameter runtime_in_ms of method foo has been deprecated and may be removed in a later version.
|
|
342
382
|
# returns {"run_time": 0.5}
|
|
@@ -345,12 +385,14 @@ def deprecated_params(
|
|
|
345
385
|
|
|
346
386
|
from manim.utils.deprecation import deprecated_params
|
|
347
387
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
388
|
+
|
|
389
|
+
@deprecated_params(
|
|
390
|
+
redirections=[lambda buff_x=1, buff_y=1: {"buff": (buff_x, buff_y)}]
|
|
391
|
+
)
|
|
351
392
|
def foo(**kwargs):
|
|
352
393
|
return kwargs
|
|
353
394
|
|
|
395
|
+
|
|
354
396
|
foo(buff_x=2)
|
|
355
397
|
# WARNING The parameter buff_x of method foo has been deprecated and may be removed in a later version.
|
|
356
398
|
# returns {"buff": (2, 1)}
|
|
@@ -359,18 +401,23 @@ def deprecated_params(
|
|
|
359
401
|
|
|
360
402
|
from manim.utils.deprecation import deprecated_params
|
|
361
403
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
404
|
+
|
|
405
|
+
@deprecated_params(
|
|
406
|
+
redirections=[
|
|
407
|
+
lambda buff=1: {"buff_x": buff[0], "buff_y": buff[1]}
|
|
408
|
+
if isinstance(buff, tuple)
|
|
409
|
+
else {"buff_x": buff, "buff_y": buff}
|
|
410
|
+
]
|
|
411
|
+
)
|
|
366
412
|
def foo(**kwargs):
|
|
367
413
|
return kwargs
|
|
368
414
|
|
|
415
|
+
|
|
369
416
|
foo(buff=0)
|
|
370
417
|
# WARNING The parameter buff of method foo has been deprecated and may be removed in a later version.
|
|
371
418
|
# returns {"buff_x": 0, buff_y: 0}
|
|
372
419
|
|
|
373
|
-
foo(buff=(1,2))
|
|
420
|
+
foo(buff=(1, 2))
|
|
374
421
|
# WARNING The parameter buff of method foo has been deprecated and may be removed in a later version.
|
|
375
422
|
# returns {"buff_x": 1, buff_y: 2}
|
|
376
423
|
|
|
@@ -405,7 +452,7 @@ def deprecated_params(
|
|
|
405
452
|
|
|
406
453
|
redirections = list(redirections)
|
|
407
454
|
|
|
408
|
-
def warning_msg(func: Callable, used: list[str]):
|
|
455
|
+
def warning_msg(func: Callable[..., T], used: list[str]) -> str:
|
|
409
456
|
"""Generate the deprecation warning message.
|
|
410
457
|
|
|
411
458
|
Parameters
|
|
@@ -428,7 +475,7 @@ def deprecated_params(
|
|
|
428
475
|
deprecated = _deprecation_text_component(since, until, message)
|
|
429
476
|
return f"The parameter{parameter_s} {used_} of {what} {name} {has_have_been} {deprecated}"
|
|
430
477
|
|
|
431
|
-
def redirect_params(kwargs: dict, used: list[str]):
|
|
478
|
+
def redirect_params(kwargs: dict[str, Any], used: list[str]) -> None:
|
|
432
479
|
"""Adjust the keyword arguments as defined by the redirections.
|
|
433
480
|
|
|
434
481
|
Parameters
|
|
@@ -452,7 +499,7 @@ def deprecated_params(
|
|
|
452
499
|
if len(redirector_args) > 0:
|
|
453
500
|
kwargs.update(redirector(**redirector_args))
|
|
454
501
|
|
|
455
|
-
def deprecate_params(func, *args, **kwargs):
|
|
502
|
+
def deprecate_params(func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
|
|
456
503
|
"""The actual decorator function used to extend the callables behavior.
|
|
457
504
|
|
|
458
505
|
Logs a warning message when a deprecated parameter is used and redirects it if
|
|
@@ -484,4 +531,4 @@ def deprecated_params(
|
|
|
484
531
|
redirect_params(kwargs, used)
|
|
485
532
|
return func(*args, **kwargs)
|
|
486
533
|
|
|
487
|
-
return decorator(deprecate_params)
|
|
534
|
+
return decorator(deprecate_params) # type: ignore[return-value]
|
manim/utils/docbuild/__init__.py
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Utilities for building the Manim documentation.
|
|
2
|
+
|
|
3
|
+
For more information about the Manim documentation building, see:
|
|
4
|
+
|
|
5
|
+
- :doc:`/contributing/development`, specifically the ``Documentation``
|
|
6
|
+
bullet point under :ref:`polishing-changes-and-submitting-a-pull-request`
|
|
7
|
+
- :doc:`/contributing/docs`
|
|
8
|
+
|
|
9
|
+
.. autosummary::
|
|
10
|
+
:toctree: ../reference
|
|
11
|
+
|
|
12
|
+
autoaliasattr_directive
|
|
13
|
+
autocolor_directive
|
|
14
|
+
manim_directive
|
|
15
|
+
module_parsing
|
|
16
|
+
|
|
17
|
+
"""
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""A directive for documenting type aliases and other module-level attributes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from docutils import nodes
|
|
8
|
+
from docutils.parsers.rst import Directive
|
|
9
|
+
from docutils.statemachine import StringList
|
|
10
|
+
|
|
11
|
+
from manim.utils.docbuild.module_parsing import parse_module_attributes
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from sphinx.application import Sphinx
|
|
15
|
+
|
|
16
|
+
__all__ = ["AliasAttrDocumenter"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
ALIAS_DOCS_DICT, DATA_DICT, TYPEVAR_DICT = parse_module_attributes()
|
|
20
|
+
ALIAS_LIST = [
|
|
21
|
+
alias_name
|
|
22
|
+
for module_dict in ALIAS_DOCS_DICT.values()
|
|
23
|
+
for category_dict in module_dict.values()
|
|
24
|
+
for alias_name in category_dict
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def smart_replace(base: str, alias: str, substitution: str) -> str:
|
|
29
|
+
"""Auxiliary function for substituting type aliases into a base
|
|
30
|
+
string, when there are overlaps between the aliases themselves.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
base
|
|
35
|
+
The string in which the type aliases will be located and
|
|
36
|
+
replaced.
|
|
37
|
+
alias
|
|
38
|
+
The substring to be substituted.
|
|
39
|
+
substitution
|
|
40
|
+
The string which will replace every occurrence of ``alias``.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
str
|
|
45
|
+
The new string after the alias substitution.
|
|
46
|
+
"""
|
|
47
|
+
occurrences = []
|
|
48
|
+
len_alias = len(alias)
|
|
49
|
+
len_base = len(base)
|
|
50
|
+
|
|
51
|
+
def condition(char: str) -> bool:
|
|
52
|
+
return not char.isalnum() and char != "_"
|
|
53
|
+
|
|
54
|
+
start = 0
|
|
55
|
+
i = 0
|
|
56
|
+
while True:
|
|
57
|
+
i = base.find(alias, start)
|
|
58
|
+
if i == -1:
|
|
59
|
+
break
|
|
60
|
+
if (i == 0 or condition(base[i - 1])) and (
|
|
61
|
+
i + len_alias == len_base or condition(base[i + len_alias])
|
|
62
|
+
):
|
|
63
|
+
occurrences.append(i)
|
|
64
|
+
start = i + len_alias
|
|
65
|
+
|
|
66
|
+
for o in occurrences[::-1]:
|
|
67
|
+
base = base[:o] + substitution + base[o + len_alias :]
|
|
68
|
+
|
|
69
|
+
return base
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def setup(app: Sphinx) -> None:
|
|
73
|
+
app.add_directive("autoaliasattr", AliasAttrDocumenter)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AliasAttrDocumenter(Directive):
|
|
77
|
+
"""Directive which replaces Sphinx's Autosummary for module-level
|
|
78
|
+
attributes: instead, it manually crafts a new "Type Aliases"
|
|
79
|
+
section, where all the module-level attributes which are explicitly
|
|
80
|
+
annotated as :class:`TypeAlias` are considered as such, for their
|
|
81
|
+
use all around the Manim docs.
|
|
82
|
+
|
|
83
|
+
These type aliases are separated from the "regular" module-level
|
|
84
|
+
attributes, which get their traditional "Module Attributes"
|
|
85
|
+
section autogenerated with Sphinx's Autosummary under "Type
|
|
86
|
+
Aliases".
|
|
87
|
+
|
|
88
|
+
See ``docs/source/_templates/autosummary/module.rst`` to watch
|
|
89
|
+
this directive in action.
|
|
90
|
+
|
|
91
|
+
See :func:`~.parse_module_attributes` for more information on how
|
|
92
|
+
the modules are parsed to obtain the :class:`TypeAlias` information
|
|
93
|
+
and separate it from the other attributes.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
objtype = "autoaliasattr"
|
|
97
|
+
required_arguments = 1
|
|
98
|
+
has_content = True
|
|
99
|
+
|
|
100
|
+
def run(self) -> list[nodes.Element]:
|
|
101
|
+
module_name = self.arguments[0]
|
|
102
|
+
# not present in the keys of the DICTs
|
|
103
|
+
module_name = module_name.removeprefix("manim.")
|
|
104
|
+
module_alias_dict = ALIAS_DOCS_DICT.get(module_name, None)
|
|
105
|
+
module_attrs_list = DATA_DICT.get(module_name, None)
|
|
106
|
+
module_typevars = TYPEVAR_DICT.get(module_name, None)
|
|
107
|
+
|
|
108
|
+
content = nodes.container()
|
|
109
|
+
|
|
110
|
+
# Add "Type Aliases" section
|
|
111
|
+
if module_alias_dict is not None:
|
|
112
|
+
module_alias_section = nodes.section(ids=[f"{module_name}.alias"])
|
|
113
|
+
content += module_alias_section
|
|
114
|
+
|
|
115
|
+
# Use a rubric (title-like), just like in `module.rst`
|
|
116
|
+
module_alias_section += nodes.rubric(text="Type Aliases")
|
|
117
|
+
|
|
118
|
+
# category_name: str
|
|
119
|
+
# category_dict: AliasCategoryDict = dict[str, AliasInfo]
|
|
120
|
+
for category_name, category_dict in module_alias_dict.items():
|
|
121
|
+
category_section = nodes.section(
|
|
122
|
+
ids=[category_name.lower().replace(" ", "_")]
|
|
123
|
+
)
|
|
124
|
+
module_alias_section += category_section
|
|
125
|
+
# category_name can be possibly "" for uncategorized aliases
|
|
126
|
+
if category_name:
|
|
127
|
+
category_section += nodes.title(text=category_name)
|
|
128
|
+
|
|
129
|
+
category_alias_container = nodes.container()
|
|
130
|
+
category_section += category_alias_container
|
|
131
|
+
|
|
132
|
+
# alias_name: str
|
|
133
|
+
# alias_info: AliasInfo = dict[str, str]
|
|
134
|
+
# Contains "definition": str
|
|
135
|
+
# Can possibly contain "doc": str
|
|
136
|
+
for alias_name, alias_info in category_dict.items():
|
|
137
|
+
# Replace all occurrences of type aliases in the
|
|
138
|
+
# definition for automatic cross-referencing!
|
|
139
|
+
alias_def = alias_info["definition"]
|
|
140
|
+
for A in ALIAS_LIST:
|
|
141
|
+
alias_def = smart_replace(alias_def, A, f":class:`~.{A}`")
|
|
142
|
+
|
|
143
|
+
# Using the `.. class::` directive is CRUCIAL, since
|
|
144
|
+
# function/method parameters are always annotated via
|
|
145
|
+
# classes - therefore Sphinx expects a class
|
|
146
|
+
unparsed = StringList(
|
|
147
|
+
[
|
|
148
|
+
f".. class:: {alias_name}",
|
|
149
|
+
"",
|
|
150
|
+
" .. parsed-literal::",
|
|
151
|
+
"",
|
|
152
|
+
f" {alias_def}",
|
|
153
|
+
"",
|
|
154
|
+
]
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if "doc" in alias_info:
|
|
158
|
+
# Replace all occurrences of type aliases in
|
|
159
|
+
# the docs for automatic cross-referencing!
|
|
160
|
+
alias_doc = alias_info["doc"]
|
|
161
|
+
for A in ALIAS_LIST:
|
|
162
|
+
alias_doc = alias_doc.replace(f"`{A}`", f":class:`~.{A}`")
|
|
163
|
+
|
|
164
|
+
# also hyperlink the TypeVars from that module
|
|
165
|
+
if module_typevars is not None:
|
|
166
|
+
for T in module_typevars:
|
|
167
|
+
alias_doc = alias_doc.replace(f"`{T}`", f":class:`{T}`")
|
|
168
|
+
|
|
169
|
+
# Add all the lines with 4 spaces behind, to consider all the
|
|
170
|
+
# documentation as a paragraph INSIDE the `.. class::` block
|
|
171
|
+
doc_lines = alias_doc.split("\n")
|
|
172
|
+
unparsed.extend(
|
|
173
|
+
StringList([f" {line}" for line in doc_lines])
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Parse the reST text into a fresh container
|
|
177
|
+
# https://www.sphinx-doc.org/en/master/extdev/markupapi.html#parsing-directive-content-as-rest
|
|
178
|
+
alias_container = nodes.container()
|
|
179
|
+
self.state.nested_parse(unparsed, 0, alias_container)
|
|
180
|
+
category_alias_container += alias_container
|
|
181
|
+
|
|
182
|
+
# then add the module TypeVars section
|
|
183
|
+
if module_typevars is not None:
|
|
184
|
+
module_typevars_section = nodes.section(ids=[f"{module_name}.typevars"])
|
|
185
|
+
content += module_typevars_section
|
|
186
|
+
|
|
187
|
+
# Use a rubric (title-like), just like in `module.rst`
|
|
188
|
+
module_typevars_section += nodes.rubric(text="TypeVar's")
|
|
189
|
+
|
|
190
|
+
# name: str
|
|
191
|
+
# definition: TypeVarDict = dict[str, str]
|
|
192
|
+
for name, definition in module_typevars.items():
|
|
193
|
+
# Using the `.. class::` directive is CRUCIAL, since
|
|
194
|
+
# function/method parameters are always annotated via
|
|
195
|
+
# classes - therefore Sphinx expects a class
|
|
196
|
+
unparsed = StringList(
|
|
197
|
+
[
|
|
198
|
+
f".. class:: {name}",
|
|
199
|
+
"",
|
|
200
|
+
" .. parsed-literal::",
|
|
201
|
+
"",
|
|
202
|
+
f" {definition}",
|
|
203
|
+
"",
|
|
204
|
+
]
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Parse the reST text into a fresh container
|
|
208
|
+
# https://www.sphinx-doc.org/en/master/extdev/markupapi.html#parsing-directive-content-as-rest
|
|
209
|
+
typevar_container = nodes.container()
|
|
210
|
+
self.state.nested_parse(unparsed, 0, typevar_container)
|
|
211
|
+
module_typevars_section += typevar_container
|
|
212
|
+
|
|
213
|
+
# Then, add the traditional "Module Attributes" section
|
|
214
|
+
if module_attrs_list is not None:
|
|
215
|
+
module_attrs_section = nodes.section(ids=[f"{module_name}.data"])
|
|
216
|
+
content += module_attrs_section
|
|
217
|
+
|
|
218
|
+
# Use the same rubric (title-like) as in `module.rst`
|
|
219
|
+
module_attrs_section += nodes.rubric(text="Module Attributes")
|
|
220
|
+
# Let Sphinx Autosummary do its thing as always
|
|
221
|
+
# Add all the attribute names with 4 spaces behind, so that
|
|
222
|
+
# they're considered as INSIDE the `.. autosummary::` block
|
|
223
|
+
unparsed = StringList(
|
|
224
|
+
[
|
|
225
|
+
".. autosummary::",
|
|
226
|
+
*(f" {attr}" for attr in module_attrs_list),
|
|
227
|
+
]
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Parse the reST text into a fresh container
|
|
231
|
+
# https://www.sphinx-doc.org/en/master/extdev/markupapi.html#parsing-directive-content-as-rest
|
|
232
|
+
data_container = nodes.container()
|
|
233
|
+
self.state.nested_parse(unparsed, 0, data_container)
|
|
234
|
+
module_attrs_section += data_container
|
|
235
|
+
|
|
236
|
+
return [content]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""A directive for documenting colors in Manim."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from docutils import nodes
|
|
9
|
+
from docutils.parsers.rst import Directive
|
|
10
|
+
|
|
11
|
+
from manim import ManimColor
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from sphinx.application import Sphinx
|
|
15
|
+
|
|
16
|
+
__all__ = ["ManimColorModuleDocumenter"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def setup(app: Sphinx) -> None:
|
|
20
|
+
app.add_directive("automanimcolormodule", ManimColorModuleDocumenter)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ManimColorModuleDocumenter(Directive):
|
|
24
|
+
objtype = "automanimcolormodule"
|
|
25
|
+
required_arguments = 1
|
|
26
|
+
has_content = True
|
|
27
|
+
|
|
28
|
+
def add_directive_header(self, sig: str) -> None:
|
|
29
|
+
# TODO: The Directive class has no method named
|
|
30
|
+
# add_directive_header.
|
|
31
|
+
super().add_directive_header(sig) # type: ignore[misc]
|
|
32
|
+
|
|
33
|
+
def run(self) -> list[nodes.Element]:
|
|
34
|
+
module_name = self.arguments[0]
|
|
35
|
+
try:
|
|
36
|
+
import importlib
|
|
37
|
+
|
|
38
|
+
module = importlib.import_module(module_name)
|
|
39
|
+
except ImportError:
|
|
40
|
+
return [
|
|
41
|
+
nodes.error(
|
|
42
|
+
None, # type: ignore[arg-type]
|
|
43
|
+
nodes.paragraph(text=f"Failed to import module '{module_name}'"),
|
|
44
|
+
)
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
# Number of Colors displayed in one row
|
|
48
|
+
num_color_cols = 2
|
|
49
|
+
table = nodes.table(align="center")
|
|
50
|
+
|
|
51
|
+
tgroup = nodes.tgroup(cols=num_color_cols * 2)
|
|
52
|
+
table += tgroup
|
|
53
|
+
for _ in range(num_color_cols * 2):
|
|
54
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
55
|
+
|
|
56
|
+
# Create header rows for the table
|
|
57
|
+
thead = nodes.thead()
|
|
58
|
+
header_row = nodes.row()
|
|
59
|
+
for _ in range(num_color_cols):
|
|
60
|
+
header_col1 = nodes.paragraph(text="Color Name")
|
|
61
|
+
header_col2 = nodes.paragraph(text="RGB Hex Code")
|
|
62
|
+
header_row += nodes.entry("", header_col1)
|
|
63
|
+
header_row += nodes.entry("", header_col2)
|
|
64
|
+
thead += header_row
|
|
65
|
+
tgroup += thead
|
|
66
|
+
|
|
67
|
+
color_elements = []
|
|
68
|
+
for member_name, member_obj in inspect.getmembers(module):
|
|
69
|
+
if isinstance(member_obj, ManimColor):
|
|
70
|
+
r, g, b = member_obj.to_rgb()
|
|
71
|
+
luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
72
|
+
|
|
73
|
+
# Choose the font color based on the background luminance
|
|
74
|
+
font_color = "black" if luminance > 0.5 else "white"
|
|
75
|
+
|
|
76
|
+
color_elements.append((member_name, member_obj.to_hex(), font_color))
|
|
77
|
+
|
|
78
|
+
tbody = nodes.tbody()
|
|
79
|
+
|
|
80
|
+
for base_i in range(0, len(color_elements), num_color_cols):
|
|
81
|
+
row = nodes.row()
|
|
82
|
+
for idx in range(base_i, base_i + num_color_cols):
|
|
83
|
+
if idx < len(color_elements):
|
|
84
|
+
member_name, hex_code, font_color = color_elements[idx]
|
|
85
|
+
col1 = nodes.literal(text=member_name)
|
|
86
|
+
col2 = nodes.raw(
|
|
87
|
+
"",
|
|
88
|
+
f'<div style="background-color:{hex_code};padding: 0.25rem 0;border-radius:8px;margin: 0.5rem 0.2rem"><code style="color:{font_color};">{hex_code}</code></div>',
|
|
89
|
+
format="html",
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
col1 = nodes.literal(text="")
|
|
93
|
+
col2 = nodes.raw("", "", format="html")
|
|
94
|
+
row += nodes.entry("", col1)
|
|
95
|
+
row += nodes.entry("", col2)
|
|
96
|
+
tbody += row
|
|
97
|
+
tgroup += tbody
|
|
98
|
+
|
|
99
|
+
return [table]
|