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.
Files changed (163) hide show
  1. manim/__init__.py +11 -6
  2. manim/__main__.py +62 -19
  3. manim/_config/__init__.py +10 -9
  4. manim/_config/cli_colors.py +26 -9
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +23 -13
  7. manim/_config/utils.py +662 -468
  8. manim/animation/animation.py +164 -18
  9. manim/animation/changing.py +34 -23
  10. manim/animation/composition.py +265 -67
  11. manim/animation/creation.py +208 -26
  12. manim/animation/fading.py +16 -18
  13. manim/animation/growing.py +35 -15
  14. manim/animation/indication.py +150 -76
  15. manim/animation/movement.py +56 -22
  16. manim/animation/numbers.py +64 -6
  17. manim/animation/rotation.py +78 -7
  18. manim/animation/specialized.py +6 -7
  19. manim/animation/speedmodifier.py +13 -10
  20. manim/animation/transform.py +14 -11
  21. manim/animation/transform_matching_parts.py +3 -4
  22. manim/animation/updaters/mobject_update_utils.py +152 -30
  23. manim/animation/updaters/update.py +10 -7
  24. manim/camera/camera.py +182 -118
  25. manim/camera/mapping_camera.py +34 -3
  26. manim/camera/moving_camera.py +95 -74
  27. manim/camera/multi_camera.py +23 -15
  28. manim/camera/three_d_camera.py +70 -52
  29. manim/cli/__init__.py +17 -0
  30. manim/cli/cfg/group.py +76 -44
  31. manim/cli/checkhealth/checks.py +192 -0
  32. manim/cli/checkhealth/commands.py +90 -0
  33. manim/cli/default_group.py +158 -25
  34. manim/cli/init/commands.py +33 -25
  35. manim/cli/plugins/commands.py +16 -3
  36. manim/cli/render/commands.py +72 -60
  37. manim/cli/render/ease_of_access_options.py +4 -3
  38. manim/cli/render/global_options.py +59 -17
  39. manim/cli/render/output_options.py +6 -5
  40. manim/cli/render/render_options.py +98 -33
  41. manim/constants.py +109 -59
  42. manim/data_structures.py +31 -0
  43. manim/mobject/frame.py +8 -5
  44. manim/mobject/geometry/__init__.py +1 -0
  45. manim/mobject/geometry/arc.py +277 -135
  46. manim/mobject/geometry/boolean_ops.py +32 -31
  47. manim/mobject/geometry/labeled.py +376 -0
  48. manim/mobject/geometry/line.py +192 -87
  49. manim/mobject/geometry/polygram.py +224 -58
  50. manim/mobject/geometry/shape_matchers.py +61 -25
  51. manim/mobject/geometry/tips.py +122 -48
  52. manim/mobject/graph.py +1027 -419
  53. manim/mobject/graphing/coordinate_systems.py +533 -278
  54. manim/mobject/graphing/functions.py +53 -32
  55. manim/mobject/graphing/number_line.py +123 -65
  56. manim/mobject/graphing/probability.py +88 -62
  57. manim/mobject/graphing/scale.py +33 -19
  58. manim/mobject/logo.py +118 -28
  59. manim/mobject/matrix.py +87 -83
  60. manim/mobject/mobject.py +912 -442
  61. manim/mobject/opengl/dot_cloud.py +16 -5
  62. manim/mobject/opengl/opengl_compatibility.py +4 -2
  63. manim/mobject/opengl/opengl_geometry.py +254 -153
  64. manim/mobject/opengl/opengl_image_mobject.py +3 -1
  65. manim/mobject/opengl/opengl_mobject.py +779 -482
  66. manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
  67. manim/mobject/opengl/opengl_surface.py +14 -92
  68. manim/mobject/opengl/opengl_three_dimensions.py +12 -8
  69. manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
  70. manim/mobject/svg/brace.py +173 -41
  71. manim/mobject/svg/svg_mobject.py +139 -53
  72. manim/mobject/table.py +61 -68
  73. manim/mobject/text/code_mobject.py +193 -539
  74. manim/mobject/text/numbers.py +81 -34
  75. manim/mobject/text/tex_mobject.py +130 -78
  76. manim/mobject/text/text_mobject.py +288 -164
  77. manim/mobject/three_d/polyhedra.py +111 -13
  78. manim/mobject/three_d/three_d_utils.py +17 -8
  79. manim/mobject/three_d/three_dimensions.py +239 -106
  80. manim/mobject/types/image_mobject.py +50 -30
  81. manim/mobject/types/point_cloud_mobject.py +120 -75
  82. manim/mobject/types/vectorized_mobject.py +841 -408
  83. manim/mobject/value_tracker.py +105 -38
  84. manim/mobject/vector_field.py +50 -31
  85. manim/opengl/__init__.py +3 -3
  86. manim/plugins/__init__.py +14 -1
  87. manim/plugins/plugins_flags.py +10 -14
  88. manim/renderer/cairo_renderer.py +65 -50
  89. manim/renderer/opengl_renderer.py +89 -69
  90. manim/renderer/opengl_renderer_window.py +39 -18
  91. manim/renderer/shader.py +123 -87
  92. manim/renderer/shader_wrapper.py +44 -28
  93. manim/renderer/vectorized_mobject_rendering.py +38 -10
  94. manim/scene/moving_camera_scene.py +32 -3
  95. manim/scene/scene.py +507 -242
  96. manim/scene/scene_file_writer.py +371 -220
  97. manim/scene/section.py +20 -16
  98. manim/scene/three_d_scene.py +14 -22
  99. manim/scene/vector_space_scene.py +223 -129
  100. manim/scene/zoomed_scene.py +46 -41
  101. manim/typing.py +990 -0
  102. manim/utils/bezier.py +1823 -371
  103. manim/utils/caching.py +12 -5
  104. manim/utils/color/AS2700.py +236 -0
  105. manim/utils/color/BS381.py +318 -0
  106. manim/utils/color/DVIPSNAMES.py +96 -0
  107. manim/utils/color/SVGNAMES.py +179 -0
  108. manim/utils/color/X11.py +533 -0
  109. manim/utils/color/XKCD.py +952 -0
  110. manim/utils/color/__init__.py +61 -0
  111. manim/utils/color/core.py +1667 -0
  112. manim/utils/color/manim_colors.py +218 -0
  113. manim/utils/commands.py +48 -20
  114. manim/utils/config_ops.py +39 -19
  115. manim/utils/debug.py +8 -7
  116. manim/utils/deprecation.py +86 -39
  117. manim/utils/docbuild/__init__.py +17 -0
  118. manim/utils/docbuild/autoaliasattr_directive.py +236 -0
  119. manim/utils/docbuild/autocolor_directive.py +99 -0
  120. manim/utils/docbuild/manim_directive.py +94 -41
  121. manim/utils/docbuild/module_parsing.py +245 -0
  122. manim/utils/exceptions.py +6 -0
  123. manim/utils/family.py +5 -3
  124. manim/utils/family_ops.py +17 -4
  125. manim/utils/file_ops.py +27 -17
  126. manim/utils/hashing.py +55 -45
  127. manim/utils/images.py +13 -7
  128. manim/utils/ipython_magic.py +13 -7
  129. manim/utils/iterables.py +163 -120
  130. manim/utils/module_ops.py +66 -24
  131. manim/utils/opengl.py +77 -24
  132. manim/utils/parameter_parsing.py +32 -0
  133. manim/utils/paths.py +30 -33
  134. manim/utils/polylabel.py +235 -0
  135. manim/utils/qhull.py +218 -0
  136. manim/utils/rate_functions.py +98 -32
  137. manim/utils/simple_functions.py +25 -33
  138. manim/utils/sounds.py +7 -1
  139. manim/utils/space_ops.py +188 -115
  140. manim/utils/testing/__init__.py +17 -0
  141. manim/utils/testing/_frames_testers.py +13 -8
  142. manim/utils/testing/_show_diff.py +5 -3
  143. manim/utils/testing/_test_class_makers.py +34 -18
  144. manim/utils/testing/frames_comparison.py +37 -19
  145. manim/utils/tex.py +130 -198
  146. manim/utils/tex_file_writing.py +77 -47
  147. manim/utils/tex_templates.py +2 -1
  148. manim/utils/unit.py +6 -5
  149. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
  150. manim-0.19.1.dist-info/RECORD +220 -0
  151. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
  152. manim-0.19.1.dist-info/entry_points.txt +3 -0
  153. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
  154. manim/cli/new/group.py +0 -189
  155. manim/communitycolors.py +0 -9
  156. manim/gui/__init__.py +0 -0
  157. manim/gui/gui.py +0 -82
  158. manim/plugins/import_plugins.py +0 -43
  159. manim/utils/color.py +0 -552
  160. manim-0.17.0.dist-info/RECORD +0 -206
  161. manim-0.17.0.dist-info/entry_points.txt +0 -4
  162. /manim/cli/{new → checkhealth}/__init__.py +0 -0
  163. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
@@ -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 typing import Any, Callable, Iterable
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
- from .. import logger
16
+ logger = logging.getLogger("manim")
15
17
 
16
18
 
17
- def _get_callable_info(callable: Callable) -> tuple[str, str]:
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(callable).__name__
32
- name = callable.__qualname__
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 | None = "",
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
- @deprecated_params(redirections=[
319
- # Two ways to redirect one parameter to another:
320
- ("old_param", "new_param"),
321
- lambda old_param2: {"new_param22": old_param2}
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
- @deprecated_params(redirections=[
335
- lambda runtime_in_ms: {"run_time": runtime_in_ms / 1000}
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
- @deprecated_params(redirections=[
349
- lambda buff_x=1, buff_y=1: {"buff": (buff_x, buff_y)}
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
- @deprecated_params(redirections=[
363
- lambda buff=1: {"buff_x": buff[0], "buff_y": buff[1]} if isinstance(buff, tuple)
364
- else {"buff_x": buff, "buff_y": buff}
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]
@@ -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]