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.
- manim/__init__.py +3 -6
- manim/__main__.py +18 -10
- manim/_config/__init__.py +5 -2
- manim/_config/cli_colors.py +12 -8
- manim/_config/default.cfg +1 -1
- manim/_config/logger_utils.py +9 -8
- manim/_config/utils.py +637 -449
- manim/animation/animation.py +9 -2
- manim/animation/composition.py +78 -40
- manim/animation/creation.py +12 -6
- manim/animation/fading.py +0 -1
- manim/animation/indication.py +10 -21
- manim/animation/movement.py +1 -2
- manim/animation/rotation.py +1 -1
- manim/animation/specialized.py +1 -1
- manim/animation/speedmodifier.py +7 -2
- manim/animation/transform_matching_parts.py +1 -1
- manim/camera/camera.py +13 -4
- manim/cli/cfg/group.py +18 -8
- manim/cli/checkhealth/checks.py +2 -0
- manim/cli/checkhealth/commands.py +2 -0
- manim/cli/default_group.py +13 -5
- manim/cli/init/commands.py +4 -1
- manim/cli/plugins/commands.py +3 -0
- manim/cli/render/commands.py +27 -20
- manim/cli/render/ease_of_access_options.py +4 -3
- manim/cli/render/global_options.py +9 -7
- manim/cli/render/output_options.py +6 -5
- manim/cli/render/render_options.py +13 -13
- manim/constants.py +54 -15
- manim/gui/gui.py +2 -0
- manim/mobject/geometry/arc.py +4 -4
- manim/mobject/geometry/boolean_ops.py +13 -9
- manim/mobject/geometry/line.py +16 -8
- manim/mobject/geometry/polygram.py +17 -5
- manim/mobject/geometry/tips.py +2 -2
- manim/mobject/graph.py +379 -106
- manim/mobject/graphing/coordinate_systems.py +17 -20
- manim/mobject/graphing/functions.py +14 -10
- manim/mobject/graphing/number_line.py +1 -1
- manim/mobject/mobject.py +175 -72
- manim/mobject/opengl/opengl_compatibility.py +2 -0
- manim/mobject/opengl/opengl_geometry.py +26 -1
- manim/mobject/opengl/opengl_image_mobject.py +2 -0
- manim/mobject/opengl/opengl_mobject.py +3 -0
- manim/mobject/opengl/opengl_point_cloud_mobject.py +2 -0
- manim/mobject/opengl/opengl_surface.py +2 -0
- manim/mobject/opengl/opengl_three_dimensions.py +2 -0
- manim/mobject/opengl/opengl_vectorized_mobject.py +19 -14
- manim/mobject/svg/brace.py +2 -0
- manim/mobject/svg/svg_mobject.py +2 -2
- manim/mobject/table.py +0 -1
- manim/mobject/text/code_mobject.py +2 -0
- manim/mobject/text/numbers.py +2 -0
- manim/mobject/text/tex_mobject.py +1 -1
- manim/mobject/text/text_mobject.py +43 -6
- manim/mobject/three_d/three_d_utils.py +4 -4
- manim/mobject/three_d/three_dimensions.py +4 -4
- manim/mobject/types/image_mobject.py +5 -1
- manim/mobject/types/point_cloud_mobject.py +2 -0
- manim/mobject/types/vectorized_mobject.py +124 -29
- manim/mobject/value_tracker.py +3 -3
- manim/mobject/vector_field.py +3 -1
- manim/plugins/__init__.py +15 -1
- manim/plugins/plugins_flags.py +11 -5
- manim/renderer/cairo_renderer.py +12 -2
- manim/renderer/opengl_renderer.py +2 -3
- manim/renderer/opengl_renderer_window.py +2 -0
- manim/renderer/shader_wrapper.py +2 -0
- manim/renderer/vectorized_mobject_rendering.py +5 -0
- manim/scene/scene.py +22 -6
- manim/scene/scene_file_writer.py +3 -1
- manim/scene/section.py +2 -0
- manim/scene/three_d_scene.py +5 -6
- manim/scene/vector_space_scene.py +21 -5
- manim/typing.py +567 -67
- manim/utils/bezier.py +9 -18
- manim/utils/caching.py +2 -0
- manim/utils/color/BS381.py +1 -0
- manim/utils/color/XKCD.py +1 -0
- manim/utils/color/core.py +31 -13
- manim/utils/commands.py +8 -1
- manim/utils/debug.py +0 -1
- manim/utils/deprecation.py +3 -2
- manim/utils/docbuild/__init__.py +17 -0
- manim/utils/docbuild/autoaliasattr_directive.py +197 -0
- manim/utils/docbuild/autocolor_directive.py +9 -4
- manim/utils/docbuild/manim_directive.py +18 -9
- manim/utils/docbuild/module_parsing.py +198 -0
- manim/utils/exceptions.py +6 -0
- manim/utils/family.py +2 -0
- manim/utils/family_ops.py +5 -0
- manim/utils/file_ops.py +6 -2
- manim/utils/hashing.py +2 -0
- manim/utils/ipython_magic.py +2 -0
- manim/utils/module_ops.py +2 -0
- manim/utils/opengl.py +14 -0
- manim/utils/parameter_parsing.py +31 -0
- manim/utils/paths.py +12 -20
- manim/utils/rate_functions.py +6 -8
- manim/utils/space_ops.py +81 -36
- manim/utils/testing/__init__.py +17 -0
- manim/utils/testing/frames_comparison.py +7 -5
- manim/utils/tex.py +124 -196
- manim/utils/tex_file_writing.py +2 -0
- manim/utils/tex_templates.py +1 -0
- {manim-0.18.0.post0.dist-info → manim-0.18.1.dist-info}/LICENSE.community +1 -1
- {manim-0.18.0.post0.dist-info → manim-0.18.1.dist-info}/METADATA +29 -35
- manim-0.18.1.dist-info/RECORD +217 -0
- manim/cli/new/__init__.py +0 -0
- manim/cli/new/group.py +0 -189
- manim/plugins/import_plugins.py +0 -43
- manim-0.18.0.post0.dist-info/RECORD +0 -217
- {manim-0.18.0.post0.dist-info → manim-0.18.1.dist-info}/LICENSE +0 -0
- {manim-0.18.0.post0.dist-info → manim-0.18.1.dist-info}/WHEEL +0 -0
- {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
|
|
29
|
-
from
|
|
30
|
-
from
|
|
31
|
-
from
|
|
32
|
-
|
|
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) ->
|
|
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) ->
|
|
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]) ->
|
|
421
|
+
def __deepcopy__(self, memo: dict[str, Any]) -> Self:
|
|
413
422
|
"""See ManimConfig.copy()."""
|
|
414
|
-
c =
|
|
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
|
|
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) ->
|
|
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
|
-
|
|
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) ->
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
)
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
)
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
)
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
)
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
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
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
)
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
)
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
)
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
)
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
)
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
)
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
)
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
)
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
)
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
)
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
@property
|
|
1179
|
-
def
|
|
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,
|
|
1190
|
-
if
|
|
1310
|
+
def quality(self, value: str | None) -> None:
|
|
1311
|
+
if value is None:
|
|
1191
1312
|
return
|
|
1192
|
-
if
|
|
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[
|
|
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,
|
|
1205
|
-
self._d["background_opacity"] = float(not
|
|
1206
|
-
self.resolve_movie_file_extension(
|
|
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,
|
|
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(
|
|
1256
|
-
|
|
1257
|
-
renderer = RendererType(
|
|
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
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
)
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
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
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
)
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
)
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
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
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
)
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
)
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
)
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
)
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
@property
|
|
1585
|
-
def
|
|
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 =
|
|
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:
|
|
1597
|
-
if isinstance(val,
|
|
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:`.
|
|
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,
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
1875
|
+
def __delitem__(self, key: Any) -> NoReturn:
|
|
1688
1876
|
raise TypeError("'ManimFrame' object does not support item deletion")
|
|
1689
1877
|
|
|
1690
1878
|
|