manim 0.18.0.post0__py3-none-any.whl → 0.18.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.

Potentially problematic release.


This version of manim might be problematic. Click here for more details.

Files changed (116) hide show
  1. manim/__init__.py +3 -6
  2. manim/__main__.py +18 -10
  3. manim/_config/__init__.py +5 -2
  4. manim/_config/cli_colors.py +12 -8
  5. manim/_config/default.cfg +1 -1
  6. manim/_config/logger_utils.py +9 -8
  7. manim/_config/utils.py +637 -449
  8. manim/animation/animation.py +9 -2
  9. manim/animation/composition.py +78 -40
  10. manim/animation/creation.py +12 -6
  11. manim/animation/fading.py +0 -1
  12. manim/animation/indication.py +10 -21
  13. manim/animation/movement.py +1 -2
  14. manim/animation/rotation.py +1 -1
  15. manim/animation/specialized.py +1 -1
  16. manim/animation/speedmodifier.py +7 -2
  17. manim/animation/transform_matching_parts.py +1 -1
  18. manim/camera/camera.py +13 -4
  19. manim/cli/cfg/group.py +18 -8
  20. manim/cli/checkhealth/checks.py +2 -0
  21. manim/cli/checkhealth/commands.py +2 -0
  22. manim/cli/default_group.py +13 -5
  23. manim/cli/init/commands.py +4 -1
  24. manim/cli/plugins/commands.py +3 -0
  25. manim/cli/render/commands.py +27 -20
  26. manim/cli/render/ease_of_access_options.py +4 -3
  27. manim/cli/render/global_options.py +9 -7
  28. manim/cli/render/output_options.py +6 -5
  29. manim/cli/render/render_options.py +13 -13
  30. manim/constants.py +54 -15
  31. manim/gui/gui.py +2 -0
  32. manim/mobject/geometry/arc.py +4 -4
  33. manim/mobject/geometry/boolean_ops.py +13 -9
  34. manim/mobject/geometry/line.py +16 -8
  35. manim/mobject/geometry/polygram.py +17 -5
  36. manim/mobject/geometry/tips.py +2 -2
  37. manim/mobject/graph.py +379 -106
  38. manim/mobject/graphing/coordinate_systems.py +17 -20
  39. manim/mobject/graphing/functions.py +14 -10
  40. manim/mobject/graphing/number_line.py +1 -1
  41. manim/mobject/mobject.py +175 -72
  42. manim/mobject/opengl/opengl_compatibility.py +2 -0
  43. manim/mobject/opengl/opengl_geometry.py +26 -1
  44. manim/mobject/opengl/opengl_image_mobject.py +2 -0
  45. manim/mobject/opengl/opengl_mobject.py +3 -0
  46. manim/mobject/opengl/opengl_point_cloud_mobject.py +2 -0
  47. manim/mobject/opengl/opengl_surface.py +2 -0
  48. manim/mobject/opengl/opengl_three_dimensions.py +2 -0
  49. manim/mobject/opengl/opengl_vectorized_mobject.py +19 -14
  50. manim/mobject/svg/brace.py +2 -0
  51. manim/mobject/svg/svg_mobject.py +2 -2
  52. manim/mobject/table.py +0 -1
  53. manim/mobject/text/code_mobject.py +2 -0
  54. manim/mobject/text/numbers.py +2 -0
  55. manim/mobject/text/tex_mobject.py +1 -1
  56. manim/mobject/text/text_mobject.py +43 -6
  57. manim/mobject/three_d/three_d_utils.py +4 -4
  58. manim/mobject/three_d/three_dimensions.py +4 -4
  59. manim/mobject/types/image_mobject.py +5 -1
  60. manim/mobject/types/point_cloud_mobject.py +2 -0
  61. manim/mobject/types/vectorized_mobject.py +124 -29
  62. manim/mobject/value_tracker.py +3 -3
  63. manim/mobject/vector_field.py +3 -1
  64. manim/plugins/__init__.py +15 -1
  65. manim/plugins/plugins_flags.py +11 -5
  66. manim/renderer/cairo_renderer.py +12 -2
  67. manim/renderer/opengl_renderer.py +2 -3
  68. manim/renderer/opengl_renderer_window.py +2 -0
  69. manim/renderer/shader_wrapper.py +2 -0
  70. manim/renderer/vectorized_mobject_rendering.py +5 -0
  71. manim/scene/scene.py +22 -6
  72. manim/scene/scene_file_writer.py +3 -1
  73. manim/scene/section.py +2 -0
  74. manim/scene/three_d_scene.py +5 -6
  75. manim/scene/vector_space_scene.py +21 -5
  76. manim/typing.py +567 -67
  77. manim/utils/bezier.py +9 -18
  78. manim/utils/caching.py +2 -0
  79. manim/utils/color/BS381.py +1 -0
  80. manim/utils/color/XKCD.py +1 -0
  81. manim/utils/color/core.py +31 -13
  82. manim/utils/commands.py +8 -1
  83. manim/utils/debug.py +0 -1
  84. manim/utils/deprecation.py +3 -2
  85. manim/utils/docbuild/__init__.py +17 -0
  86. manim/utils/docbuild/autoaliasattr_directive.py +197 -0
  87. manim/utils/docbuild/autocolor_directive.py +9 -4
  88. manim/utils/docbuild/manim_directive.py +18 -9
  89. manim/utils/docbuild/module_parsing.py +198 -0
  90. manim/utils/exceptions.py +6 -0
  91. manim/utils/family.py +2 -0
  92. manim/utils/family_ops.py +5 -0
  93. manim/utils/file_ops.py +6 -2
  94. manim/utils/hashing.py +2 -0
  95. manim/utils/ipython_magic.py +2 -0
  96. manim/utils/module_ops.py +2 -0
  97. manim/utils/opengl.py +14 -0
  98. manim/utils/parameter_parsing.py +31 -0
  99. manim/utils/paths.py +12 -20
  100. manim/utils/rate_functions.py +6 -8
  101. manim/utils/space_ops.py +81 -36
  102. manim/utils/testing/__init__.py +17 -0
  103. manim/utils/testing/frames_comparison.py +7 -5
  104. manim/utils/tex.py +124 -196
  105. manim/utils/tex_file_writing.py +2 -0
  106. manim/utils/tex_templates.py +1 -0
  107. {manim-0.18.0.post0.dist-info → manim-0.18.1.dist-info}/LICENSE.community +1 -1
  108. {manim-0.18.0.post0.dist-info → manim-0.18.1.dist-info}/METADATA +29 -35
  109. manim-0.18.1.dist-info/RECORD +217 -0
  110. manim/cli/new/__init__.py +0 -0
  111. manim/cli/new/group.py +0 -189
  112. manim/plugins/import_plugins.py +0 -43
  113. manim-0.18.0.post0.dist-info/RECORD +0 -217
  114. {manim-0.18.0.post0.dist-info → manim-0.18.1.dist-info}/LICENSE +0 -0
  115. {manim-0.18.0.post0.dist-info → manim-0.18.1.dist-info}/WHEEL +0 -0
  116. {manim-0.18.0.post0.dist-info → manim-0.18.1.dist-info}/entry_points.txt +0 -0
manim/_config/utils.py CHANGED
@@ -9,6 +9,7 @@ movie vs writing a single frame).
9
9
  See :doc:`/guides/configuration` for an introduction to Manim's configuration system.
10
10
 
11
11
  """
12
+
12
13
  from __future__ import annotations
13
14
 
14
15
  import argparse
@@ -21,15 +22,23 @@ import re
21
22
  import sys
22
23
  from collections.abc import Mapping, MutableMapping
23
24
  from pathlib import Path
24
- from typing import Any, Iterable, Iterator
25
+ from typing import TYPE_CHECKING, Any, ClassVar, Iterable, Iterator, NoReturn
25
26
 
26
27
  import numpy as np
27
28
 
28
- from .. import constants
29
- from ..constants import RendererType
30
- from ..typing import StrPath
31
- from ..utils.color import ManimColor
32
- from ..utils.tex import TexTemplate, TexTemplateFromFile
29
+ from manim import constants
30
+ from manim.constants import RendererType
31
+ from manim.utils.color import ManimColor
32
+ from manim.utils.tex import TexTemplate
33
+
34
+ if TYPE_CHECKING:
35
+ from enum import EnumMeta
36
+
37
+ from typing_extensions import Self
38
+
39
+ from manim.typing import StrPath, Vector3D
40
+
41
+ __all__ = ["config_file_paths", "make_config_parser", "ManimConfig", "ManimFrame"]
33
42
 
34
43
 
35
44
  def config_file_paths() -> list[Path]:
@@ -237,8 +246,7 @@ class ManimConfig(MutableMapping):
237
246
  config.background_color = RED
238
247
 
239
248
 
240
- class MyScene(Scene):
241
- ...
249
+ class MyScene(Scene): ...
242
250
 
243
251
  the background color will be set to RED, regardless of the contents of
244
252
  ``manim.cfg`` or the CLI arguments used when invoking manim.
@@ -310,10 +318,11 @@ class ManimConfig(MutableMapping):
310
318
  "zero_pad",
311
319
  "force_window",
312
320
  "no_latex_cleanup",
321
+ "preview_command",
313
322
  }
314
323
 
315
324
  def __init__(self) -> None:
316
- self._d = {k: None for k in self._OPTS}
325
+ self._d: dict[str, Any | None] = {k: None for k in self._OPTS}
317
326
 
318
327
  # behave like a dict
319
328
  def __iter__(self) -> Iterator[str]:
@@ -322,20 +331,20 @@ class ManimConfig(MutableMapping):
322
331
  def __len__(self) -> int:
323
332
  return len(self._d)
324
333
 
325
- def __contains__(self, key) -> bool:
334
+ def __contains__(self, key: object) -> bool:
326
335
  try:
327
336
  self.__getitem__(key)
328
337
  return True
329
338
  except AttributeError:
330
339
  return False
331
340
 
332
- def __getitem__(self, key) -> Any:
341
+ def __getitem__(self, key: str) -> Any:
333
342
  return getattr(self, key)
334
343
 
335
344
  def __setitem__(self, key: str, val: Any) -> None:
336
345
  getattr(ManimConfig, key).fset(self, val) # fset is the property's setter
337
346
 
338
- def update(self, obj: ManimConfig | dict) -> None:
347
+ def update(self, obj: ManimConfig | dict[str, Any]) -> None: # type: ignore[override]
339
348
  """Digest the options found in another :class:`ManimConfig` or in a dict.
340
349
 
341
350
  Similar to :meth:`dict.update`, replaces the values of this object with
@@ -379,14 +388,14 @@ class ManimConfig(MutableMapping):
379
388
  self[k] = v
380
389
 
381
390
  # don't allow to delete anything
382
- def __delitem__(self, key: str):
391
+ def __delitem__(self, key: str) -> NoReturn:
383
392
  raise AttributeError("'ManimConfig' object does not support item deletion")
384
393
 
385
- def __delattr__(self, key: str):
394
+ def __delattr__(self, key: str) -> NoReturn:
386
395
  raise AttributeError("'ManimConfig' object does not support item deletion")
387
396
 
388
397
  # copy functions
389
- def copy(self) -> ManimConfig:
398
+ def copy(self) -> Self:
390
399
  """Deepcopy the contents of this ManimConfig.
391
400
 
392
401
  Returns
@@ -405,13 +414,13 @@ class ManimConfig(MutableMapping):
405
414
  """
406
415
  return copy.deepcopy(self)
407
416
 
408
- def __copy__(self) -> ManimConfig:
417
+ def __copy__(self) -> Self:
409
418
  """See ManimConfig.copy()."""
410
419
  return copy.deepcopy(self)
411
420
 
412
- def __deepcopy__(self, memo: dict[str, Any]) -> ManimConfig:
421
+ def __deepcopy__(self, memo: dict[str, Any]) -> Self:
413
422
  """See ManimConfig.copy()."""
414
- c = ManimConfig()
423
+ c = type(self)()
415
424
  # Deepcopying the underlying dict is enough because all properties
416
425
  # either read directly from it or compute their value on the fly from
417
426
  # values read directly from it.
@@ -419,7 +428,7 @@ class ManimConfig(MutableMapping):
419
428
  return c
420
429
 
421
430
  # helper type-checking methods
422
- def _set_from_list(self, key: str, val: Any, values: list) -> None:
431
+ def _set_from_list(self, key: str, val: Any, values: list[Any]) -> None:
423
432
  """Set ``key`` to ``val`` if ``val`` is contained in ``values``."""
424
433
  if val in values:
425
434
  self._d[key] = val
@@ -451,14 +460,14 @@ class ManimConfig(MutableMapping):
451
460
  """
452
461
  self._d[key] = enum_class(enum_value)
453
462
 
454
- def _set_boolean(self, key: str | int, val: Any) -> None:
463
+ def _set_boolean(self, key: str, val: Any) -> None:
455
464
  """Set ``key`` to ``val`` if ``val`` is Boolean."""
456
465
  if val in [True, False]:
457
466
  self._d[key] = val
458
467
  else:
459
468
  raise ValueError(f"{key} must be boolean")
460
469
 
461
- def _set_tuple(self, key: str, val: tuple) -> None:
470
+ def _set_tuple(self, key: str, val: tuple[Any]) -> None:
462
471
  if isinstance(val, tuple):
463
472
  self._d[key] = val
464
473
  else:
@@ -507,7 +516,7 @@ class ManimConfig(MutableMapping):
507
516
  return rep
508
517
 
509
518
  # builders
510
- def digest_parser(self, parser: configparser.ConfigParser) -> ManimConfig:
519
+ def digest_parser(self, parser: configparser.ConfigParser) -> Self:
511
520
  """Process the config options present in a :class:`ConfigParser` object.
512
521
 
513
522
  This method processes arbitrary parsers, not only those read from a
@@ -643,7 +652,12 @@ class ManimConfig(MutableMapping):
643
652
  setattr(self, "window_size", window_size)
644
653
 
645
654
  # plugins
646
- self.plugins = parser["CLI"].get("plugins", fallback="", raw=True).split(",")
655
+ plugins = parser["CLI"].get("plugins", fallback="", raw=True)
656
+ if plugins == "":
657
+ plugins = []
658
+ else:
659
+ plugins = plugins.split(",")
660
+ self.plugins = plugins
647
661
  # the next two must be set AFTER digesting pixel_width and pixel_height
648
662
  self["frame_height"] = parser["CLI"].getfloat("frame_height", 8.0)
649
663
  width = parser["CLI"].getfloat("frame_width", None)
@@ -685,7 +699,7 @@ class ManimConfig(MutableMapping):
685
699
 
686
700
  return self
687
701
 
688
- def digest_args(self, args: argparse.Namespace) -> ManimConfig:
702
+ def digest_args(self, args: argparse.Namespace) -> Self:
689
703
  """Process the config options present in CLI arguments.
690
704
 
691
705
  Parameters
@@ -759,6 +773,7 @@ class ManimConfig(MutableMapping):
759
773
  "force_window",
760
774
  "dry_run",
761
775
  "no_latex_cleanup",
776
+ "preview_command",
762
777
  ]:
763
778
  if hasattr(args, key):
764
779
  attr = getattr(args, key)
@@ -825,7 +840,7 @@ class ManimConfig(MutableMapping):
825
840
 
826
841
  # Handle --tex_template
827
842
  if args.tex_template:
828
- self.tex_template = TexTemplateFromFile(tex_filename=args.tex_template)
843
+ self.tex_template = TexTemplate.from_file(args.tex_template)
829
844
 
830
845
  if (
831
846
  self.renderer == RendererType.OPENGL
@@ -840,7 +855,7 @@ class ManimConfig(MutableMapping):
840
855
 
841
856
  return self
842
857
 
843
- def digest_file(self, filename: str | os.PathLike) -> ManimConfig:
858
+ def digest_file(self, filename: StrPath) -> Self:
844
859
  """Process the config options present in a ``.cfg`` file.
845
860
 
846
861
  This method processes a single ``.cfg`` file, whereas
@@ -881,102 +896,148 @@ class ManimConfig(MutableMapping):
881
896
  return self.digest_parser(make_config_parser(filename))
882
897
 
883
898
  # config options are properties
884
- preview = property(
885
- lambda self: self._d["preview"] or self._d["enable_gui"],
886
- lambda self, val: self._set_boolean("preview", val),
887
- doc="Whether to play the rendered movie (-p).",
888
- )
889
-
890
- show_in_file_browser = property(
891
- lambda self: self._d["show_in_file_browser"],
892
- lambda self, val: self._set_boolean("show_in_file_browser", val),
893
- doc="Whether to show the output file in the file browser (-f).",
894
- )
895
-
896
- progress_bar = property(
897
- lambda self: self._d["progress_bar"],
898
- lambda self, val: self._set_from_list(
899
- "progress_bar",
900
- val,
901
- ["none", "display", "leave"],
902
- ),
903
- doc="Whether to show progress bars while rendering animations.",
904
- )
905
-
906
- log_to_file = property(
907
- lambda self: self._d["log_to_file"],
908
- lambda self, val: self._set_boolean("log_to_file", val),
909
- doc="Whether to save logs to a file.",
910
- )
911
-
912
- notify_outdated_version = property(
913
- lambda self: self._d["notify_outdated_version"],
914
- lambda self, val: self._set_boolean("notify_outdated_version", val),
915
- doc="Whether to notify if there is a version update available.",
916
- )
917
-
918
- write_to_movie = property(
919
- lambda self: self._d["write_to_movie"],
920
- lambda self, val: self._set_boolean("write_to_movie", val),
921
- doc="Whether to render the scene to a movie file (-w).",
922
- )
923
-
924
- save_last_frame = property(
925
- lambda self: self._d["save_last_frame"],
926
- lambda self, val: self._set_boolean("save_last_frame", val),
927
- doc="Whether to save the last frame of the scene as an image file (-s).",
928
- )
929
-
930
- write_all = property(
931
- lambda self: self._d["write_all"],
932
- lambda self, val: self._set_boolean("write_all", val),
933
- doc="Whether to render all scenes in the input file (-a).",
934
- )
935
-
936
- save_pngs = property(
937
- lambda self: self._d["save_pngs"],
938
- lambda self, val: self._set_boolean("save_pngs", val),
939
- doc="Whether to save all frames in the scene as images files (-g).",
940
- )
941
-
942
- save_as_gif = property(
943
- lambda self: self._d["save_as_gif"],
944
- lambda self, val: self._set_boolean("save_as_gif", val),
945
- doc="Whether to save the rendered scene in .gif format (-i).",
946
- )
947
-
948
- save_sections = property(
949
- lambda self: self._d["save_sections"],
950
- lambda self, val: self._set_boolean("save_sections", val),
951
- doc="Whether to save single videos for each section in addition to the movie file.",
952
- )
953
-
954
- enable_wireframe = property(
955
- lambda self: self._d["enable_wireframe"],
956
- lambda self, val: self._set_boolean("enable_wireframe", val),
957
- doc="Enable wireframe debugging mode in opengl.",
958
- )
959
-
960
- force_window = property(
961
- lambda self: self._d["force_window"],
962
- lambda self, val: self._set_boolean("force_window", val),
963
- doc="Set to force window when using the opengl renderer",
964
- )
965
-
966
- no_latex_cleanup = property(
967
- lambda self: self._d["no_latex_cleanup"],
968
- lambda self, val: self._set_boolean("no_latex_cleanup", val),
969
- doc="Prevents deletion of .aux, .dvi, and .log files produced by Tex and MathTex.",
970
- )
971
-
972
- @property
973
- def verbosity(self):
899
+
900
+ @property
901
+ def preview(self) -> bool:
902
+ """Whether to play the rendered movie (-p)."""
903
+ return self._d["preview"] or self._d["enable_gui"]
904
+
905
+ @preview.setter
906
+ def preview(self, value: bool) -> None:
907
+ self._set_boolean("preview", value)
908
+
909
+ @property
910
+ def show_in_file_browser(self) -> bool:
911
+ """Whether to show the output file in the file browser (-f)."""
912
+ return self._d["show_in_file_browser"]
913
+
914
+ @show_in_file_browser.setter
915
+ def show_in_file_browser(self, value: bool) -> None:
916
+ self._set_boolean("show_in_file_browser", value)
917
+
918
+ @property
919
+ def progress_bar(self) -> str:
920
+ """Whether to show progress bars while rendering animations."""
921
+ return self._d["progress_bar"]
922
+
923
+ @progress_bar.setter
924
+ def progress_bar(self, value: str) -> None:
925
+ self._set_from_list("progress_bar", value, ["none", "display", "leave"])
926
+
927
+ @property
928
+ def log_to_file(self) -> bool:
929
+ """Whether to save logs to a file."""
930
+ return self._d["log_to_file"]
931
+
932
+ @log_to_file.setter
933
+ def log_to_file(self, value: bool) -> None:
934
+ self._set_boolean("log_to_file", value)
935
+
936
+ @property
937
+ def notify_outdated_version(self) -> bool:
938
+ """Whether to notify if there is a version update available."""
939
+ return self._d["notify_outdated_version"]
940
+
941
+ @notify_outdated_version.setter
942
+ def notify_outdated_version(self, value: bool) -> None:
943
+ self._set_boolean("notify_outdated_version", value)
944
+
945
+ @property
946
+ def write_to_movie(self) -> bool:
947
+ """Whether to render the scene to a movie file (-w)."""
948
+ return self._d["write_to_movie"]
949
+
950
+ @write_to_movie.setter
951
+ def write_to_movie(self, value: bool) -> None:
952
+ self._set_boolean("write_to_movie", value)
953
+
954
+ @property
955
+ def save_last_frame(self) -> bool:
956
+ """Whether to save the last frame of the scene as an image file (-s)."""
957
+ return self._d["save_last_frame"]
958
+
959
+ @save_last_frame.setter
960
+ def save_last_frame(self, value: bool) -> None:
961
+ self._set_boolean("save_last_frame", value)
962
+
963
+ @property
964
+ def write_all(self) -> bool:
965
+ """Whether to render all scenes in the input file (-a)."""
966
+ return self._d["write_all"]
967
+
968
+ @write_all.setter
969
+ def write_all(self, value: bool) -> None:
970
+ self._set_boolean("write_all", value)
971
+
972
+ @property
973
+ def save_pngs(self) -> bool:
974
+ """Whether to save all frames in the scene as images files (-g)."""
975
+ return self._d["save_pngs"]
976
+
977
+ @save_pngs.setter
978
+ def save_pngs(self, value: bool) -> None:
979
+ self._set_boolean("save_pngs", value)
980
+
981
+ @property
982
+ def save_as_gif(self) -> bool:
983
+ """Whether to save the rendered scene in .gif format (-i)."""
984
+ return self._d["save_as_gif"]
985
+
986
+ @save_as_gif.setter
987
+ def save_as_gif(self, value: bool) -> None:
988
+ self._set_boolean("save_as_gif", value)
989
+
990
+ @property
991
+ def save_sections(self) -> bool:
992
+ """Whether to save single videos for each section in addition to the movie file."""
993
+ return self._d["save_sections"]
994
+
995
+ @save_sections.setter
996
+ def save_sections(self, value: bool) -> None:
997
+ self._set_boolean("save_sections", value)
998
+
999
+ @property
1000
+ def enable_wireframe(self) -> bool:
1001
+ """Whether to enable wireframe debugging mode in opengl."""
1002
+ return self._d["enable_wireframe"]
1003
+
1004
+ @enable_wireframe.setter
1005
+ def enable_wireframe(self, value: bool) -> None:
1006
+ self._set_boolean("enable_wireframe", value)
1007
+
1008
+ @property
1009
+ def force_window(self) -> bool:
1010
+ """Whether to force window when using the opengl renderer."""
1011
+ return self._d["force_window"]
1012
+
1013
+ @force_window.setter
1014
+ def force_window(self, value: bool) -> None:
1015
+ self._set_boolean("force_window", value)
1016
+
1017
+ @property
1018
+ def no_latex_cleanup(self) -> bool:
1019
+ """Prevents deletion of .aux, .dvi, and .log files produced by Tex and MathTex."""
1020
+ return self._d["no_latex_cleanup"]
1021
+
1022
+ @no_latex_cleanup.setter
1023
+ def no_latex_cleanup(self, value: bool) -> None:
1024
+ self._set_boolean("no_latex_cleanup", value)
1025
+
1026
+ @property
1027
+ def preview_command(self) -> str:
1028
+ return self._d["preview_command"]
1029
+
1030
+ @preview_command.setter
1031
+ def preview_command(self, value: str) -> None:
1032
+ self._set_str("preview_command", value)
1033
+
1034
+ @property
1035
+ def verbosity(self) -> str:
974
1036
  """Logger verbosity; "DEBUG", "INFO", "WARNING", "ERROR", or "CRITICAL" (-v)."""
975
1037
  return self._d["verbosity"]
976
1038
 
977
1039
  @verbosity.setter
978
1040
  def verbosity(self, val: str) -> None:
979
- """Verbosity level of the logger."""
980
1041
  self._set_from_list(
981
1042
  "verbosity",
982
1043
  val,
@@ -985,13 +1046,12 @@ class ManimConfig(MutableMapping):
985
1046
  logging.getLogger("manim").setLevel(val)
986
1047
 
987
1048
  @property
988
- def format(self):
1049
+ def format(self) -> str:
989
1050
  """File format; "png", "gif", "mp4", "webm" or "mov"."""
990
1051
  return self._d["format"]
991
1052
 
992
1053
  @format.setter
993
1054
  def format(self, val: str) -> None:
994
- """File format the renderer will output."""
995
1055
  self._set_from_list(
996
1056
  "format",
997
1057
  val,
@@ -1002,181 +1062,242 @@ class ManimConfig(MutableMapping):
1002
1062
  "Output format set as webm, this can be slower than other formats",
1003
1063
  )
1004
1064
 
1005
- ffmpeg_loglevel = property(
1006
- lambda self: self._d["ffmpeg_loglevel"],
1007
- lambda self, val: self._set_from_list(
1065
+ @property
1066
+ def ffmpeg_loglevel(self) -> str:
1067
+ """Verbosity level of ffmpeg (no flag)."""
1068
+ return self._d["ffmpeg_loglevel"]
1069
+
1070
+ @ffmpeg_loglevel.setter
1071
+ def ffmpeg_loglevel(self, val: str) -> None:
1072
+ self._set_from_list(
1008
1073
  "ffmpeg_loglevel",
1009
1074
  val,
1010
1075
  ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
1011
- ),
1012
- doc="Verbosity level of ffmpeg (no flag).",
1013
- )
1014
-
1015
- ffmpeg_executable = property(
1016
- lambda self: self._d["ffmpeg_executable"],
1017
- lambda self, val: self._set_str("ffmpeg_executable", val),
1018
- doc="Manually specify the path to the ffmpeg executable",
1019
- )
1020
-
1021
- media_embed = property(
1022
- lambda self: self._d["media_embed"],
1023
- lambda self, val: self._d.__setitem__("media_embed", val),
1024
- doc="Embed videos in Jupyter notebook",
1025
- )
1026
-
1027
- media_width = property(
1028
- lambda self: self._d["media_width"],
1029
- lambda self, val: self._d.__setitem__("media_width", val),
1030
- doc="Media width in Jupyter notebook",
1031
- )
1032
-
1033
- pixel_width = property(
1034
- lambda self: self._d["pixel_width"],
1035
- lambda self, val: self._set_pos_number("pixel_width", val, False),
1036
- doc="Frame width in pixels (--resolution, -r).",
1037
- )
1038
-
1039
- pixel_height = property(
1040
- lambda self: self._d["pixel_height"],
1041
- lambda self, val: self._set_pos_number("pixel_height", val, False),
1042
- doc="Frame height in pixels (--resolution, -r).",
1043
- )
1044
-
1045
- aspect_ratio = property(
1046
- lambda self: self._d["pixel_width"] / self._d["pixel_height"],
1047
- doc="Aspect ratio (width / height) in pixels (--resolution, -r).",
1048
- )
1049
-
1050
- frame_height = property(
1051
- lambda self: self._d["frame_height"],
1052
- lambda self, val: self._d.__setitem__("frame_height", val),
1053
- doc="Frame height in logical units (no flag).",
1054
- )
1055
-
1056
- frame_width = property(
1057
- lambda self: self._d["frame_width"],
1058
- lambda self, val: self._d.__setitem__("frame_width", val),
1059
- doc="Frame width in logical units (no flag).",
1060
- )
1061
-
1062
- frame_y_radius = property(
1063
- lambda self: self._d["frame_height"] / 2,
1064
- lambda self, val: (
1065
- self._d.__setitem__("frame_y_radius", val)
1066
- or self._d.__setitem__("frame_height", 2 * val)
1067
- ),
1068
- doc="Half the frame height (no flag).",
1069
- )
1070
-
1071
- frame_x_radius = property(
1072
- lambda self: self._d["frame_width"] / 2,
1073
- lambda self, val: (
1074
- self._d.__setitem__("frame_x_radius", val)
1075
- or self._d.__setitem__("frame_width", 2 * val)
1076
- ),
1077
- doc="Half the frame width (no flag).",
1078
- )
1079
-
1080
- top = property(
1081
- lambda self: self.frame_y_radius * constants.UP,
1082
- doc="Coordinate at the center top of the frame.",
1083
- )
1084
-
1085
- bottom = property(
1086
- lambda self: self.frame_y_radius * constants.DOWN,
1087
- doc="Coordinate at the center bottom of the frame.",
1088
- )
1089
-
1090
- left_side = property(
1091
- lambda self: self.frame_x_radius * constants.LEFT,
1092
- doc="Coordinate at the middle left of the frame.",
1093
- )
1094
-
1095
- right_side = property(
1096
- lambda self: self.frame_x_radius * constants.RIGHT,
1097
- doc="Coordinate at the middle right of the frame.",
1098
- )
1099
-
1100
- frame_rate = property(
1101
- lambda self: self._d["frame_rate"],
1102
- lambda self, val: self._d.__setitem__("frame_rate", val),
1103
- doc="Frame rate in frames per second.",
1104
- )
1105
-
1106
- background_color = property(
1107
- lambda self: self._d["background_color"],
1108
- lambda self, val: self._d.__setitem__("background_color", ManimColor(val)),
1109
- doc="Background color of the scene (-c).",
1110
- )
1111
-
1112
- from_animation_number = property(
1113
- lambda self: self._d["from_animation_number"],
1114
- lambda self, val: self._d.__setitem__("from_animation_number", val),
1115
- doc="Start rendering animations at this number (-n).",
1116
- )
1117
-
1118
- upto_animation_number = property(
1119
- lambda self: self._d["upto_animation_number"],
1120
- lambda self, val: self._set_pos_number("upto_animation_number", val, True),
1121
- doc="Stop rendering animations at this nmber. Use -1 to avoid skipping (-n).",
1122
- )
1123
-
1124
- max_files_cached = property(
1125
- lambda self: self._d["max_files_cached"],
1126
- lambda self, val: self._set_pos_number("max_files_cached", val, True),
1127
- doc="Maximum number of files cached. Use -1 for infinity (no flag).",
1128
- )
1129
-
1130
- window_monitor = property(
1131
- lambda self: self._d["window_monitor"],
1132
- lambda self, val: self._set_pos_number("window_monitor", val, True),
1133
- doc="The monitor on which the scene will be rendered",
1134
- )
1135
- flush_cache = property(
1136
- lambda self: self._d["flush_cache"],
1137
- lambda self, val: self._set_boolean("flush_cache", val),
1138
- doc="Whether to delete all the cached partial movie files.",
1139
- )
1140
-
1141
- disable_caching = property(
1142
- lambda self: self._d["disable_caching"],
1143
- lambda self, val: self._set_boolean("disable_caching", val),
1144
- doc="Whether to use scene caching.",
1145
- )
1146
-
1147
- disable_caching_warning = property(
1148
- lambda self: self._d["disable_caching_warning"],
1149
- lambda self, val: self._set_boolean("disable_caching_warning", val),
1150
- doc="Whether a warning is raised if there are too much submobjects to hash.",
1151
- )
1152
-
1153
- movie_file_extension = property(
1154
- lambda self: self._d["movie_file_extension"],
1155
- lambda self, val: self._set_from_list(
1156
- "movie_file_extension",
1157
- val,
1158
- [".mp4", ".mov", ".webm"],
1159
- ),
1160
- doc="Either .mp4, .webm or .mov.",
1161
- )
1162
-
1163
- background_opacity = property(
1164
- lambda self: self._d["background_opacity"],
1165
- lambda self, val: self._set_between("background_opacity", val, 0, 1),
1166
- doc="A number between 0.0 (fully transparent) and 1.0 (fully opaque).",
1167
- )
1168
-
1169
- frame_size = property(
1170
- lambda self: (self._d["pixel_width"], self._d["pixel_height"]),
1171
- lambda self, tup: (
1172
- self._d.__setitem__("pixel_width", tup[0])
1173
- or self._d.__setitem__("pixel_height", tup[1])
1174
- ),
1175
- doc="Tuple with (pixel width, pixel height) (no flag).",
1176
- )
1177
-
1178
- @property
1179
- def quality(self):
1076
+ )
1077
+
1078
+ @property
1079
+ def ffmpeg_executable(self) -> str:
1080
+ """Custom path to the ffmpeg executable."""
1081
+ return self._d["ffmpeg_executable"]
1082
+
1083
+ @ffmpeg_executable.setter
1084
+ def ffmpeg_executable(self, value: str) -> None:
1085
+ self._set_str("ffmpeg_executable", value)
1086
+
1087
+ @property
1088
+ def media_embed(self) -> bool:
1089
+ """Whether to embed videos in Jupyter notebook."""
1090
+ return self._d["media_embed"]
1091
+
1092
+ @media_embed.setter
1093
+ def media_embed(self, value: bool) -> None:
1094
+ self._set_boolean("media_embed", value)
1095
+
1096
+ @property
1097
+ def media_width(self) -> str:
1098
+ """Media width in Jupyter notebook."""
1099
+ return self._d["media_width"]
1100
+
1101
+ @media_width.setter
1102
+ def media_width(self, value: str) -> None:
1103
+ self._set_str("media_width", value)
1104
+
1105
+ @property
1106
+ def pixel_width(self) -> int:
1107
+ """Frame width in pixels (--resolution, -r)."""
1108
+ return self._d["pixel_width"]
1109
+
1110
+ @pixel_width.setter
1111
+ def pixel_width(self, value: int) -> None:
1112
+ self._set_pos_number("pixel_width", value, False)
1113
+
1114
+ @property
1115
+ def pixel_height(self) -> int:
1116
+ """Frame height in pixels (--resolution, -r)."""
1117
+ return self._d["pixel_height"]
1118
+
1119
+ @pixel_height.setter
1120
+ def pixel_height(self, value: int) -> None:
1121
+ self._set_pos_number("pixel_height", value, False)
1122
+
1123
+ @property
1124
+ def aspect_ratio(self) -> int:
1125
+ """Aspect ratio (width / height) in pixels (--resolution, -r)."""
1126
+ return self._d["pixel_width"] / self._d["pixel_height"]
1127
+
1128
+ @property
1129
+ def frame_height(self) -> float:
1130
+ """Frame height in logical units (no flag)."""
1131
+ return self._d["frame_height"]
1132
+
1133
+ @frame_height.setter
1134
+ def frame_height(self, value: float) -> None:
1135
+ self._d.__setitem__("frame_height", value)
1136
+
1137
+ @property
1138
+ def frame_width(self) -> float:
1139
+ """Frame width in logical units (no flag)."""
1140
+ return self._d["frame_width"]
1141
+
1142
+ @frame_width.setter
1143
+ def frame_width(self, value: float) -> None:
1144
+ self._d.__setitem__("frame_width", value)
1145
+
1146
+ @property
1147
+ def frame_y_radius(self) -> float:
1148
+ """Half the frame height (no flag)."""
1149
+ return self._d["frame_height"] / 2
1150
+
1151
+ @frame_y_radius.setter
1152
+ def frame_y_radius(self, value: float) -> None:
1153
+ self._d.__setitem__("frame_y_radius", value) or self._d.__setitem__(
1154
+ "frame_height", 2 * value
1155
+ )
1156
+
1157
+ @property
1158
+ def frame_x_radius(self) -> float:
1159
+ """Half the frame width (no flag)."""
1160
+ return self._d["frame_width"] / 2
1161
+
1162
+ @frame_x_radius.setter
1163
+ def frame_x_radius(self, value: float) -> None:
1164
+ self._d.__setitem__("frame_x_radius", value) or self._d.__setitem__(
1165
+ "frame_width", 2 * value
1166
+ )
1167
+
1168
+ @property
1169
+ def top(self) -> Vector3D:
1170
+ """Coordinate at the center top of the frame."""
1171
+ return self.frame_y_radius * constants.UP
1172
+
1173
+ @property
1174
+ def bottom(self) -> Vector3D:
1175
+ """Coordinate at the center bottom of the frame."""
1176
+ return self.frame_y_radius * constants.DOWN
1177
+
1178
+ @property
1179
+ def left_side(self) -> Vector3D:
1180
+ """Coordinate at the middle left of the frame."""
1181
+ return self.frame_x_radius * constants.LEFT
1182
+
1183
+ @property
1184
+ def right_side(self) -> Vector3D:
1185
+ """Coordinate at the middle right of the frame."""
1186
+ return self.frame_x_radius * constants.RIGHT
1187
+
1188
+ @property
1189
+ def frame_rate(self) -> float:
1190
+ """Frame rate in frames per second."""
1191
+ return self._d["frame_rate"]
1192
+
1193
+ @frame_rate.setter
1194
+ def frame_rate(self, value: float) -> None:
1195
+ self._d.__setitem__("frame_rate", value)
1196
+
1197
+ # TODO: This was parsed before maybe add ManimColor(val), but results in circular import
1198
+ @property
1199
+ def background_color(self) -> ManimColor:
1200
+ """Background color of the scene (-c)."""
1201
+ return self._d["background_color"]
1202
+
1203
+ @background_color.setter
1204
+ def background_color(self, value: Any) -> None:
1205
+ self._d.__setitem__("background_color", ManimColor(value))
1206
+
1207
+ @property
1208
+ def from_animation_number(self) -> int:
1209
+ """Start rendering animations at this number (-n)."""
1210
+ return self._d["from_animation_number"]
1211
+
1212
+ @from_animation_number.setter
1213
+ def from_animation_number(self, value: int) -> None:
1214
+ self._d.__setitem__("from_animation_number", value)
1215
+
1216
+ @property
1217
+ def upto_animation_number(self) -> int:
1218
+ """Stop rendering animations at this number. Use -1 to avoid skipping (-n)."""
1219
+ return self._d["upto_animation_number"]
1220
+
1221
+ @upto_animation_number.setter
1222
+ def upto_animation_number(self, value: int) -> None:
1223
+ self._set_pos_number("upto_animation_number", value, True)
1224
+
1225
+ @property
1226
+ def max_files_cached(self) -> int:
1227
+ """Maximum number of files cached. Use -1 for infinity (no flag)."""
1228
+ return self._d["max_files_cached"]
1229
+
1230
+ @max_files_cached.setter
1231
+ def max_files_cached(self, value: int) -> None:
1232
+ self._set_pos_number("max_files_cached", value, True)
1233
+
1234
+ @property
1235
+ def window_monitor(self) -> int:
1236
+ """The monitor on which the scene will be rendered."""
1237
+ return self._d["window_monitor"]
1238
+
1239
+ @window_monitor.setter
1240
+ def window_monitor(self, value: int) -> None:
1241
+ self._set_pos_number("window_monitor", value, True)
1242
+
1243
+ @property
1244
+ def flush_cache(self) -> bool:
1245
+ """Whether to delete all the cached partial movie files."""
1246
+ return self._d["flush_cache"]
1247
+
1248
+ @flush_cache.setter
1249
+ def flush_cache(self, value: bool) -> None:
1250
+ self._set_boolean("flush_cache", value)
1251
+
1252
+ @property
1253
+ def disable_caching(self) -> bool:
1254
+ """Whether to use scene caching."""
1255
+ return self._d["disable_caching"]
1256
+
1257
+ @disable_caching.setter
1258
+ def disable_caching(self, value: bool) -> None:
1259
+ self._set_boolean("disable_caching", value)
1260
+
1261
+ @property
1262
+ def disable_caching_warning(self) -> bool:
1263
+ """Whether a warning is raised if there are too much submobjects to hash."""
1264
+ return self._d["disable_caching_warning"]
1265
+
1266
+ @disable_caching_warning.setter
1267
+ def disable_caching_warning(self, value: bool) -> None:
1268
+ self._set_boolean("disable_caching_warning", value)
1269
+
1270
+ @property
1271
+ def movie_file_extension(self) -> str:
1272
+ """Either .mp4, .webm or .mov."""
1273
+ return self._d["movie_file_extension"]
1274
+
1275
+ @movie_file_extension.setter
1276
+ def movie_file_extension(self, value: str) -> None:
1277
+ self._set_from_list("movie_file_extension", value, [".mp4", ".mov", ".webm"])
1278
+
1279
+ @property
1280
+ def background_opacity(self) -> float:
1281
+ """A number between 0.0 (fully transparent) and 1.0 (fully opaque)."""
1282
+ return self._d["background_opacity"]
1283
+
1284
+ @background_opacity.setter
1285
+ def background_opacity(self, value: float) -> None:
1286
+ self._set_between("background_opacity", value, 0, 1)
1287
+
1288
+ @property
1289
+ def frame_size(self) -> tuple[int, int]:
1290
+ """Tuple with (pixel width, pixel height) (no flag)."""
1291
+ return (self._d["pixel_width"], self._d["pixel_height"])
1292
+
1293
+ @frame_size.setter
1294
+ def frame_size(self, value: tuple[int, int]) -> None:
1295
+ self._d.__setitem__("pixel_width", value[0]) or self._d.__setitem__(
1296
+ "pixel_height", value[1]
1297
+ )
1298
+
1299
+ @property
1300
+ def quality(self) -> str | None:
1180
1301
  """Video quality (-q)."""
1181
1302
  keys = ["pixel_width", "pixel_height", "frame_rate"]
1182
1303
  q = {k: self[k] for k in keys}
@@ -1186,27 +1307,27 @@ class ManimConfig(MutableMapping):
1186
1307
  return None
1187
1308
 
1188
1309
  @quality.setter
1189
- def quality(self, qual: str) -> None:
1190
- if qual is None:
1310
+ def quality(self, value: str | None) -> None:
1311
+ if value is None:
1191
1312
  return
1192
- if qual not in constants.QUALITIES:
1313
+ if value not in constants.QUALITIES:
1193
1314
  raise KeyError(f"quality must be one of {list(constants.QUALITIES.keys())}")
1194
- q = constants.QUALITIES[qual]
1315
+ q = constants.QUALITIES[value]
1195
1316
  self.frame_size = q["pixel_width"], q["pixel_height"]
1196
1317
  self.frame_rate = q["frame_rate"]
1197
1318
 
1198
1319
  @property
1199
- def transparent(self):
1320
+ def transparent(self) -> bool:
1200
1321
  """Whether the background opacity is 0.0 (-t)."""
1201
1322
  return self._d["background_opacity"] == 0.0
1202
1323
 
1203
1324
  @transparent.setter
1204
- def transparent(self, val: bool) -> None:
1205
- self._d["background_opacity"] = float(not val)
1206
- self.resolve_movie_file_extension(val)
1325
+ def transparent(self, value: bool) -> None:
1326
+ self._d["background_opacity"] = float(not value)
1327
+ self.resolve_movie_file_extension(value)
1207
1328
 
1208
1329
  @property
1209
- def dry_run(self):
1330
+ def dry_run(self) -> bool:
1210
1331
  """Whether dry run is enabled."""
1211
1332
  return self._d["dry_run"]
1212
1333
 
@@ -1220,7 +1341,7 @@ class ManimConfig(MutableMapping):
1220
1341
  self.format = None
1221
1342
 
1222
1343
  @property
1223
- def renderer(self):
1344
+ def renderer(self) -> RendererType:
1224
1345
  """The currently active renderer.
1225
1346
 
1226
1347
  Populated with one of the available renderers in :class:`.RendererType`.
@@ -1246,15 +1367,15 @@ class ManimConfig(MutableMapping):
1246
1367
  return self._d["renderer"]
1247
1368
 
1248
1369
  @renderer.setter
1249
- def renderer(self, val: str | RendererType) -> None:
1370
+ def renderer(self, value: str | RendererType) -> None:
1250
1371
  """The setter of the renderer property.
1251
1372
 
1252
1373
  Takes care of switching inheritance bases using the
1253
1374
  :class:`.ConvertToOpenGL` metaclass.
1254
1375
  """
1255
- if isinstance(val, str):
1256
- val = val.lower()
1257
- renderer = RendererType(val)
1376
+ if isinstance(value, str):
1377
+ value = value.lower()
1378
+ renderer = RendererType(value)
1258
1379
  try:
1259
1380
  from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
1260
1381
  from manim.mobject.opengl.opengl_mobject import OpenGLMobject
@@ -1288,25 +1409,34 @@ class ManimConfig(MutableMapping):
1288
1409
 
1289
1410
  self._set_from_enum("renderer", renderer, RendererType)
1290
1411
 
1291
- media_dir = property(
1292
- lambda self: self._d["media_dir"],
1293
- lambda self, val: self._set_dir("media_dir", val),
1294
- doc="Main output directory. See :meth:`ManimConfig.get_dir`.",
1295
- )
1296
-
1297
- window_position = property(
1298
- lambda self: self._d["window_position"],
1299
- lambda self, val: self._d.__setitem__("window_position", val),
1300
- doc="Set the position of preview window. You can use directions, e.g. UL/DR/ORIGIN/LEFT...or the position(pixel) of the upper left corner of the window, e.g. '960,540'",
1301
- )
1302
-
1303
- window_size = property(
1304
- lambda self: self._d["window_size"],
1305
- lambda self, val: self._d.__setitem__("window_size", val),
1306
- doc="The size of the opengl window. 'default' to automatically scale the window based on the display monitor.",
1307
- )
1308
-
1309
- def resolve_movie_file_extension(self, is_transparent):
1412
+ @property
1413
+ def media_dir(self) -> str:
1414
+ """Main output directory. See :meth:`ManimConfig.get_dir`."""
1415
+ return self._d["media_dir"]
1416
+
1417
+ @media_dir.setter
1418
+ def media_dir(self, value: str | Path) -> None:
1419
+ self._set_dir("media_dir", value)
1420
+
1421
+ @property
1422
+ def window_position(self) -> str:
1423
+ """Set the position of preview window. You can use directions, e.g. UL/DR/ORIGIN/LEFT...or the position(pixel) of the upper left corner of the window, e.g. '960,540'."""
1424
+ return self._d["window_position"]
1425
+
1426
+ @window_position.setter
1427
+ def window_position(self, value: str) -> None:
1428
+ self._d.__setitem__("window_position", value)
1429
+
1430
+ @property
1431
+ def window_size(self) -> str:
1432
+ """The size of the opengl window. 'default' to automatically scale the window based on the display monitor."""
1433
+ return self._d["window_size"]
1434
+
1435
+ @window_size.setter
1436
+ def window_size(self, value: str) -> None:
1437
+ self._d.__setitem__("window_size", value)
1438
+
1439
+ def resolve_movie_file_extension(self, is_transparent: bool) -> None:
1310
1440
  if is_transparent:
1311
1441
  self.movie_file_extension = ".webm" if self.format == "webm" else ".mov"
1312
1442
  elif self.format == "webm":
@@ -1316,43 +1446,61 @@ class ManimConfig(MutableMapping):
1316
1446
  else:
1317
1447
  self.movie_file_extension = ".mp4"
1318
1448
 
1319
- enable_gui = property(
1320
- lambda self: self._d["enable_gui"],
1321
- lambda self, val: self._set_boolean("enable_gui", val),
1322
- doc="Enable GUI interaction.",
1323
- )
1324
-
1325
- gui_location = property(
1326
- lambda self: self._d["gui_location"],
1327
- lambda self, val: self._set_tuple("gui_location", val),
1328
- doc="Enable GUI interaction.",
1329
- )
1330
-
1331
- fullscreen = property(
1332
- lambda self: self._d["fullscreen"],
1333
- lambda self, val: self._set_boolean("fullscreen", val),
1334
- doc="Expand the window to its maximum possible size.",
1335
- )
1336
-
1337
- use_projection_fill_shaders = property(
1338
- lambda self: self._d["use_projection_fill_shaders"],
1339
- lambda self, val: self._set_boolean("use_projection_fill_shaders", val),
1340
- doc="Use shaders for OpenGLVMobject fill which are compatible with transformation matrices.",
1341
- )
1342
-
1343
- use_projection_stroke_shaders = property(
1344
- lambda self: self._d["use_projection_stroke_shaders"],
1345
- lambda self, val: self._set_boolean("use_projection_stroke_shaders", val),
1346
- doc="Use shaders for OpenGLVMobject stroke which are compatible with transformation matrices.",
1347
- )
1348
-
1349
- zero_pad = property(
1350
- lambda self: self._d["zero_pad"],
1351
- lambda self, val: self._set_int_between("zero_pad", val, 0, 9),
1352
- doc="PNG zero padding. A number between 0 (no zero padding) and 9 (9 columns minimum).",
1353
- )
1354
-
1355
- def get_dir(self, key: str, **kwargs: str) -> Path:
1449
+ @property
1450
+ def enable_gui(self) -> bool:
1451
+ """Enable GUI interaction."""
1452
+ return self._d["enable_gui"]
1453
+
1454
+ @enable_gui.setter
1455
+ def enable_gui(self, value: bool) -> None:
1456
+ self._set_boolean("enable_gui", value)
1457
+
1458
+ @property
1459
+ def gui_location(self) -> tuple[Any]:
1460
+ """Enable GUI interaction."""
1461
+ return self._d["gui_location"]
1462
+
1463
+ @gui_location.setter
1464
+ def gui_location(self, value: tuple[Any]) -> None:
1465
+ self._set_tuple("gui_location", value)
1466
+
1467
+ @property
1468
+ def fullscreen(self) -> bool:
1469
+ """Expand the window to its maximum possible size."""
1470
+ return self._d["fullscreen"]
1471
+
1472
+ @fullscreen.setter
1473
+ def fullscreen(self, value: bool) -> None:
1474
+ self._set_boolean("fullscreen", value)
1475
+
1476
+ @property
1477
+ def use_projection_fill_shaders(self) -> bool:
1478
+ """Use shaders for OpenGLVMobject fill which are compatible with transformation matrices."""
1479
+ return self._d["use_projection_fill_shaders"]
1480
+
1481
+ @use_projection_fill_shaders.setter
1482
+ def use_projection_fill_shaders(self, value: bool) -> None:
1483
+ self._set_boolean("use_projection_fill_shaders", value)
1484
+
1485
+ @property
1486
+ def use_projection_stroke_shaders(self) -> bool:
1487
+ """Use shaders for OpenGLVMobject stroke which are compatible with transformation matrices."""
1488
+ return self._d["use_projection_stroke_shaders"]
1489
+
1490
+ @use_projection_stroke_shaders.setter
1491
+ def use_projection_stroke_shaders(self, value: bool) -> None:
1492
+ self._set_boolean("use_projection_stroke_shaders", value)
1493
+
1494
+ @property
1495
+ def zero_pad(self) -> int:
1496
+ """PNG zero padding. A number between 0 (no zero padding) and 9 (9 columns minimum)."""
1497
+ return self._d["zero_pad"]
1498
+
1499
+ @zero_pad.setter
1500
+ def zero_pad(self, value: int) -> None:
1501
+ self._set_int_between("zero_pad", value, 0, 9)
1502
+
1503
+ def get_dir(self, key: str, **kwargs: Any) -> Path:
1356
1504
  """Resolve a config option that stores a directory.
1357
1505
 
1358
1506
  Config options that store directories may depend on one another. This
@@ -1503,103 +1651,139 @@ class ManimConfig(MutableMapping):
1503
1651
  ) from exc
1504
1652
  return Path(path) if path else None
1505
1653
 
1506
- def _set_dir(self, key: str, val: str | Path):
1654
+ def _set_dir(self, key: str, val: str | Path) -> None:
1507
1655
  if isinstance(val, Path):
1508
1656
  self._d.__setitem__(key, str(val))
1509
1657
  else:
1510
1658
  self._d.__setitem__(key, val)
1511
1659
 
1512
- assets_dir = property(
1513
- lambda self: self._d["assets_dir"],
1514
- lambda self, val: self._set_dir("assets_dir", val),
1515
- doc="Directory to locate video assets (no flag).",
1516
- )
1517
-
1518
- log_dir = property(
1519
- lambda self: self._d["log_dir"],
1520
- lambda self, val: self._set_dir("log_dir", val),
1521
- doc="Directory to place logs. See :meth:`ManimConfig.get_dir`.",
1522
- )
1523
-
1524
- video_dir = property(
1525
- lambda self: self._d["video_dir"],
1526
- lambda self, val: self._set_dir("video_dir", val),
1527
- doc="Directory to place videos (no flag). See :meth:`ManimConfig.get_dir`.",
1528
- )
1529
-
1530
- sections_dir = property(
1531
- lambda self: self._d["sections_dir"],
1532
- lambda self, val: self._set_dir("sections_dir", val),
1533
- doc="Directory to place section videos (no flag). See :meth:`ManimConfig.get_dir`.",
1534
- )
1535
-
1536
- images_dir = property(
1537
- lambda self: self._d["images_dir"],
1538
- lambda self, val: self._set_dir("images_dir", val),
1539
- doc="Directory to place images (no flag). See :meth:`ManimConfig.get_dir`.",
1540
- )
1541
-
1542
- text_dir = property(
1543
- lambda self: self._d["text_dir"],
1544
- lambda self, val: self._set_dir("text_dir", val),
1545
- doc="Directory to place text (no flag). See :meth:`ManimConfig.get_dir`.",
1546
- )
1547
-
1548
- tex_dir = property(
1549
- lambda self: self._d["tex_dir"],
1550
- lambda self, val: self._set_dir("tex_dir", val),
1551
- doc="Directory to place tex (no flag). See :meth:`ManimConfig.get_dir`.",
1552
- )
1553
-
1554
- partial_movie_dir = property(
1555
- lambda self: self._d["partial_movie_dir"],
1556
- lambda self, val: self._set_dir("partial_movie_dir", val),
1557
- doc="Directory to place partial movie files (no flag). See :meth:`ManimConfig.get_dir`.",
1558
- )
1559
-
1560
- custom_folders = property(
1561
- lambda self: self._d["custom_folders"],
1562
- lambda self, val: self._set_boolean("custom_folders", val),
1563
- doc="Whether to use custom folder output.",
1564
- )
1565
-
1566
- input_file = property(
1567
- lambda self: self._d["input_file"],
1568
- lambda self, val: self._set_dir("input_file", val),
1569
- doc="Input file name.",
1570
- )
1571
-
1572
- output_file = property(
1573
- lambda self: self._d["output_file"],
1574
- lambda self, val: self._set_dir("output_file", val),
1575
- doc="Output file name (-o).",
1576
- )
1577
-
1578
- scene_names = property(
1579
- lambda self: self._d["scene_names"],
1580
- lambda self, val: self._d.__setitem__("scene_names", val),
1581
- doc="Scenes to play from file.",
1582
- )
1583
-
1584
- @property
1585
- def tex_template(self):
1660
+ @property
1661
+ def assets_dir(self) -> str:
1662
+ """Directory to locate video assets (no flag)."""
1663
+ return self._d["assets_dir"]
1664
+
1665
+ @assets_dir.setter
1666
+ def assets_dir(self, value: str | Path) -> None:
1667
+ self._set_dir("assets_dir", value)
1668
+
1669
+ @property
1670
+ def log_dir(self) -> str:
1671
+ """Directory to place logs. See :meth:`ManimConfig.get_dir`."""
1672
+ return self._d["log_dir"]
1673
+
1674
+ @log_dir.setter
1675
+ def log_dir(self, value: str | Path) -> None:
1676
+ self._set_dir("log_dir", value)
1677
+
1678
+ @property
1679
+ def video_dir(self) -> str:
1680
+ """Directory to place videos (no flag). See :meth:`ManimConfig.get_dir`."""
1681
+ return self._d["video_dir"]
1682
+
1683
+ @video_dir.setter
1684
+ def video_dir(self, value: str | Path) -> None:
1685
+ self._set_dir("video_dir", value)
1686
+
1687
+ @property
1688
+ def sections_dir(self) -> str:
1689
+ """Directory to place section videos (no flag). See :meth:`ManimConfig.get_dir`."""
1690
+ return self._d["sections_dir"]
1691
+
1692
+ @sections_dir.setter
1693
+ def sections_dir(self, value: str | Path) -> None:
1694
+ self._set_dir("sections_dir", value)
1695
+
1696
+ @property
1697
+ def images_dir(self) -> str:
1698
+ """Directory to place images (no flag). See :meth:`ManimConfig.get_dir`."""
1699
+ return self._d["images_dir"]
1700
+
1701
+ @images_dir.setter
1702
+ def images_dir(self, value: str | Path) -> None:
1703
+ self._set_dir("images_dir", value)
1704
+
1705
+ @property
1706
+ def text_dir(self) -> str:
1707
+ """Directory to place text (no flag). See :meth:`ManimConfig.get_dir`."""
1708
+ return self._d["text_dir"]
1709
+
1710
+ @text_dir.setter
1711
+ def text_dir(self, value: str | Path) -> None:
1712
+ self._set_dir("text_dir", value)
1713
+
1714
+ @property
1715
+ def tex_dir(self) -> str:
1716
+ """Directory to place tex (no flag). See :meth:`ManimConfig.get_dir`."""
1717
+ return self._d["tex_dir"]
1718
+
1719
+ @tex_dir.setter
1720
+ def tex_dir(self, value: str | Path) -> None:
1721
+ self._set_dir("tex_dir", value)
1722
+
1723
+ @property
1724
+ def partial_movie_dir(self) -> str:
1725
+ """Directory to place partial movie files (no flag). See :meth:`ManimConfig.get_dir`."""
1726
+ return self._d["partial_movie_dir"]
1727
+
1728
+ @partial_movie_dir.setter
1729
+ def partial_movie_dir(self, value: str | Path) -> None:
1730
+ self._set_dir("partial_movie_dir", value)
1731
+
1732
+ @property
1733
+ def custom_folders(self) -> str:
1734
+ """Whether to use custom folder output."""
1735
+ return self._d["custom_folders"]
1736
+
1737
+ @custom_folders.setter
1738
+ def custom_folders(self, value: str | Path) -> None:
1739
+ self._set_dir("custom_folders", value)
1740
+
1741
+ @property
1742
+ def input_file(self) -> str:
1743
+ """Input file name."""
1744
+ return self._d["input_file"]
1745
+
1746
+ @input_file.setter
1747
+ def input_file(self, value: str | Path) -> None:
1748
+ self._set_dir("input_file", value)
1749
+
1750
+ @property
1751
+ def output_file(self) -> str:
1752
+ """Output file name (-o)."""
1753
+ return self._d["output_file"]
1754
+
1755
+ @output_file.setter
1756
+ def output_file(self, value: str | Path) -> None:
1757
+ self._set_dir("output_file", value)
1758
+
1759
+ @property
1760
+ def scene_names(self) -> list[str]:
1761
+ """Scenes to play from file."""
1762
+ return self._d["scene_names"]
1763
+
1764
+ @scene_names.setter
1765
+ def scene_names(self, value: list[str]) -> None:
1766
+ self._d.__setitem__("scene_names", value)
1767
+
1768
+ @property
1769
+ def tex_template(self) -> TexTemplate:
1586
1770
  """Template used when rendering Tex. See :class:`.TexTemplate`."""
1587
1771
  if not hasattr(self, "_tex_template") or not self._tex_template:
1588
1772
  fn = self._d["tex_template_file"]
1589
1773
  if fn:
1590
- self._tex_template = TexTemplateFromFile(tex_filename=fn)
1774
+ self._tex_template = TexTemplate.from_file(fn)
1591
1775
  else:
1592
1776
  self._tex_template = TexTemplate()
1593
1777
  return self._tex_template
1594
1778
 
1595
1779
  @tex_template.setter
1596
- def tex_template(self, val: TexTemplateFromFile | TexTemplate) -> None:
1597
- if isinstance(val, (TexTemplateFromFile, TexTemplate)):
1780
+ def tex_template(self, val: TexTemplate) -> None:
1781
+ if isinstance(val, TexTemplate):
1598
1782
  self._tex_template = val
1599
1783
 
1600
1784
  @property
1601
- def tex_template_file(self):
1602
- """File to read Tex template from (no flag). See :class:`.TexTemplateFromFile`."""
1785
+ def tex_template_file(self) -> Path:
1786
+ """File to read Tex template from (no flag). See :class:`.TexTemplate`."""
1603
1787
  return self._d["tex_template_file"]
1604
1788
 
1605
1789
  @tex_template_file.setter
@@ -1615,17 +1799,19 @@ class ManimConfig(MutableMapping):
1615
1799
  self._d["tex_template_file"] = val # actually set the falsy value
1616
1800
 
1617
1801
  @property
1618
- def plugins(self):
1802
+ def plugins(self) -> list[str]:
1619
1803
  """List of plugins to enable."""
1620
1804
  return self._d["plugins"]
1621
1805
 
1622
1806
  @plugins.setter
1623
- def plugins(self, value):
1807
+ def plugins(self, value: list[str]):
1624
1808
  self._d["plugins"] = value
1625
1809
 
1626
1810
 
1811
+ # TODO: to be used in the future - see PR #620
1812
+ # https://github.com/ManimCommunity/manim/pull/620
1627
1813
  class ManimFrame(Mapping):
1628
- _OPTS: set[str] = {
1814
+ _OPTS: ClassVar[set[str]] = {
1629
1815
  "pixel_width",
1630
1816
  "pixel_height",
1631
1817
  "aspect_ratio",
@@ -1638,7 +1824,7 @@ class ManimFrame(Mapping):
1638
1824
  "left_side",
1639
1825
  "right_side",
1640
1826
  }
1641
- _CONSTANTS: dict[str, np.ndarray] = {
1827
+ _CONSTANTS: ClassVar[dict[str, Vector3D]] = {
1642
1828
  "UP": np.array((0.0, 1.0, 0.0)),
1643
1829
  "DOWN": np.array((0.0, -1.0, 0.0)),
1644
1830
  "RIGHT": np.array((1.0, 0.0, 0.0)),
@@ -1655,6 +1841,8 @@ class ManimFrame(Mapping):
1655
1841
  "DR": np.array((1.0, -1.0, 0.0)),
1656
1842
  }
1657
1843
 
1844
+ _c: ManimConfig
1845
+
1658
1846
  def __init__(self, c: ManimConfig) -> None:
1659
1847
  if not isinstance(c, ManimConfig):
1660
1848
  raise TypeError("argument must be instance of 'ManimConfig'")
@@ -1671,20 +1859,20 @@ class ManimFrame(Mapping):
1671
1859
  else:
1672
1860
  raise KeyError(key)
1673
1861
 
1674
- def __iter__(self) -> Iterable:
1862
+ def __iter__(self) -> Iterable[str]:
1675
1863
  return iter(list(self._OPTS) + list(self._CONSTANTS))
1676
1864
 
1677
1865
  def __len__(self) -> int:
1678
1866
  return len(self._OPTS)
1679
1867
 
1680
1868
  # make this truly immutable
1681
- def __setattr__(self, attr, val) -> None:
1869
+ def __setattr__(self, attr: Any, val: Any) -> NoReturn:
1682
1870
  raise TypeError("'ManimFrame' object does not support item assignment")
1683
1871
 
1684
- def __setitem__(self, key, val) -> None:
1872
+ def __setitem__(self, key: Any, val: Any) -> NoReturn:
1685
1873
  raise TypeError("'ManimFrame' object does not support item assignment")
1686
1874
 
1687
- def __delitem__(self, key) -> None:
1875
+ def __delitem__(self, key: Any) -> NoReturn:
1688
1876
  raise TypeError("'ManimFrame' object does not support item deletion")
1689
1877
 
1690
1878