manim 0.18.0.post0__py3-none-any.whl → 0.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of manim might be problematic. Click here for more details.
- manim/__init__.py +3 -6
- manim/__main__.py +61 -20
- manim/_config/__init__.py +6 -3
- manim/_config/cli_colors.py +16 -8
- manim/_config/default.cfg +1 -3
- manim/_config/logger_utils.py +14 -8
- manim/_config/utils.py +651 -472
- manim/animation/animation.py +152 -5
- manim/animation/composition.py +80 -39
- manim/animation/creation.py +196 -14
- manim/animation/fading.py +5 -9
- manim/animation/indication.py +103 -47
- manim/animation/movement.py +22 -5
- manim/animation/rotation.py +3 -2
- manim/animation/specialized.py +4 -6
- manim/animation/speedmodifier.py +10 -5
- manim/animation/transform.py +4 -5
- manim/animation/transform_matching_parts.py +1 -1
- manim/animation/updaters/mobject_update_utils.py +17 -14
- manim/camera/camera.py +15 -6
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +70 -44
- manim/cli/checkhealth/checks.py +93 -75
- manim/cli/checkhealth/commands.py +14 -5
- manim/cli/default_group.py +157 -25
- manim/cli/init/commands.py +32 -24
- manim/cli/plugins/commands.py +16 -3
- manim/cli/render/commands.py +72 -60
- manim/cli/render/ease_of_access_options.py +4 -3
- manim/cli/render/global_options.py +51 -15
- manim/cli/render/output_options.py +6 -5
- manim/cli/render/render_options.py +97 -32
- manim/constants.py +65 -19
- manim/gui/gui.py +2 -0
- manim/mobject/frame.py +0 -1
- manim/mobject/geometry/arc.py +112 -78
- manim/mobject/geometry/boolean_ops.py +32 -25
- manim/mobject/geometry/labeled.py +300 -77
- manim/mobject/geometry/line.py +132 -64
- manim/mobject/geometry/polygram.py +126 -30
- manim/mobject/geometry/shape_matchers.py +35 -15
- manim/mobject/geometry/tips.py +38 -29
- manim/mobject/graph.py +414 -133
- manim/mobject/graphing/coordinate_systems.py +126 -64
- manim/mobject/graphing/functions.py +25 -15
- manim/mobject/graphing/number_line.py +24 -10
- manim/mobject/graphing/probability.py +2 -10
- manim/mobject/graphing/scale.py +6 -5
- manim/mobject/matrix.py +17 -19
- manim/mobject/mobject.py +314 -165
- manim/mobject/opengl/opengl_compatibility.py +2 -0
- manim/mobject/opengl/opengl_geometry.py +30 -9
- manim/mobject/opengl/opengl_image_mobject.py +2 -0
- manim/mobject/opengl/opengl_mobject.py +509 -343
- manim/mobject/opengl/opengl_point_cloud_mobject.py +5 -7
- manim/mobject/opengl/opengl_surface.py +3 -2
- manim/mobject/opengl/opengl_three_dimensions.py +2 -0
- manim/mobject/opengl/opengl_vectorized_mobject.py +46 -79
- manim/mobject/svg/brace.py +63 -13
- manim/mobject/svg/svg_mobject.py +4 -3
- manim/mobject/table.py +11 -13
- manim/mobject/text/code_mobject.py +186 -548
- manim/mobject/text/numbers.py +9 -7
- manim/mobject/text/tex_mobject.py +23 -14
- manim/mobject/text/text_mobject.py +70 -24
- manim/mobject/three_d/polyhedra.py +98 -1
- manim/mobject/three_d/three_d_utils.py +4 -4
- manim/mobject/three_d/three_dimensions.py +62 -34
- manim/mobject/types/image_mobject.py +42 -24
- manim/mobject/types/point_cloud_mobject.py +105 -67
- manim/mobject/types/vectorized_mobject.py +496 -228
- manim/mobject/value_tracker.py +5 -4
- manim/mobject/vector_field.py +5 -5
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +14 -1
- manim/plugins/plugins_flags.py +14 -8
- manim/renderer/cairo_renderer.py +20 -10
- manim/renderer/opengl_renderer.py +21 -23
- manim/renderer/opengl_renderer_window.py +2 -0
- manim/renderer/shader.py +2 -3
- manim/renderer/shader_wrapper.py +5 -2
- manim/renderer/vectorized_mobject_rendering.py +5 -0
- manim/scene/moving_camera_scene.py +23 -0
- manim/scene/scene.py +90 -43
- manim/scene/scene_file_writer.py +316 -165
- manim/scene/section.py +17 -15
- manim/scene/three_d_scene.py +13 -21
- manim/scene/vector_space_scene.py +22 -9
- manim/typing.py +830 -70
- manim/utils/bezier.py +1667 -399
- manim/utils/caching.py +13 -5
- manim/utils/color/AS2700.py +2 -0
- manim/utils/color/BS381.py +3 -0
- manim/utils/color/DVIPSNAMES.py +96 -0
- manim/utils/color/SVGNAMES.py +179 -0
- manim/utils/color/X11.py +3 -0
- manim/utils/color/XKCD.py +3 -0
- manim/utils/color/__init__.py +8 -5
- manim/utils/color/core.py +844 -309
- manim/utils/color/manim_colors.py +7 -9
- manim/utils/commands.py +48 -20
- manim/utils/config_ops.py +18 -13
- manim/utils/debug.py +8 -7
- manim/utils/deprecation.py +90 -40
- manim/utils/docbuild/__init__.py +17 -0
- manim/utils/docbuild/autoaliasattr_directive.py +234 -0
- manim/utils/docbuild/autocolor_directive.py +21 -17
- manim/utils/docbuild/manim_directive.py +50 -35
- manim/utils/docbuild/module_parsing.py +245 -0
- manim/utils/exceptions.py +6 -0
- manim/utils/family.py +5 -3
- manim/utils/family_ops.py +17 -4
- manim/utils/file_ops.py +26 -16
- manim/utils/hashing.py +9 -7
- manim/utils/images.py +10 -4
- manim/utils/ipython_magic.py +14 -8
- manim/utils/iterables.py +161 -119
- manim/utils/module_ops.py +57 -19
- manim/utils/opengl.py +83 -24
- manim/utils/parameter_parsing.py +32 -0
- manim/utils/paths.py +21 -23
- manim/utils/polylabel.py +168 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +74 -39
- manim/utils/simple_functions.py +24 -15
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +125 -69
- manim/utils/testing/__init__.py +17 -0
- manim/utils/testing/_frames_testers.py +13 -8
- manim/utils/testing/_show_diff.py +5 -3
- manim/utils/testing/_test_class_makers.py +33 -18
- manim/utils/testing/frames_comparison.py +27 -19
- manim/utils/tex.py +127 -197
- manim/utils/tex_file_writing.py +47 -45
- manim/utils/tex_templates.py +2 -1
- manim/utils/unit.py +6 -5
- {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE.community +1 -1
- {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/METADATA +40 -39
- manim-0.19.0.dist-info/RECORD +221 -0
- {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
- manim/cli/new/__init__.py +0 -0
- manim/cli/new/group.py +0 -189
- manim/plugins/import_plugins.py +0 -43
- manim-0.18.0.post0.dist-info/RECORD +0 -217
- {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
- {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
manim/_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
|
|
@@ -19,17 +20,27 @@ import logging
|
|
|
19
20
|
import os
|
|
20
21
|
import re
|
|
21
22
|
import sys
|
|
22
|
-
from collections.abc import Mapping, MutableMapping
|
|
23
|
+
from collections.abc import Iterable, Iterator, Mapping, MutableMapping
|
|
23
24
|
from pathlib import Path
|
|
24
|
-
from typing import Any,
|
|
25
|
+
from typing import TYPE_CHECKING, Any, ClassVar, 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"]
|
|
42
|
+
|
|
43
|
+
logger = logging.getLogger("manim")
|
|
33
44
|
|
|
34
45
|
|
|
35
46
|
def config_file_paths() -> list[Path]:
|
|
@@ -237,8 +248,7 @@ class ManimConfig(MutableMapping):
|
|
|
237
248
|
config.background_color = RED
|
|
238
249
|
|
|
239
250
|
|
|
240
|
-
class MyScene(Scene):
|
|
241
|
-
...
|
|
251
|
+
class MyScene(Scene): ...
|
|
242
252
|
|
|
243
253
|
the background color will be set to RED, regardless of the contents of
|
|
244
254
|
``manim.cfg`` or the CLI arguments used when invoking manim.
|
|
@@ -255,7 +265,6 @@ class ManimConfig(MutableMapping):
|
|
|
255
265
|
"dry_run",
|
|
256
266
|
"enable_wireframe",
|
|
257
267
|
"ffmpeg_loglevel",
|
|
258
|
-
"ffmpeg_executable",
|
|
259
268
|
"format",
|
|
260
269
|
"flush_cache",
|
|
261
270
|
"frame_height",
|
|
@@ -310,10 +319,11 @@ class ManimConfig(MutableMapping):
|
|
|
310
319
|
"zero_pad",
|
|
311
320
|
"force_window",
|
|
312
321
|
"no_latex_cleanup",
|
|
322
|
+
"preview_command",
|
|
313
323
|
}
|
|
314
324
|
|
|
315
325
|
def __init__(self) -> None:
|
|
316
|
-
self._d
|
|
326
|
+
self._d: dict[str, Any | None] = dict.fromkeys(self._OPTS)
|
|
317
327
|
|
|
318
328
|
# behave like a dict
|
|
319
329
|
def __iter__(self) -> Iterator[str]:
|
|
@@ -322,20 +332,20 @@ class ManimConfig(MutableMapping):
|
|
|
322
332
|
def __len__(self) -> int:
|
|
323
333
|
return len(self._d)
|
|
324
334
|
|
|
325
|
-
def __contains__(self, key) -> bool:
|
|
335
|
+
def __contains__(self, key: object) -> bool:
|
|
326
336
|
try:
|
|
327
337
|
self.__getitem__(key)
|
|
328
338
|
return True
|
|
329
339
|
except AttributeError:
|
|
330
340
|
return False
|
|
331
341
|
|
|
332
|
-
def __getitem__(self, key) -> Any:
|
|
342
|
+
def __getitem__(self, key: str) -> Any:
|
|
333
343
|
return getattr(self, key)
|
|
334
344
|
|
|
335
345
|
def __setitem__(self, key: str, val: Any) -> None:
|
|
336
346
|
getattr(ManimConfig, key).fset(self, val) # fset is the property's setter
|
|
337
347
|
|
|
338
|
-
def update(self, obj: ManimConfig | dict) -> None:
|
|
348
|
+
def update(self, obj: ManimConfig | dict[str, Any]) -> None: # type: ignore[override]
|
|
339
349
|
"""Digest the options found in another :class:`ManimConfig` or in a dict.
|
|
340
350
|
|
|
341
351
|
Similar to :meth:`dict.update`, replaces the values of this object with
|
|
@@ -362,7 +372,6 @@ class ManimConfig(MutableMapping):
|
|
|
362
372
|
:meth:`~ManimConfig.digest_parser`
|
|
363
373
|
|
|
364
374
|
"""
|
|
365
|
-
|
|
366
375
|
if isinstance(obj, ManimConfig):
|
|
367
376
|
self._d.update(obj._d)
|
|
368
377
|
if obj.tex_template:
|
|
@@ -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
|
|
@@ -633,17 +642,19 @@ class ManimConfig(MutableMapping):
|
|
|
633
642
|
gui_location = tuple(
|
|
634
643
|
map(int, re.split(r"[;,\-]", parser["CLI"]["gui_location"])),
|
|
635
644
|
)
|
|
636
|
-
|
|
645
|
+
self.gui_location = gui_location
|
|
637
646
|
|
|
638
647
|
window_size = parser["CLI"][
|
|
639
648
|
"window_size"
|
|
640
649
|
] # if not "default", get a tuple of the position
|
|
641
650
|
if window_size != "default":
|
|
642
651
|
window_size = tuple(map(int, re.split(r"[;,\-]", window_size)))
|
|
643
|
-
|
|
652
|
+
self.window_size = window_size
|
|
644
653
|
|
|
645
654
|
# plugins
|
|
646
|
-
|
|
655
|
+
plugins = parser["CLI"].get("plugins", fallback="", raw=True)
|
|
656
|
+
plugins = [] if plugins == "" else plugins.split(",")
|
|
657
|
+
self.plugins = plugins
|
|
647
658
|
# the next two must be set AFTER digesting pixel_width and pixel_height
|
|
648
659
|
self["frame_height"] = parser["CLI"].getfloat("frame_height", 8.0)
|
|
649
660
|
width = parser["CLI"].getfloat("frame_width", None)
|
|
@@ -659,25 +670,21 @@ class ManimConfig(MutableMapping):
|
|
|
659
670
|
|
|
660
671
|
val = parser["CLI"].get("progress_bar")
|
|
661
672
|
if val:
|
|
662
|
-
|
|
673
|
+
self.progress_bar = val
|
|
663
674
|
|
|
664
675
|
val = parser["ffmpeg"].get("loglevel")
|
|
665
676
|
if val:
|
|
666
677
|
self.ffmpeg_loglevel = val
|
|
667
678
|
|
|
668
|
-
# TODO: Fix the mess above and below
|
|
669
|
-
val = parser["ffmpeg"].get("ffmpeg_executable")
|
|
670
|
-
setattr(self, "ffmpeg_executable", val)
|
|
671
|
-
|
|
672
679
|
try:
|
|
673
680
|
val = parser["jupyter"].getboolean("media_embed")
|
|
674
681
|
except ValueError:
|
|
675
682
|
val = None
|
|
676
|
-
|
|
683
|
+
self.media_embed = val
|
|
677
684
|
|
|
678
685
|
val = parser["jupyter"].get("media_width")
|
|
679
686
|
if val:
|
|
680
|
-
|
|
687
|
+
self.media_width = val
|
|
681
688
|
|
|
682
689
|
val = parser["CLI"].get("quality", fallback="", raw=True)
|
|
683
690
|
if val:
|
|
@@ -685,7 +692,7 @@ class ManimConfig(MutableMapping):
|
|
|
685
692
|
|
|
686
693
|
return self
|
|
687
694
|
|
|
688
|
-
def digest_args(self, args: argparse.Namespace) ->
|
|
695
|
+
def digest_args(self, args: argparse.Namespace) -> Self:
|
|
689
696
|
"""Process the config options present in CLI arguments.
|
|
690
697
|
|
|
691
698
|
Parameters
|
|
@@ -759,6 +766,7 @@ class ManimConfig(MutableMapping):
|
|
|
759
766
|
"force_window",
|
|
760
767
|
"dry_run",
|
|
761
768
|
"no_latex_cleanup",
|
|
769
|
+
"preview_command",
|
|
762
770
|
]:
|
|
763
771
|
if hasattr(args, key):
|
|
764
772
|
attr = getattr(args, key)
|
|
@@ -789,7 +797,7 @@ class ManimConfig(MutableMapping):
|
|
|
789
797
|
try:
|
|
790
798
|
self.upto_animation_number = nflag[1]
|
|
791
799
|
except Exception:
|
|
792
|
-
|
|
800
|
+
logger.info(
|
|
793
801
|
f"No end scene number specified in -n option. Rendering from {nflag[0]} onwards...",
|
|
794
802
|
)
|
|
795
803
|
|
|
@@ -825,22 +833,19 @@ class ManimConfig(MutableMapping):
|
|
|
825
833
|
|
|
826
834
|
# Handle --tex_template
|
|
827
835
|
if args.tex_template:
|
|
828
|
-
self.tex_template =
|
|
836
|
+
self.tex_template = TexTemplate.from_file(args.tex_template)
|
|
829
837
|
|
|
830
|
-
if
|
|
831
|
-
self.renderer == RendererType.OPENGL
|
|
832
|
-
and getattr(args, "write_to_movie") is None
|
|
833
|
-
):
|
|
838
|
+
if self.renderer == RendererType.OPENGL and args.write_to_movie is None:
|
|
834
839
|
# --write_to_movie was not passed on the command line, so don't generate video.
|
|
835
840
|
self["write_to_movie"] = False
|
|
836
841
|
|
|
837
842
|
# Handle --gui_location flag.
|
|
838
|
-
if
|
|
843
|
+
if args.gui_location is not None:
|
|
839
844
|
self.gui_location = args.gui_location
|
|
840
845
|
|
|
841
846
|
return self
|
|
842
847
|
|
|
843
|
-
def digest_file(self, filename:
|
|
848
|
+
def digest_file(self, filename: StrPath) -> Self:
|
|
844
849
|
"""Process the config options present in a ``.cfg`` file.
|
|
845
850
|
|
|
846
851
|
This method processes a single ``.cfg`` file, whereas
|
|
@@ -881,302 +886,403 @@ class ManimConfig(MutableMapping):
|
|
|
881
886
|
return self.digest_parser(make_config_parser(filename))
|
|
882
887
|
|
|
883
888
|
# 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
|
-
|
|
889
|
+
|
|
890
|
+
@property
|
|
891
|
+
def preview(self) -> bool:
|
|
892
|
+
"""Whether to play the rendered movie (-p)."""
|
|
893
|
+
return self._d["preview"] or self._d["enable_gui"]
|
|
894
|
+
|
|
895
|
+
@preview.setter
|
|
896
|
+
def preview(self, value: bool) -> None:
|
|
897
|
+
self._set_boolean("preview", value)
|
|
898
|
+
|
|
899
|
+
@property
|
|
900
|
+
def show_in_file_browser(self) -> bool:
|
|
901
|
+
"""Whether to show the output file in the file browser (-f)."""
|
|
902
|
+
return self._d["show_in_file_browser"]
|
|
903
|
+
|
|
904
|
+
@show_in_file_browser.setter
|
|
905
|
+
def show_in_file_browser(self, value: bool) -> None:
|
|
906
|
+
self._set_boolean("show_in_file_browser", value)
|
|
907
|
+
|
|
908
|
+
@property
|
|
909
|
+
def progress_bar(self) -> str:
|
|
910
|
+
"""Whether to show progress bars while rendering animations."""
|
|
911
|
+
return self._d["progress_bar"]
|
|
912
|
+
|
|
913
|
+
@progress_bar.setter
|
|
914
|
+
def progress_bar(self, value: str) -> None:
|
|
915
|
+
self._set_from_list("progress_bar", value, ["none", "display", "leave"])
|
|
916
|
+
|
|
917
|
+
@property
|
|
918
|
+
def log_to_file(self) -> bool:
|
|
919
|
+
"""Whether to save logs to a file."""
|
|
920
|
+
return self._d["log_to_file"]
|
|
921
|
+
|
|
922
|
+
@log_to_file.setter
|
|
923
|
+
def log_to_file(self, value: bool) -> None:
|
|
924
|
+
self._set_boolean("log_to_file", value)
|
|
925
|
+
|
|
926
|
+
@property
|
|
927
|
+
def notify_outdated_version(self) -> bool:
|
|
928
|
+
"""Whether to notify if there is a version update available."""
|
|
929
|
+
return self._d["notify_outdated_version"]
|
|
930
|
+
|
|
931
|
+
@notify_outdated_version.setter
|
|
932
|
+
def notify_outdated_version(self, value: bool) -> None:
|
|
933
|
+
self._set_boolean("notify_outdated_version", value)
|
|
934
|
+
|
|
935
|
+
@property
|
|
936
|
+
def write_to_movie(self) -> bool:
|
|
937
|
+
"""Whether to render the scene to a movie file (-w)."""
|
|
938
|
+
return self._d["write_to_movie"]
|
|
939
|
+
|
|
940
|
+
@write_to_movie.setter
|
|
941
|
+
def write_to_movie(self, value: bool) -> None:
|
|
942
|
+
self._set_boolean("write_to_movie", value)
|
|
943
|
+
|
|
944
|
+
@property
|
|
945
|
+
def save_last_frame(self) -> bool:
|
|
946
|
+
"""Whether to save the last frame of the scene as an image file (-s)."""
|
|
947
|
+
return self._d["save_last_frame"]
|
|
948
|
+
|
|
949
|
+
@save_last_frame.setter
|
|
950
|
+
def save_last_frame(self, value: bool) -> None:
|
|
951
|
+
self._set_boolean("save_last_frame", value)
|
|
952
|
+
|
|
953
|
+
@property
|
|
954
|
+
def write_all(self) -> bool:
|
|
955
|
+
"""Whether to render all scenes in the input file (-a)."""
|
|
956
|
+
return self._d["write_all"]
|
|
957
|
+
|
|
958
|
+
@write_all.setter
|
|
959
|
+
def write_all(self, value: bool) -> None:
|
|
960
|
+
self._set_boolean("write_all", value)
|
|
961
|
+
|
|
962
|
+
@property
|
|
963
|
+
def save_pngs(self) -> bool:
|
|
964
|
+
"""Whether to save all frames in the scene as images files (-g)."""
|
|
965
|
+
return self._d["save_pngs"]
|
|
966
|
+
|
|
967
|
+
@save_pngs.setter
|
|
968
|
+
def save_pngs(self, value: bool) -> None:
|
|
969
|
+
self._set_boolean("save_pngs", value)
|
|
970
|
+
|
|
971
|
+
@property
|
|
972
|
+
def save_as_gif(self) -> bool:
|
|
973
|
+
"""Whether to save the rendered scene in .gif format (-i)."""
|
|
974
|
+
return self._d["save_as_gif"]
|
|
975
|
+
|
|
976
|
+
@save_as_gif.setter
|
|
977
|
+
def save_as_gif(self, value: bool) -> None:
|
|
978
|
+
self._set_boolean("save_as_gif", value)
|
|
979
|
+
|
|
980
|
+
@property
|
|
981
|
+
def save_sections(self) -> bool:
|
|
982
|
+
"""Whether to save single videos for each section in addition to the movie file."""
|
|
983
|
+
return self._d["save_sections"]
|
|
984
|
+
|
|
985
|
+
@save_sections.setter
|
|
986
|
+
def save_sections(self, value: bool) -> None:
|
|
987
|
+
self._set_boolean("save_sections", value)
|
|
988
|
+
|
|
989
|
+
@property
|
|
990
|
+
def enable_wireframe(self) -> bool:
|
|
991
|
+
"""Whether to enable wireframe debugging mode in opengl."""
|
|
992
|
+
return self._d["enable_wireframe"]
|
|
993
|
+
|
|
994
|
+
@enable_wireframe.setter
|
|
995
|
+
def enable_wireframe(self, value: bool) -> None:
|
|
996
|
+
self._set_boolean("enable_wireframe", value)
|
|
997
|
+
|
|
998
|
+
@property
|
|
999
|
+
def force_window(self) -> bool:
|
|
1000
|
+
"""Whether to force window when using the opengl renderer."""
|
|
1001
|
+
return self._d["force_window"]
|
|
1002
|
+
|
|
1003
|
+
@force_window.setter
|
|
1004
|
+
def force_window(self, value: bool) -> None:
|
|
1005
|
+
self._set_boolean("force_window", value)
|
|
1006
|
+
|
|
1007
|
+
@property
|
|
1008
|
+
def no_latex_cleanup(self) -> bool:
|
|
1009
|
+
"""Prevents deletion of .aux, .dvi, and .log files produced by Tex and MathTex."""
|
|
1010
|
+
return self._d["no_latex_cleanup"]
|
|
1011
|
+
|
|
1012
|
+
@no_latex_cleanup.setter
|
|
1013
|
+
def no_latex_cleanup(self, value: bool) -> None:
|
|
1014
|
+
self._set_boolean("no_latex_cleanup", value)
|
|
1015
|
+
|
|
1016
|
+
@property
|
|
1017
|
+
def preview_command(self) -> str:
|
|
1018
|
+
return self._d["preview_command"]
|
|
1019
|
+
|
|
1020
|
+
@preview_command.setter
|
|
1021
|
+
def preview_command(self, value: str) -> None:
|
|
1022
|
+
self._set_str("preview_command", value)
|
|
1023
|
+
|
|
1024
|
+
@property
|
|
1025
|
+
def verbosity(self) -> str:
|
|
974
1026
|
"""Logger verbosity; "DEBUG", "INFO", "WARNING", "ERROR", or "CRITICAL" (-v)."""
|
|
975
1027
|
return self._d["verbosity"]
|
|
976
1028
|
|
|
977
1029
|
@verbosity.setter
|
|
978
1030
|
def verbosity(self, val: str) -> None:
|
|
979
|
-
"""Verbosity level of the logger."""
|
|
980
1031
|
self._set_from_list(
|
|
981
1032
|
"verbosity",
|
|
982
1033
|
val,
|
|
983
1034
|
["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
984
1035
|
)
|
|
985
|
-
|
|
1036
|
+
logger.setLevel(val)
|
|
986
1037
|
|
|
987
1038
|
@property
|
|
988
|
-
def format(self):
|
|
1039
|
+
def format(self) -> str:
|
|
989
1040
|
"""File format; "png", "gif", "mp4", "webm" or "mov"."""
|
|
990
1041
|
return self._d["format"]
|
|
991
1042
|
|
|
992
1043
|
@format.setter
|
|
993
1044
|
def format(self, val: str) -> None:
|
|
994
|
-
"""File format the renderer will output."""
|
|
995
1045
|
self._set_from_list(
|
|
996
1046
|
"format",
|
|
997
1047
|
val,
|
|
998
1048
|
[None, "png", "gif", "mp4", "mov", "webm"],
|
|
999
1049
|
)
|
|
1050
|
+
self.resolve_movie_file_extension(self.transparent)
|
|
1000
1051
|
if self.format == "webm":
|
|
1001
|
-
|
|
1052
|
+
logger.warning(
|
|
1002
1053
|
"Output format set as webm, this can be slower than other formats",
|
|
1003
1054
|
)
|
|
1004
1055
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1056
|
+
@property
|
|
1057
|
+
def ffmpeg_loglevel(self) -> str:
|
|
1058
|
+
"""Verbosity level of ffmpeg (no flag)."""
|
|
1059
|
+
return self._d["ffmpeg_loglevel"]
|
|
1060
|
+
|
|
1061
|
+
@ffmpeg_loglevel.setter
|
|
1062
|
+
def ffmpeg_loglevel(self, val: str) -> None:
|
|
1063
|
+
self._set_from_list(
|
|
1008
1064
|
"ffmpeg_loglevel",
|
|
1009
1065
|
val,
|
|
1010
1066
|
["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
|
-
pixel_width
|
|
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
|
-
|
|
1179
|
-
|
|
1067
|
+
)
|
|
1068
|
+
logging.getLogger("libav").setLevel(self.ffmpeg_loglevel)
|
|
1069
|
+
|
|
1070
|
+
@property
|
|
1071
|
+
def media_embed(self) -> bool:
|
|
1072
|
+
"""Whether to embed videos in Jupyter notebook."""
|
|
1073
|
+
return self._d["media_embed"]
|
|
1074
|
+
|
|
1075
|
+
@media_embed.setter
|
|
1076
|
+
def media_embed(self, value: bool) -> None:
|
|
1077
|
+
self._set_boolean("media_embed", value)
|
|
1078
|
+
|
|
1079
|
+
@property
|
|
1080
|
+
def media_width(self) -> str:
|
|
1081
|
+
"""Media width in Jupyter notebook."""
|
|
1082
|
+
return self._d["media_width"]
|
|
1083
|
+
|
|
1084
|
+
@media_width.setter
|
|
1085
|
+
def media_width(self, value: str) -> None:
|
|
1086
|
+
self._set_str("media_width", value)
|
|
1087
|
+
|
|
1088
|
+
@property
|
|
1089
|
+
def pixel_width(self) -> int:
|
|
1090
|
+
"""Frame width in pixels (--resolution, -r)."""
|
|
1091
|
+
return self._d["pixel_width"]
|
|
1092
|
+
|
|
1093
|
+
@pixel_width.setter
|
|
1094
|
+
def pixel_width(self, value: int) -> None:
|
|
1095
|
+
self._set_pos_number("pixel_width", value, False)
|
|
1096
|
+
|
|
1097
|
+
@property
|
|
1098
|
+
def pixel_height(self) -> int:
|
|
1099
|
+
"""Frame height in pixels (--resolution, -r)."""
|
|
1100
|
+
return self._d["pixel_height"]
|
|
1101
|
+
|
|
1102
|
+
@pixel_height.setter
|
|
1103
|
+
def pixel_height(self, value: int) -> None:
|
|
1104
|
+
self._set_pos_number("pixel_height", value, False)
|
|
1105
|
+
|
|
1106
|
+
@property
|
|
1107
|
+
def aspect_ratio(self) -> int:
|
|
1108
|
+
"""Aspect ratio (width / height) in pixels (--resolution, -r)."""
|
|
1109
|
+
return self._d["pixel_width"] / self._d["pixel_height"]
|
|
1110
|
+
|
|
1111
|
+
@property
|
|
1112
|
+
def frame_height(self) -> float:
|
|
1113
|
+
"""Frame height in logical units (no flag)."""
|
|
1114
|
+
return self._d["frame_height"]
|
|
1115
|
+
|
|
1116
|
+
@frame_height.setter
|
|
1117
|
+
def frame_height(self, value: float) -> None:
|
|
1118
|
+
self._d.__setitem__("frame_height", value)
|
|
1119
|
+
|
|
1120
|
+
@property
|
|
1121
|
+
def frame_width(self) -> float:
|
|
1122
|
+
"""Frame width in logical units (no flag)."""
|
|
1123
|
+
return self._d["frame_width"]
|
|
1124
|
+
|
|
1125
|
+
@frame_width.setter
|
|
1126
|
+
def frame_width(self, value: float) -> None:
|
|
1127
|
+
self._d.__setitem__("frame_width", value)
|
|
1128
|
+
|
|
1129
|
+
@property
|
|
1130
|
+
def frame_y_radius(self) -> float:
|
|
1131
|
+
"""Half the frame height (no flag)."""
|
|
1132
|
+
return self._d["frame_height"] / 2
|
|
1133
|
+
|
|
1134
|
+
@frame_y_radius.setter
|
|
1135
|
+
def frame_y_radius(self, value: float) -> None:
|
|
1136
|
+
self._d.__setitem__("frame_y_radius", value) or self._d.__setitem__(
|
|
1137
|
+
"frame_height", 2 * value
|
|
1138
|
+
)
|
|
1139
|
+
|
|
1140
|
+
@property
|
|
1141
|
+
def frame_x_radius(self) -> float:
|
|
1142
|
+
"""Half the frame width (no flag)."""
|
|
1143
|
+
return self._d["frame_width"] / 2
|
|
1144
|
+
|
|
1145
|
+
@frame_x_radius.setter
|
|
1146
|
+
def frame_x_radius(self, value: float) -> None:
|
|
1147
|
+
self._d.__setitem__("frame_x_radius", value) or self._d.__setitem__(
|
|
1148
|
+
"frame_width", 2 * value
|
|
1149
|
+
)
|
|
1150
|
+
|
|
1151
|
+
@property
|
|
1152
|
+
def top(self) -> Vector3D:
|
|
1153
|
+
"""Coordinate at the center top of the frame."""
|
|
1154
|
+
return self.frame_y_radius * constants.UP
|
|
1155
|
+
|
|
1156
|
+
@property
|
|
1157
|
+
def bottom(self) -> Vector3D:
|
|
1158
|
+
"""Coordinate at the center bottom of the frame."""
|
|
1159
|
+
return self.frame_y_radius * constants.DOWN
|
|
1160
|
+
|
|
1161
|
+
@property
|
|
1162
|
+
def left_side(self) -> Vector3D:
|
|
1163
|
+
"""Coordinate at the middle left of the frame."""
|
|
1164
|
+
return self.frame_x_radius * constants.LEFT
|
|
1165
|
+
|
|
1166
|
+
@property
|
|
1167
|
+
def right_side(self) -> Vector3D:
|
|
1168
|
+
"""Coordinate at the middle right of the frame."""
|
|
1169
|
+
return self.frame_x_radius * constants.RIGHT
|
|
1170
|
+
|
|
1171
|
+
@property
|
|
1172
|
+
def frame_rate(self) -> float:
|
|
1173
|
+
"""Frame rate in frames per second."""
|
|
1174
|
+
return self._d["frame_rate"]
|
|
1175
|
+
|
|
1176
|
+
@frame_rate.setter
|
|
1177
|
+
def frame_rate(self, value: float) -> None:
|
|
1178
|
+
self._d.__setitem__("frame_rate", value)
|
|
1179
|
+
|
|
1180
|
+
# TODO: This was parsed before maybe add ManimColor(val), but results in circular import
|
|
1181
|
+
@property
|
|
1182
|
+
def background_color(self) -> ManimColor:
|
|
1183
|
+
"""Background color of the scene (-c)."""
|
|
1184
|
+
return self._d["background_color"]
|
|
1185
|
+
|
|
1186
|
+
@background_color.setter
|
|
1187
|
+
def background_color(self, value: Any) -> None:
|
|
1188
|
+
self._d.__setitem__("background_color", ManimColor(value))
|
|
1189
|
+
|
|
1190
|
+
@property
|
|
1191
|
+
def from_animation_number(self) -> int:
|
|
1192
|
+
"""Start rendering animations at this number (-n)."""
|
|
1193
|
+
return self._d["from_animation_number"]
|
|
1194
|
+
|
|
1195
|
+
@from_animation_number.setter
|
|
1196
|
+
def from_animation_number(self, value: int) -> None:
|
|
1197
|
+
self._d.__setitem__("from_animation_number", value)
|
|
1198
|
+
|
|
1199
|
+
@property
|
|
1200
|
+
def upto_animation_number(self) -> int:
|
|
1201
|
+
"""Stop rendering animations at this number. Use -1 to avoid skipping (-n)."""
|
|
1202
|
+
return self._d["upto_animation_number"]
|
|
1203
|
+
|
|
1204
|
+
@upto_animation_number.setter
|
|
1205
|
+
def upto_animation_number(self, value: int) -> None:
|
|
1206
|
+
self._set_pos_number("upto_animation_number", value, True)
|
|
1207
|
+
|
|
1208
|
+
@property
|
|
1209
|
+
def max_files_cached(self) -> int:
|
|
1210
|
+
"""Maximum number of files cached. Use -1 for infinity (no flag)."""
|
|
1211
|
+
return self._d["max_files_cached"]
|
|
1212
|
+
|
|
1213
|
+
@max_files_cached.setter
|
|
1214
|
+
def max_files_cached(self, value: int) -> None:
|
|
1215
|
+
self._set_pos_number("max_files_cached", value, True)
|
|
1216
|
+
|
|
1217
|
+
@property
|
|
1218
|
+
def window_monitor(self) -> int:
|
|
1219
|
+
"""The monitor on which the scene will be rendered."""
|
|
1220
|
+
return self._d["window_monitor"]
|
|
1221
|
+
|
|
1222
|
+
@window_monitor.setter
|
|
1223
|
+
def window_monitor(self, value: int) -> None:
|
|
1224
|
+
self._set_pos_number("window_monitor", value, True)
|
|
1225
|
+
|
|
1226
|
+
@property
|
|
1227
|
+
def flush_cache(self) -> bool:
|
|
1228
|
+
"""Whether to delete all the cached partial movie files."""
|
|
1229
|
+
return self._d["flush_cache"]
|
|
1230
|
+
|
|
1231
|
+
@flush_cache.setter
|
|
1232
|
+
def flush_cache(self, value: bool) -> None:
|
|
1233
|
+
self._set_boolean("flush_cache", value)
|
|
1234
|
+
|
|
1235
|
+
@property
|
|
1236
|
+
def disable_caching(self) -> bool:
|
|
1237
|
+
"""Whether to use scene caching."""
|
|
1238
|
+
return self._d["disable_caching"]
|
|
1239
|
+
|
|
1240
|
+
@disable_caching.setter
|
|
1241
|
+
def disable_caching(self, value: bool) -> None:
|
|
1242
|
+
self._set_boolean("disable_caching", value)
|
|
1243
|
+
|
|
1244
|
+
@property
|
|
1245
|
+
def disable_caching_warning(self) -> bool:
|
|
1246
|
+
"""Whether a warning is raised if there are too much submobjects to hash."""
|
|
1247
|
+
return self._d["disable_caching_warning"]
|
|
1248
|
+
|
|
1249
|
+
@disable_caching_warning.setter
|
|
1250
|
+
def disable_caching_warning(self, value: bool) -> None:
|
|
1251
|
+
self._set_boolean("disable_caching_warning", value)
|
|
1252
|
+
|
|
1253
|
+
@property
|
|
1254
|
+
def movie_file_extension(self) -> str:
|
|
1255
|
+
"""Either .mp4, .webm or .mov."""
|
|
1256
|
+
return self._d["movie_file_extension"]
|
|
1257
|
+
|
|
1258
|
+
@movie_file_extension.setter
|
|
1259
|
+
def movie_file_extension(self, value: str) -> None:
|
|
1260
|
+
self._set_from_list("movie_file_extension", value, [".mp4", ".mov", ".webm"])
|
|
1261
|
+
|
|
1262
|
+
@property
|
|
1263
|
+
def background_opacity(self) -> float:
|
|
1264
|
+
"""A number between 0.0 (fully transparent) and 1.0 (fully opaque)."""
|
|
1265
|
+
return self._d["background_opacity"]
|
|
1266
|
+
|
|
1267
|
+
@background_opacity.setter
|
|
1268
|
+
def background_opacity(self, value: float) -> None:
|
|
1269
|
+
self._set_between("background_opacity", value, 0, 1)
|
|
1270
|
+
if self.background_opacity < 1:
|
|
1271
|
+
self.resolve_movie_file_extension(is_transparent=True)
|
|
1272
|
+
|
|
1273
|
+
@property
|
|
1274
|
+
def frame_size(self) -> tuple[int, int]:
|
|
1275
|
+
"""Tuple with (pixel width, pixel height) (no flag)."""
|
|
1276
|
+
return (self._d["pixel_width"], self._d["pixel_height"])
|
|
1277
|
+
|
|
1278
|
+
@frame_size.setter
|
|
1279
|
+
def frame_size(self, value: tuple[int, int]) -> None:
|
|
1280
|
+
self._d.__setitem__("pixel_width", value[0]) or self._d.__setitem__(
|
|
1281
|
+
"pixel_height", value[1]
|
|
1282
|
+
)
|
|
1283
|
+
|
|
1284
|
+
@property
|
|
1285
|
+
def quality(self) -> str | None:
|
|
1180
1286
|
"""Video quality (-q)."""
|
|
1181
1287
|
keys = ["pixel_width", "pixel_height", "frame_rate"]
|
|
1182
1288
|
q = {k: self[k] for k in keys}
|
|
@@ -1186,27 +1292,27 @@ class ManimConfig(MutableMapping):
|
|
|
1186
1292
|
return None
|
|
1187
1293
|
|
|
1188
1294
|
@quality.setter
|
|
1189
|
-
def quality(self,
|
|
1190
|
-
if
|
|
1295
|
+
def quality(self, value: str | None) -> None:
|
|
1296
|
+
if value is None:
|
|
1191
1297
|
return
|
|
1192
|
-
if
|
|
1298
|
+
if value not in constants.QUALITIES:
|
|
1193
1299
|
raise KeyError(f"quality must be one of {list(constants.QUALITIES.keys())}")
|
|
1194
|
-
q = constants.QUALITIES[
|
|
1300
|
+
q = constants.QUALITIES[value]
|
|
1195
1301
|
self.frame_size = q["pixel_width"], q["pixel_height"]
|
|
1196
1302
|
self.frame_rate = q["frame_rate"]
|
|
1197
1303
|
|
|
1198
1304
|
@property
|
|
1199
|
-
def transparent(self):
|
|
1200
|
-
"""Whether the background opacity is
|
|
1201
|
-
return self._d["background_opacity"]
|
|
1305
|
+
def transparent(self) -> bool:
|
|
1306
|
+
"""Whether the background opacity is less than 1.0 (-t)."""
|
|
1307
|
+
return self._d["background_opacity"] < 1.0
|
|
1202
1308
|
|
|
1203
1309
|
@transparent.setter
|
|
1204
|
-
def transparent(self,
|
|
1205
|
-
self._d["background_opacity"] = float(not
|
|
1206
|
-
self.resolve_movie_file_extension(
|
|
1310
|
+
def transparent(self, value: bool) -> None:
|
|
1311
|
+
self._d["background_opacity"] = float(not value)
|
|
1312
|
+
self.resolve_movie_file_extension(value)
|
|
1207
1313
|
|
|
1208
1314
|
@property
|
|
1209
|
-
def dry_run(self):
|
|
1315
|
+
def dry_run(self) -> bool:
|
|
1210
1316
|
"""Whether dry run is enabled."""
|
|
1211
1317
|
return self._d["dry_run"]
|
|
1212
1318
|
|
|
@@ -1220,7 +1326,7 @@ class ManimConfig(MutableMapping):
|
|
|
1220
1326
|
self.format = None
|
|
1221
1327
|
|
|
1222
1328
|
@property
|
|
1223
|
-
def renderer(self):
|
|
1329
|
+
def renderer(self) -> RendererType:
|
|
1224
1330
|
"""The currently active renderer.
|
|
1225
1331
|
|
|
1226
1332
|
Populated with one of the available renderers in :class:`.RendererType`.
|
|
@@ -1246,15 +1352,15 @@ class ManimConfig(MutableMapping):
|
|
|
1246
1352
|
return self._d["renderer"]
|
|
1247
1353
|
|
|
1248
1354
|
@renderer.setter
|
|
1249
|
-
def renderer(self,
|
|
1355
|
+
def renderer(self, value: str | RendererType) -> None:
|
|
1250
1356
|
"""The setter of the renderer property.
|
|
1251
1357
|
|
|
1252
1358
|
Takes care of switching inheritance bases using the
|
|
1253
1359
|
:class:`.ConvertToOpenGL` metaclass.
|
|
1254
1360
|
"""
|
|
1255
|
-
if isinstance(
|
|
1256
|
-
|
|
1257
|
-
renderer = RendererType(
|
|
1361
|
+
if isinstance(value, str):
|
|
1362
|
+
value = value.lower()
|
|
1363
|
+
renderer = RendererType(value)
|
|
1258
1364
|
try:
|
|
1259
1365
|
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
|
1260
1366
|
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
|
|
@@ -1288,25 +1394,35 @@ class ManimConfig(MutableMapping):
|
|
|
1288
1394
|
|
|
1289
1395
|
self._set_from_enum("renderer", renderer, RendererType)
|
|
1290
1396
|
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
)
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1397
|
+
@property
|
|
1398
|
+
def media_dir(self) -> str:
|
|
1399
|
+
"""Main output directory. See :meth:`ManimConfig.get_dir`."""
|
|
1400
|
+
return self._d["media_dir"]
|
|
1401
|
+
|
|
1402
|
+
@media_dir.setter
|
|
1403
|
+
def media_dir(self, value: str | Path) -> None:
|
|
1404
|
+
self._set_dir("media_dir", value)
|
|
1405
|
+
|
|
1406
|
+
@property
|
|
1407
|
+
def window_position(self) -> str:
|
|
1408
|
+
"""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'."""
|
|
1409
|
+
return self._d["window_position"]
|
|
1410
|
+
|
|
1411
|
+
@window_position.setter
|
|
1412
|
+
def window_position(self, value: str) -> None:
|
|
1413
|
+
self._d.__setitem__("window_position", value)
|
|
1414
|
+
|
|
1415
|
+
@property
|
|
1416
|
+
def window_size(self) -> str:
|
|
1417
|
+
"""The size of the opengl window. 'default' to automatically scale the window based on the display monitor."""
|
|
1418
|
+
return self._d["window_size"]
|
|
1419
|
+
|
|
1420
|
+
@window_size.setter
|
|
1421
|
+
def window_size(self, value: str) -> None:
|
|
1422
|
+
self._d.__setitem__("window_size", value)
|
|
1423
|
+
|
|
1424
|
+
def resolve_movie_file_extension(self, is_transparent: bool) -> None:
|
|
1425
|
+
prev_file_extension = self.movie_file_extension
|
|
1310
1426
|
if is_transparent:
|
|
1311
1427
|
self.movie_file_extension = ".webm" if self.format == "webm" else ".mov"
|
|
1312
1428
|
elif self.format == "webm":
|
|
@@ -1315,44 +1431,67 @@ class ManimConfig(MutableMapping):
|
|
|
1315
1431
|
self.movie_file_extension = ".mov"
|
|
1316
1432
|
else:
|
|
1317
1433
|
self.movie_file_extension = ".mp4"
|
|
1434
|
+
if self.movie_file_extension != prev_file_extension:
|
|
1435
|
+
logger.warning(
|
|
1436
|
+
f"Output format changed to '{self.movie_file_extension}' "
|
|
1437
|
+
"to support transparency",
|
|
1438
|
+
)
|
|
1439
|
+
|
|
1440
|
+
@property
|
|
1441
|
+
def enable_gui(self) -> bool:
|
|
1442
|
+
"""Enable GUI interaction."""
|
|
1443
|
+
return self._d["enable_gui"]
|
|
1444
|
+
|
|
1445
|
+
@enable_gui.setter
|
|
1446
|
+
def enable_gui(self, value: bool) -> None:
|
|
1447
|
+
self._set_boolean("enable_gui", value)
|
|
1448
|
+
|
|
1449
|
+
@property
|
|
1450
|
+
def gui_location(self) -> tuple[Any]:
|
|
1451
|
+
"""Enable GUI interaction."""
|
|
1452
|
+
return self._d["gui_location"]
|
|
1453
|
+
|
|
1454
|
+
@gui_location.setter
|
|
1455
|
+
def gui_location(self, value: tuple[Any]) -> None:
|
|
1456
|
+
self._set_tuple("gui_location", value)
|
|
1318
1457
|
|
|
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
|
-
use_projection_stroke_shaders
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
)
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
def get_dir(self, key: str, **kwargs:
|
|
1458
|
+
@property
|
|
1459
|
+
def fullscreen(self) -> bool:
|
|
1460
|
+
"""Expand the window to its maximum possible size."""
|
|
1461
|
+
return self._d["fullscreen"]
|
|
1462
|
+
|
|
1463
|
+
@fullscreen.setter
|
|
1464
|
+
def fullscreen(self, value: bool) -> None:
|
|
1465
|
+
self._set_boolean("fullscreen", value)
|
|
1466
|
+
|
|
1467
|
+
@property
|
|
1468
|
+
def use_projection_fill_shaders(self) -> bool:
|
|
1469
|
+
"""Use shaders for OpenGLVMobject fill which are compatible with transformation matrices."""
|
|
1470
|
+
return self._d["use_projection_fill_shaders"]
|
|
1471
|
+
|
|
1472
|
+
@use_projection_fill_shaders.setter
|
|
1473
|
+
def use_projection_fill_shaders(self, value: bool) -> None:
|
|
1474
|
+
self._set_boolean("use_projection_fill_shaders", value)
|
|
1475
|
+
|
|
1476
|
+
@property
|
|
1477
|
+
def use_projection_stroke_shaders(self) -> bool:
|
|
1478
|
+
"""Use shaders for OpenGLVMobject stroke which are compatible with transformation matrices."""
|
|
1479
|
+
return self._d["use_projection_stroke_shaders"]
|
|
1480
|
+
|
|
1481
|
+
@use_projection_stroke_shaders.setter
|
|
1482
|
+
def use_projection_stroke_shaders(self, value: bool) -> None:
|
|
1483
|
+
self._set_boolean("use_projection_stroke_shaders", value)
|
|
1484
|
+
|
|
1485
|
+
@property
|
|
1486
|
+
def zero_pad(self) -> int:
|
|
1487
|
+
"""PNG zero padding. A number between 0 (no zero padding) and 9 (9 columns minimum)."""
|
|
1488
|
+
return self._d["zero_pad"]
|
|
1489
|
+
|
|
1490
|
+
@zero_pad.setter
|
|
1491
|
+
def zero_pad(self, value: int) -> None:
|
|
1492
|
+
self._set_int_between("zero_pad", value, 0, 9)
|
|
1493
|
+
|
|
1494
|
+
def get_dir(self, key: str, **kwargs: Any) -> Path:
|
|
1356
1495
|
"""Resolve a config option that stores a directory.
|
|
1357
1496
|
|
|
1358
1497
|
Config options that store directories may depend on one another. This
|
|
@@ -1503,110 +1642,146 @@ class ManimConfig(MutableMapping):
|
|
|
1503
1642
|
) from exc
|
|
1504
1643
|
return Path(path) if path else None
|
|
1505
1644
|
|
|
1506
|
-
def _set_dir(self, key: str, val: str | Path):
|
|
1645
|
+
def _set_dir(self, key: str, val: str | Path) -> None:
|
|
1507
1646
|
if isinstance(val, Path):
|
|
1508
1647
|
self._d.__setitem__(key, str(val))
|
|
1509
1648
|
else:
|
|
1510
1649
|
self._d.__setitem__(key, val)
|
|
1511
1650
|
|
|
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
|
|
1651
|
+
@property
|
|
1652
|
+
def assets_dir(self) -> str:
|
|
1653
|
+
"""Directory to locate video assets (no flag)."""
|
|
1654
|
+
return self._d["assets_dir"]
|
|
1655
|
+
|
|
1656
|
+
@assets_dir.setter
|
|
1657
|
+
def assets_dir(self, value: str | Path) -> None:
|
|
1658
|
+
self._set_dir("assets_dir", value)
|
|
1659
|
+
|
|
1660
|
+
@property
|
|
1661
|
+
def log_dir(self) -> str:
|
|
1662
|
+
"""Directory to place logs. See :meth:`ManimConfig.get_dir`."""
|
|
1663
|
+
return self._d["log_dir"]
|
|
1664
|
+
|
|
1665
|
+
@log_dir.setter
|
|
1666
|
+
def log_dir(self, value: str | Path) -> None:
|
|
1667
|
+
self._set_dir("log_dir", value)
|
|
1668
|
+
|
|
1669
|
+
@property
|
|
1670
|
+
def video_dir(self) -> str:
|
|
1671
|
+
"""Directory to place videos (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1672
|
+
return self._d["video_dir"]
|
|
1673
|
+
|
|
1674
|
+
@video_dir.setter
|
|
1675
|
+
def video_dir(self, value: str | Path) -> None:
|
|
1676
|
+
self._set_dir("video_dir", value)
|
|
1677
|
+
|
|
1678
|
+
@property
|
|
1679
|
+
def sections_dir(self) -> str:
|
|
1680
|
+
"""Directory to place section videos (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1681
|
+
return self._d["sections_dir"]
|
|
1682
|
+
|
|
1683
|
+
@sections_dir.setter
|
|
1684
|
+
def sections_dir(self, value: str | Path) -> None:
|
|
1685
|
+
self._set_dir("sections_dir", value)
|
|
1686
|
+
|
|
1687
|
+
@property
|
|
1688
|
+
def images_dir(self) -> str:
|
|
1689
|
+
"""Directory to place images (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1690
|
+
return self._d["images_dir"]
|
|
1691
|
+
|
|
1692
|
+
@images_dir.setter
|
|
1693
|
+
def images_dir(self, value: str | Path) -> None:
|
|
1694
|
+
self._set_dir("images_dir", value)
|
|
1695
|
+
|
|
1696
|
+
@property
|
|
1697
|
+
def text_dir(self) -> str:
|
|
1698
|
+
"""Directory to place text (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1699
|
+
return self._d["text_dir"]
|
|
1700
|
+
|
|
1701
|
+
@text_dir.setter
|
|
1702
|
+
def text_dir(self, value: str | Path) -> None:
|
|
1703
|
+
self._set_dir("text_dir", value)
|
|
1704
|
+
|
|
1705
|
+
@property
|
|
1706
|
+
def tex_dir(self) -> str:
|
|
1707
|
+
"""Directory to place tex (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1708
|
+
return self._d["tex_dir"]
|
|
1709
|
+
|
|
1710
|
+
@tex_dir.setter
|
|
1711
|
+
def tex_dir(self, value: str | Path) -> None:
|
|
1712
|
+
self._set_dir("tex_dir", value)
|
|
1713
|
+
|
|
1714
|
+
@property
|
|
1715
|
+
def partial_movie_dir(self) -> str:
|
|
1716
|
+
"""Directory to place partial movie files (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1717
|
+
return self._d["partial_movie_dir"]
|
|
1718
|
+
|
|
1719
|
+
@partial_movie_dir.setter
|
|
1720
|
+
def partial_movie_dir(self, value: str | Path) -> None:
|
|
1721
|
+
self._set_dir("partial_movie_dir", value)
|
|
1722
|
+
|
|
1723
|
+
@property
|
|
1724
|
+
def custom_folders(self) -> str:
|
|
1725
|
+
"""Whether to use custom folder output."""
|
|
1726
|
+
return self._d["custom_folders"]
|
|
1727
|
+
|
|
1728
|
+
@custom_folders.setter
|
|
1729
|
+
def custom_folders(self, value: str | Path) -> None:
|
|
1730
|
+
self._set_dir("custom_folders", value)
|
|
1731
|
+
|
|
1732
|
+
@property
|
|
1733
|
+
def input_file(self) -> str:
|
|
1734
|
+
"""Input file name."""
|
|
1735
|
+
return self._d["input_file"]
|
|
1736
|
+
|
|
1737
|
+
@input_file.setter
|
|
1738
|
+
def input_file(self, value: str | Path) -> None:
|
|
1739
|
+
self._set_dir("input_file", value)
|
|
1740
|
+
|
|
1741
|
+
@property
|
|
1742
|
+
def output_file(self) -> str:
|
|
1743
|
+
"""Output file name (-o)."""
|
|
1744
|
+
return self._d["output_file"]
|
|
1745
|
+
|
|
1746
|
+
@output_file.setter
|
|
1747
|
+
def output_file(self, value: str | Path) -> None:
|
|
1748
|
+
self._set_dir("output_file", value)
|
|
1749
|
+
|
|
1750
|
+
@property
|
|
1751
|
+
def scene_names(self) -> list[str]:
|
|
1752
|
+
"""Scenes to play from file."""
|
|
1753
|
+
return self._d["scene_names"]
|
|
1754
|
+
|
|
1755
|
+
@scene_names.setter
|
|
1756
|
+
def scene_names(self, value: list[str]) -> None:
|
|
1757
|
+
self._d.__setitem__("scene_names", value)
|
|
1758
|
+
|
|
1759
|
+
@property
|
|
1760
|
+
def tex_template(self) -> TexTemplate:
|
|
1586
1761
|
"""Template used when rendering Tex. See :class:`.TexTemplate`."""
|
|
1587
1762
|
if not hasattr(self, "_tex_template") or not self._tex_template:
|
|
1588
1763
|
fn = self._d["tex_template_file"]
|
|
1589
1764
|
if fn:
|
|
1590
|
-
self._tex_template =
|
|
1765
|
+
self._tex_template = TexTemplate.from_file(fn)
|
|
1591
1766
|
else:
|
|
1592
1767
|
self._tex_template = TexTemplate()
|
|
1593
1768
|
return self._tex_template
|
|
1594
1769
|
|
|
1595
1770
|
@tex_template.setter
|
|
1596
|
-
def tex_template(self, val:
|
|
1597
|
-
if isinstance(val,
|
|
1771
|
+
def tex_template(self, val: TexTemplate) -> None:
|
|
1772
|
+
if isinstance(val, TexTemplate):
|
|
1598
1773
|
self._tex_template = val
|
|
1599
1774
|
|
|
1600
1775
|
@property
|
|
1601
|
-
def tex_template_file(self):
|
|
1602
|
-
"""File to read Tex template from (no flag). See :class:`.
|
|
1776
|
+
def tex_template_file(self) -> Path:
|
|
1777
|
+
"""File to read Tex template from (no flag). See :class:`.TexTemplate`."""
|
|
1603
1778
|
return self._d["tex_template_file"]
|
|
1604
1779
|
|
|
1605
1780
|
@tex_template_file.setter
|
|
1606
1781
|
def tex_template_file(self, val: str) -> None:
|
|
1607
1782
|
if val:
|
|
1608
1783
|
if not os.access(val, os.R_OK):
|
|
1609
|
-
|
|
1784
|
+
logger.warning(
|
|
1610
1785
|
f"Custom TeX template {val} not found or not readable.",
|
|
1611
1786
|
)
|
|
1612
1787
|
else:
|
|
@@ -1615,17 +1790,19 @@ class ManimConfig(MutableMapping):
|
|
|
1615
1790
|
self._d["tex_template_file"] = val # actually set the falsy value
|
|
1616
1791
|
|
|
1617
1792
|
@property
|
|
1618
|
-
def plugins(self):
|
|
1793
|
+
def plugins(self) -> list[str]:
|
|
1619
1794
|
"""List of plugins to enable."""
|
|
1620
1795
|
return self._d["plugins"]
|
|
1621
1796
|
|
|
1622
1797
|
@plugins.setter
|
|
1623
|
-
def plugins(self, value):
|
|
1798
|
+
def plugins(self, value: list[str]):
|
|
1624
1799
|
self._d["plugins"] = value
|
|
1625
1800
|
|
|
1626
1801
|
|
|
1802
|
+
# TODO: to be used in the future - see PR #620
|
|
1803
|
+
# https://github.com/ManimCommunity/manim/pull/620
|
|
1627
1804
|
class ManimFrame(Mapping):
|
|
1628
|
-
_OPTS: set[str] = {
|
|
1805
|
+
_OPTS: ClassVar[set[str]] = {
|
|
1629
1806
|
"pixel_width",
|
|
1630
1807
|
"pixel_height",
|
|
1631
1808
|
"aspect_ratio",
|
|
@@ -1638,7 +1815,7 @@ class ManimFrame(Mapping):
|
|
|
1638
1815
|
"left_side",
|
|
1639
1816
|
"right_side",
|
|
1640
1817
|
}
|
|
1641
|
-
_CONSTANTS: dict[str,
|
|
1818
|
+
_CONSTANTS: ClassVar[dict[str, Vector3D]] = {
|
|
1642
1819
|
"UP": np.array((0.0, 1.0, 0.0)),
|
|
1643
1820
|
"DOWN": np.array((0.0, -1.0, 0.0)),
|
|
1644
1821
|
"RIGHT": np.array((1.0, 0.0, 0.0)),
|
|
@@ -1655,6 +1832,8 @@ class ManimFrame(Mapping):
|
|
|
1655
1832
|
"DR": np.array((1.0, -1.0, 0.0)),
|
|
1656
1833
|
}
|
|
1657
1834
|
|
|
1835
|
+
_c: ManimConfig
|
|
1836
|
+
|
|
1658
1837
|
def __init__(self, c: ManimConfig) -> None:
|
|
1659
1838
|
if not isinstance(c, ManimConfig):
|
|
1660
1839
|
raise TypeError("argument must be instance of 'ManimConfig'")
|
|
@@ -1671,20 +1850,20 @@ class ManimFrame(Mapping):
|
|
|
1671
1850
|
else:
|
|
1672
1851
|
raise KeyError(key)
|
|
1673
1852
|
|
|
1674
|
-
def __iter__(self) -> Iterable:
|
|
1853
|
+
def __iter__(self) -> Iterable[str]:
|
|
1675
1854
|
return iter(list(self._OPTS) + list(self._CONSTANTS))
|
|
1676
1855
|
|
|
1677
1856
|
def __len__(self) -> int:
|
|
1678
1857
|
return len(self._OPTS)
|
|
1679
1858
|
|
|
1680
1859
|
# make this truly immutable
|
|
1681
|
-
def __setattr__(self, attr, val) ->
|
|
1860
|
+
def __setattr__(self, attr: Any, val: Any) -> NoReturn:
|
|
1682
1861
|
raise TypeError("'ManimFrame' object does not support item assignment")
|
|
1683
1862
|
|
|
1684
|
-
def __setitem__(self, key, val) ->
|
|
1863
|
+
def __setitem__(self, key: Any, val: Any) -> NoReturn:
|
|
1685
1864
|
raise TypeError("'ManimFrame' object does not support item assignment")
|
|
1686
1865
|
|
|
1687
|
-
def __delitem__(self, key) ->
|
|
1866
|
+
def __delitem__(self, key: Any) -> NoReturn:
|
|
1688
1867
|
raise TypeError("'ManimFrame' object does not support item deletion")
|
|
1689
1868
|
|
|
1690
1869
|
|