manim 0.17.0__py3-none-any.whl → 0.19.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- manim/__init__.py +11 -6
- manim/__main__.py +62 -19
- manim/_config/__init__.py +10 -9
- manim/_config/cli_colors.py +26 -9
- manim/_config/default.cfg +1 -3
- manim/_config/logger_utils.py +23 -13
- manim/_config/utils.py +662 -468
- manim/animation/animation.py +164 -18
- manim/animation/changing.py +34 -23
- manim/animation/composition.py +265 -67
- manim/animation/creation.py +208 -26
- manim/animation/fading.py +16 -18
- manim/animation/growing.py +35 -15
- manim/animation/indication.py +150 -76
- manim/animation/movement.py +56 -22
- manim/animation/numbers.py +64 -6
- manim/animation/rotation.py +78 -7
- manim/animation/specialized.py +6 -7
- manim/animation/speedmodifier.py +13 -10
- manim/animation/transform.py +14 -11
- manim/animation/transform_matching_parts.py +3 -4
- manim/animation/updaters/mobject_update_utils.py +152 -30
- manim/animation/updaters/update.py +10 -7
- manim/camera/camera.py +182 -118
- manim/camera/mapping_camera.py +34 -3
- manim/camera/moving_camera.py +95 -74
- manim/camera/multi_camera.py +23 -15
- manim/camera/three_d_camera.py +70 -52
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +76 -44
- manim/cli/checkhealth/checks.py +192 -0
- manim/cli/checkhealth/commands.py +90 -0
- manim/cli/default_group.py +158 -25
- manim/cli/init/commands.py +33 -25
- manim/cli/plugins/commands.py +16 -3
- manim/cli/render/commands.py +72 -60
- manim/cli/render/ease_of_access_options.py +4 -3
- manim/cli/render/global_options.py +59 -17
- manim/cli/render/output_options.py +6 -5
- manim/cli/render/render_options.py +98 -33
- manim/constants.py +109 -59
- manim/data_structures.py +31 -0
- manim/mobject/frame.py +8 -5
- manim/mobject/geometry/__init__.py +1 -0
- manim/mobject/geometry/arc.py +277 -135
- manim/mobject/geometry/boolean_ops.py +32 -31
- manim/mobject/geometry/labeled.py +376 -0
- manim/mobject/geometry/line.py +192 -87
- manim/mobject/geometry/polygram.py +224 -58
- manim/mobject/geometry/shape_matchers.py +61 -25
- manim/mobject/geometry/tips.py +122 -48
- manim/mobject/graph.py +1027 -419
- manim/mobject/graphing/coordinate_systems.py +533 -278
- manim/mobject/graphing/functions.py +53 -32
- manim/mobject/graphing/number_line.py +123 -65
- manim/mobject/graphing/probability.py +88 -62
- manim/mobject/graphing/scale.py +33 -19
- manim/mobject/logo.py +118 -28
- manim/mobject/matrix.py +87 -83
- manim/mobject/mobject.py +912 -442
- manim/mobject/opengl/dot_cloud.py +16 -5
- manim/mobject/opengl/opengl_compatibility.py +4 -2
- manim/mobject/opengl/opengl_geometry.py +254 -153
- manim/mobject/opengl/opengl_image_mobject.py +3 -1
- manim/mobject/opengl/opengl_mobject.py +779 -482
- manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
- manim/mobject/opengl/opengl_surface.py +14 -92
- manim/mobject/opengl/opengl_three_dimensions.py +12 -8
- manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
- manim/mobject/svg/brace.py +173 -41
- manim/mobject/svg/svg_mobject.py +139 -53
- manim/mobject/table.py +61 -68
- manim/mobject/text/code_mobject.py +193 -539
- manim/mobject/text/numbers.py +81 -34
- manim/mobject/text/tex_mobject.py +130 -78
- manim/mobject/text/text_mobject.py +288 -164
- manim/mobject/three_d/polyhedra.py +111 -13
- manim/mobject/three_d/three_d_utils.py +17 -8
- manim/mobject/three_d/three_dimensions.py +239 -106
- manim/mobject/types/image_mobject.py +50 -30
- manim/mobject/types/point_cloud_mobject.py +120 -75
- manim/mobject/types/vectorized_mobject.py +841 -408
- manim/mobject/value_tracker.py +105 -38
- manim/mobject/vector_field.py +50 -31
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +14 -1
- manim/plugins/plugins_flags.py +10 -14
- manim/renderer/cairo_renderer.py +65 -50
- manim/renderer/opengl_renderer.py +89 -69
- manim/renderer/opengl_renderer_window.py +39 -18
- manim/renderer/shader.py +123 -87
- manim/renderer/shader_wrapper.py +44 -28
- manim/renderer/vectorized_mobject_rendering.py +38 -10
- manim/scene/moving_camera_scene.py +32 -3
- manim/scene/scene.py +507 -242
- manim/scene/scene_file_writer.py +371 -220
- manim/scene/section.py +20 -16
- manim/scene/three_d_scene.py +14 -22
- manim/scene/vector_space_scene.py +223 -129
- manim/scene/zoomed_scene.py +46 -41
- manim/typing.py +990 -0
- manim/utils/bezier.py +1823 -371
- manim/utils/caching.py +12 -5
- manim/utils/color/AS2700.py +236 -0
- manim/utils/color/BS381.py +318 -0
- manim/utils/color/DVIPSNAMES.py +96 -0
- manim/utils/color/SVGNAMES.py +179 -0
- manim/utils/color/X11.py +533 -0
- manim/utils/color/XKCD.py +952 -0
- manim/utils/color/__init__.py +61 -0
- manim/utils/color/core.py +1667 -0
- manim/utils/color/manim_colors.py +218 -0
- manim/utils/commands.py +48 -20
- manim/utils/config_ops.py +39 -19
- manim/utils/debug.py +8 -7
- manim/utils/deprecation.py +86 -39
- manim/utils/docbuild/__init__.py +17 -0
- manim/utils/docbuild/autoaliasattr_directive.py +236 -0
- manim/utils/docbuild/autocolor_directive.py +99 -0
- manim/utils/docbuild/manim_directive.py +94 -41
- manim/utils/docbuild/module_parsing.py +245 -0
- manim/utils/exceptions.py +6 -0
- manim/utils/family.py +5 -3
- manim/utils/family_ops.py +17 -4
- manim/utils/file_ops.py +27 -17
- manim/utils/hashing.py +55 -45
- manim/utils/images.py +13 -7
- manim/utils/ipython_magic.py +13 -7
- manim/utils/iterables.py +163 -120
- manim/utils/module_ops.py +66 -24
- manim/utils/opengl.py +77 -24
- manim/utils/parameter_parsing.py +32 -0
- manim/utils/paths.py +30 -33
- manim/utils/polylabel.py +235 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +98 -32
- manim/utils/simple_functions.py +25 -33
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +188 -115
- manim/utils/testing/__init__.py +17 -0
- manim/utils/testing/_frames_testers.py +13 -8
- manim/utils/testing/_show_diff.py +5 -3
- manim/utils/testing/_test_class_makers.py +34 -18
- manim/utils/testing/frames_comparison.py +37 -19
- manim/utils/tex.py +130 -198
- manim/utils/tex_file_writing.py +77 -47
- manim/utils/tex_templates.py +2 -1
- manim/utils/unit.py +6 -5
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
- manim-0.19.1.dist-info/RECORD +220 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
- manim-0.19.1.dist-info/entry_points.txt +3 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
- manim/cli/new/group.py +0 -189
- manim/communitycolors.py +0 -9
- manim/gui/__init__.py +0 -0
- manim/gui/gui.py +0 -82
- manim/plugins/import_plugins.py +0 -43
- manim/utils/color.py +0 -552
- manim-0.17.0.dist-info/RECORD +0 -206
- manim-0.17.0.dist-info/entry_points.txt +0 -4
- /manim/cli/{new → checkhealth}/__init__.py +0 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
manim/_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
|
-
import colour
|
|
27
27
|
import numpy as np
|
|
28
28
|
|
|
29
|
-
from
|
|
30
|
-
from
|
|
31
|
-
from
|
|
32
|
-
from
|
|
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]:
|
|
@@ -76,7 +87,7 @@ def config_file_paths() -> list[Path]:
|
|
|
76
87
|
|
|
77
88
|
|
|
78
89
|
def make_config_parser(
|
|
79
|
-
custom_file:
|
|
90
|
+
custom_file: StrPath | None = None,
|
|
80
91
|
) -> configparser.ConfigParser:
|
|
81
92
|
"""Make a :class:`ConfigParser` object and load any ``.cfg`` files.
|
|
82
93
|
|
|
@@ -111,10 +122,14 @@ def make_config_parser(
|
|
|
111
122
|
# read_file() before calling read() for any optional files."
|
|
112
123
|
# https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.read
|
|
113
124
|
parser = configparser.ConfigParser()
|
|
125
|
+
logger.info(f"Reading config file: {library_wide}")
|
|
114
126
|
with library_wide.open() as file:
|
|
115
127
|
parser.read_file(file) # necessary file
|
|
116
128
|
|
|
117
129
|
other_files = [user_wide, Path(custom_file) if custom_file else folder_wide]
|
|
130
|
+
for path in other_files:
|
|
131
|
+
if path.exists():
|
|
132
|
+
logger.info(f"Reading config file: {path}")
|
|
118
133
|
parser.read(other_files) # optional files
|
|
119
134
|
|
|
120
135
|
return parser
|
|
@@ -237,8 +252,7 @@ class ManimConfig(MutableMapping):
|
|
|
237
252
|
config.background_color = RED
|
|
238
253
|
|
|
239
254
|
|
|
240
|
-
class MyScene(Scene):
|
|
241
|
-
...
|
|
255
|
+
class MyScene(Scene): ...
|
|
242
256
|
|
|
243
257
|
the background color will be set to RED, regardless of the contents of
|
|
244
258
|
``manim.cfg`` or the CLI arguments used when invoking manim.
|
|
@@ -255,7 +269,6 @@ class ManimConfig(MutableMapping):
|
|
|
255
269
|
"dry_run",
|
|
256
270
|
"enable_wireframe",
|
|
257
271
|
"ffmpeg_loglevel",
|
|
258
|
-
"ffmpeg_executable",
|
|
259
272
|
"format",
|
|
260
273
|
"flush_cache",
|
|
261
274
|
"frame_height",
|
|
@@ -309,10 +322,12 @@ class ManimConfig(MutableMapping):
|
|
|
309
322
|
"write_to_movie",
|
|
310
323
|
"zero_pad",
|
|
311
324
|
"force_window",
|
|
325
|
+
"no_latex_cleanup",
|
|
326
|
+
"preview_command",
|
|
312
327
|
}
|
|
313
328
|
|
|
314
329
|
def __init__(self) -> None:
|
|
315
|
-
self._d
|
|
330
|
+
self._d: dict[str, Any | None] = dict.fromkeys(self._OPTS)
|
|
316
331
|
|
|
317
332
|
# behave like a dict
|
|
318
333
|
def __iter__(self) -> Iterator[str]:
|
|
@@ -321,20 +336,20 @@ class ManimConfig(MutableMapping):
|
|
|
321
336
|
def __len__(self) -> int:
|
|
322
337
|
return len(self._d)
|
|
323
338
|
|
|
324
|
-
def __contains__(self, key) -> bool:
|
|
339
|
+
def __contains__(self, key: object) -> bool:
|
|
325
340
|
try:
|
|
326
341
|
self.__getitem__(key)
|
|
327
342
|
return True
|
|
328
343
|
except AttributeError:
|
|
329
344
|
return False
|
|
330
345
|
|
|
331
|
-
def __getitem__(self, key) -> Any:
|
|
346
|
+
def __getitem__(self, key: str) -> Any:
|
|
332
347
|
return getattr(self, key)
|
|
333
348
|
|
|
334
349
|
def __setitem__(self, key: str, val: Any) -> None:
|
|
335
350
|
getattr(ManimConfig, key).fset(self, val) # fset is the property's setter
|
|
336
351
|
|
|
337
|
-
def update(self, obj: ManimConfig | dict) -> None:
|
|
352
|
+
def update(self, obj: ManimConfig | dict[str, Any]) -> None: # type: ignore[override]
|
|
338
353
|
"""Digest the options found in another :class:`ManimConfig` or in a dict.
|
|
339
354
|
|
|
340
355
|
Similar to :meth:`dict.update`, replaces the values of this object with
|
|
@@ -361,7 +376,6 @@ class ManimConfig(MutableMapping):
|
|
|
361
376
|
:meth:`~ManimConfig.digest_parser`
|
|
362
377
|
|
|
363
378
|
"""
|
|
364
|
-
|
|
365
379
|
if isinstance(obj, ManimConfig):
|
|
366
380
|
self._d.update(obj._d)
|
|
367
381
|
if obj.tex_template:
|
|
@@ -378,14 +392,14 @@ class ManimConfig(MutableMapping):
|
|
|
378
392
|
self[k] = v
|
|
379
393
|
|
|
380
394
|
# don't allow to delete anything
|
|
381
|
-
def __delitem__(self, key: str):
|
|
395
|
+
def __delitem__(self, key: str) -> NoReturn:
|
|
382
396
|
raise AttributeError("'ManimConfig' object does not support item deletion")
|
|
383
397
|
|
|
384
|
-
def __delattr__(self, key: str):
|
|
398
|
+
def __delattr__(self, key: str) -> NoReturn:
|
|
385
399
|
raise AttributeError("'ManimConfig' object does not support item deletion")
|
|
386
400
|
|
|
387
401
|
# copy functions
|
|
388
|
-
def copy(self) ->
|
|
402
|
+
def copy(self) -> Self:
|
|
389
403
|
"""Deepcopy the contents of this ManimConfig.
|
|
390
404
|
|
|
391
405
|
Returns
|
|
@@ -404,13 +418,13 @@ class ManimConfig(MutableMapping):
|
|
|
404
418
|
"""
|
|
405
419
|
return copy.deepcopy(self)
|
|
406
420
|
|
|
407
|
-
def __copy__(self) ->
|
|
421
|
+
def __copy__(self) -> Self:
|
|
408
422
|
"""See ManimConfig.copy()."""
|
|
409
423
|
return copy.deepcopy(self)
|
|
410
424
|
|
|
411
|
-
def __deepcopy__(self, memo: dict[str, Any]) ->
|
|
425
|
+
def __deepcopy__(self, memo: dict[str, Any]) -> Self:
|
|
412
426
|
"""See ManimConfig.copy()."""
|
|
413
|
-
c =
|
|
427
|
+
c = type(self)()
|
|
414
428
|
# Deepcopying the underlying dict is enough because all properties
|
|
415
429
|
# either read directly from it or compute their value on the fly from
|
|
416
430
|
# values read directly from it.
|
|
@@ -418,7 +432,7 @@ class ManimConfig(MutableMapping):
|
|
|
418
432
|
return c
|
|
419
433
|
|
|
420
434
|
# helper type-checking methods
|
|
421
|
-
def _set_from_list(self, key: str, val: Any, values: list) -> None:
|
|
435
|
+
def _set_from_list(self, key: str, val: Any, values: list[Any]) -> None:
|
|
422
436
|
"""Set ``key`` to ``val`` if ``val`` is contained in ``values``."""
|
|
423
437
|
if val in values:
|
|
424
438
|
self._d[key] = val
|
|
@@ -450,14 +464,14 @@ class ManimConfig(MutableMapping):
|
|
|
450
464
|
"""
|
|
451
465
|
self._d[key] = enum_class(enum_value)
|
|
452
466
|
|
|
453
|
-
def _set_boolean(self, key: str
|
|
467
|
+
def _set_boolean(self, key: str, val: Any) -> None:
|
|
454
468
|
"""Set ``key`` to ``val`` if ``val`` is Boolean."""
|
|
455
469
|
if val in [True, False]:
|
|
456
470
|
self._d[key] = val
|
|
457
471
|
else:
|
|
458
472
|
raise ValueError(f"{key} must be boolean")
|
|
459
473
|
|
|
460
|
-
def _set_tuple(self, key: str, val: tuple) -> None:
|
|
474
|
+
def _set_tuple(self, key: str, val: tuple[Any]) -> None:
|
|
461
475
|
if isinstance(val, tuple):
|
|
462
476
|
self._d[key] = val
|
|
463
477
|
else:
|
|
@@ -506,7 +520,7 @@ class ManimConfig(MutableMapping):
|
|
|
506
520
|
return rep
|
|
507
521
|
|
|
508
522
|
# builders
|
|
509
|
-
def digest_parser(self, parser: configparser.ConfigParser) ->
|
|
523
|
+
def digest_parser(self, parser: configparser.ConfigParser) -> Self:
|
|
510
524
|
"""Process the config options present in a :class:`ConfigParser` object.
|
|
511
525
|
|
|
512
526
|
This method processes arbitrary parsers, not only those read from a
|
|
@@ -580,6 +594,8 @@ class ManimConfig(MutableMapping):
|
|
|
580
594
|
"use_projection_stroke_shaders",
|
|
581
595
|
"enable_wireframe",
|
|
582
596
|
"force_window",
|
|
597
|
+
"no_latex_cleanup",
|
|
598
|
+
"dry_run",
|
|
583
599
|
]:
|
|
584
600
|
setattr(self, key, parser["CLI"].getboolean(key, fallback=False))
|
|
585
601
|
|
|
@@ -614,6 +630,7 @@ class ManimConfig(MutableMapping):
|
|
|
614
630
|
"background_color",
|
|
615
631
|
"renderer",
|
|
616
632
|
"window_position",
|
|
633
|
+
"preview_command",
|
|
617
634
|
]:
|
|
618
635
|
setattr(self, key, parser["CLI"].get(key, fallback="", raw=True))
|
|
619
636
|
|
|
@@ -631,17 +648,19 @@ class ManimConfig(MutableMapping):
|
|
|
631
648
|
gui_location = tuple(
|
|
632
649
|
map(int, re.split(r"[;,\-]", parser["CLI"]["gui_location"])),
|
|
633
650
|
)
|
|
634
|
-
|
|
651
|
+
self.gui_location = gui_location
|
|
635
652
|
|
|
636
653
|
window_size = parser["CLI"][
|
|
637
654
|
"window_size"
|
|
638
655
|
] # if not "default", get a tuple of the position
|
|
639
656
|
if window_size != "default":
|
|
640
657
|
window_size = tuple(map(int, re.split(r"[;,\-]", window_size)))
|
|
641
|
-
|
|
658
|
+
self.window_size = window_size
|
|
642
659
|
|
|
643
660
|
# plugins
|
|
644
|
-
|
|
661
|
+
plugins = parser["CLI"].get("plugins", fallback="", raw=True)
|
|
662
|
+
plugins = [] if plugins == "" else plugins.split(",")
|
|
663
|
+
self.plugins = plugins
|
|
645
664
|
# the next two must be set AFTER digesting pixel_width and pixel_height
|
|
646
665
|
self["frame_height"] = parser["CLI"].getfloat("frame_height", 8.0)
|
|
647
666
|
width = parser["CLI"].getfloat("frame_width", None)
|
|
@@ -657,25 +676,21 @@ class ManimConfig(MutableMapping):
|
|
|
657
676
|
|
|
658
677
|
val = parser["CLI"].get("progress_bar")
|
|
659
678
|
if val:
|
|
660
|
-
|
|
679
|
+
self.progress_bar = val
|
|
661
680
|
|
|
662
681
|
val = parser["ffmpeg"].get("loglevel")
|
|
663
682
|
if val:
|
|
664
683
|
self.ffmpeg_loglevel = val
|
|
665
684
|
|
|
666
|
-
# TODO: Fix the mess above and below
|
|
667
|
-
val = parser["ffmpeg"].get("ffmpeg_executable")
|
|
668
|
-
setattr(self, "ffmpeg_executable", val)
|
|
669
|
-
|
|
670
685
|
try:
|
|
671
686
|
val = parser["jupyter"].getboolean("media_embed")
|
|
672
687
|
except ValueError:
|
|
673
688
|
val = None
|
|
674
|
-
|
|
689
|
+
self.media_embed = val
|
|
675
690
|
|
|
676
691
|
val = parser["jupyter"].get("media_width")
|
|
677
692
|
if val:
|
|
678
|
-
|
|
693
|
+
self.media_width = val
|
|
679
694
|
|
|
680
695
|
val = parser["CLI"].get("quality", fallback="", raw=True)
|
|
681
696
|
if val:
|
|
@@ -683,7 +698,7 @@ class ManimConfig(MutableMapping):
|
|
|
683
698
|
|
|
684
699
|
return self
|
|
685
700
|
|
|
686
|
-
def digest_args(self, args: argparse.Namespace) ->
|
|
701
|
+
def digest_args(self, args: argparse.Namespace) -> Self:
|
|
687
702
|
"""Process the config options present in CLI arguments.
|
|
688
703
|
|
|
689
704
|
Parameters
|
|
@@ -756,6 +771,8 @@ class ManimConfig(MutableMapping):
|
|
|
756
771
|
"enable_wireframe",
|
|
757
772
|
"force_window",
|
|
758
773
|
"dry_run",
|
|
774
|
+
"no_latex_cleanup",
|
|
775
|
+
"preview_command",
|
|
759
776
|
]:
|
|
760
777
|
if hasattr(args, key):
|
|
761
778
|
attr = getattr(args, key)
|
|
@@ -786,7 +803,7 @@ class ManimConfig(MutableMapping):
|
|
|
786
803
|
try:
|
|
787
804
|
self.upto_animation_number = nflag[1]
|
|
788
805
|
except Exception:
|
|
789
|
-
|
|
806
|
+
logger.info(
|
|
790
807
|
f"No end scene number specified in -n option. Rendering from {nflag[0]} onwards...",
|
|
791
808
|
)
|
|
792
809
|
|
|
@@ -822,22 +839,19 @@ class ManimConfig(MutableMapping):
|
|
|
822
839
|
|
|
823
840
|
# Handle --tex_template
|
|
824
841
|
if args.tex_template:
|
|
825
|
-
self.tex_template =
|
|
842
|
+
self.tex_template = TexTemplate.from_file(args.tex_template)
|
|
826
843
|
|
|
827
|
-
if
|
|
828
|
-
self.renderer == RendererType.OPENGL
|
|
829
|
-
and getattr(args, "write_to_movie") is None
|
|
830
|
-
):
|
|
844
|
+
if self.renderer == RendererType.OPENGL and args.write_to_movie is None:
|
|
831
845
|
# --write_to_movie was not passed on the command line, so don't generate video.
|
|
832
846
|
self["write_to_movie"] = False
|
|
833
847
|
|
|
834
848
|
# Handle --gui_location flag.
|
|
835
|
-
if
|
|
849
|
+
if args.gui_location is not None:
|
|
836
850
|
self.gui_location = args.gui_location
|
|
837
851
|
|
|
838
852
|
return self
|
|
839
853
|
|
|
840
|
-
def digest_file(self, filename:
|
|
854
|
+
def digest_file(self, filename: StrPath) -> Self:
|
|
841
855
|
"""Process the config options present in a ``.cfg`` file.
|
|
842
856
|
|
|
843
857
|
This method processes a single ``.cfg`` file, whereas
|
|
@@ -878,326 +892,433 @@ class ManimConfig(MutableMapping):
|
|
|
878
892
|
return self.digest_parser(make_config_parser(filename))
|
|
879
893
|
|
|
880
894
|
# config options are properties
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
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
|
-
@property
|
|
964
|
-
def
|
|
895
|
+
|
|
896
|
+
@property
|
|
897
|
+
def preview(self) -> bool:
|
|
898
|
+
"""Whether to play the rendered movie (-p)."""
|
|
899
|
+
return self._d["preview"] or self._d["enable_gui"]
|
|
900
|
+
|
|
901
|
+
@preview.setter
|
|
902
|
+
def preview(self, value: bool) -> None:
|
|
903
|
+
self._set_boolean("preview", value)
|
|
904
|
+
|
|
905
|
+
@property
|
|
906
|
+
def show_in_file_browser(self) -> bool:
|
|
907
|
+
"""Whether to show the output file in the file browser (-f)."""
|
|
908
|
+
return self._d["show_in_file_browser"]
|
|
909
|
+
|
|
910
|
+
@show_in_file_browser.setter
|
|
911
|
+
def show_in_file_browser(self, value: bool) -> None:
|
|
912
|
+
self._set_boolean("show_in_file_browser", value)
|
|
913
|
+
|
|
914
|
+
@property
|
|
915
|
+
def progress_bar(self) -> str:
|
|
916
|
+
"""Whether to show progress bars while rendering animations."""
|
|
917
|
+
return self._d["progress_bar"]
|
|
918
|
+
|
|
919
|
+
@progress_bar.setter
|
|
920
|
+
def progress_bar(self, value: str) -> None:
|
|
921
|
+
self._set_from_list("progress_bar", value, ["none", "display", "leave"])
|
|
922
|
+
|
|
923
|
+
@property
|
|
924
|
+
def log_to_file(self) -> bool:
|
|
925
|
+
"""Whether to save logs to a file."""
|
|
926
|
+
return self._d["log_to_file"]
|
|
927
|
+
|
|
928
|
+
@log_to_file.setter
|
|
929
|
+
def log_to_file(self, value: bool) -> None:
|
|
930
|
+
self._set_boolean("log_to_file", value)
|
|
931
|
+
|
|
932
|
+
@property
|
|
933
|
+
def notify_outdated_version(self) -> bool:
|
|
934
|
+
"""Whether to notify if there is a version update available."""
|
|
935
|
+
return self._d["notify_outdated_version"]
|
|
936
|
+
|
|
937
|
+
@notify_outdated_version.setter
|
|
938
|
+
def notify_outdated_version(self, value: bool) -> None:
|
|
939
|
+
self._set_boolean("notify_outdated_version", value)
|
|
940
|
+
|
|
941
|
+
@property
|
|
942
|
+
def write_to_movie(self) -> bool:
|
|
943
|
+
"""Whether to render the scene to a movie file (-w)."""
|
|
944
|
+
return self._d["write_to_movie"]
|
|
945
|
+
|
|
946
|
+
@write_to_movie.setter
|
|
947
|
+
def write_to_movie(self, value: bool) -> None:
|
|
948
|
+
self._set_boolean("write_to_movie", value)
|
|
949
|
+
|
|
950
|
+
@property
|
|
951
|
+
def save_last_frame(self) -> bool:
|
|
952
|
+
"""Whether to save the last frame of the scene as an image file (-s)."""
|
|
953
|
+
return self._d["save_last_frame"]
|
|
954
|
+
|
|
955
|
+
@save_last_frame.setter
|
|
956
|
+
def save_last_frame(self, value: bool) -> None:
|
|
957
|
+
self._set_boolean("save_last_frame", value)
|
|
958
|
+
|
|
959
|
+
@property
|
|
960
|
+
def write_all(self) -> bool:
|
|
961
|
+
"""Whether to render all scenes in the input file (-a)."""
|
|
962
|
+
return self._d["write_all"]
|
|
963
|
+
|
|
964
|
+
@write_all.setter
|
|
965
|
+
def write_all(self, value: bool) -> None:
|
|
966
|
+
self._set_boolean("write_all", value)
|
|
967
|
+
|
|
968
|
+
@property
|
|
969
|
+
def save_pngs(self) -> bool:
|
|
970
|
+
"""Whether to save all frames in the scene as images files (-g)."""
|
|
971
|
+
return self._d["save_pngs"]
|
|
972
|
+
|
|
973
|
+
@save_pngs.setter
|
|
974
|
+
def save_pngs(self, value: bool) -> None:
|
|
975
|
+
self._set_boolean("save_pngs", value)
|
|
976
|
+
|
|
977
|
+
@property
|
|
978
|
+
def save_as_gif(self) -> bool:
|
|
979
|
+
"""Whether to save the rendered scene in .gif format (-i)."""
|
|
980
|
+
return self._d["save_as_gif"]
|
|
981
|
+
|
|
982
|
+
@save_as_gif.setter
|
|
983
|
+
def save_as_gif(self, value: bool) -> None:
|
|
984
|
+
self._set_boolean("save_as_gif", value)
|
|
985
|
+
|
|
986
|
+
@property
|
|
987
|
+
def save_sections(self) -> bool:
|
|
988
|
+
"""Whether to save single videos for each section in addition to the movie file."""
|
|
989
|
+
return self._d["save_sections"]
|
|
990
|
+
|
|
991
|
+
@save_sections.setter
|
|
992
|
+
def save_sections(self, value: bool) -> None:
|
|
993
|
+
self._set_boolean("save_sections", value)
|
|
994
|
+
|
|
995
|
+
@property
|
|
996
|
+
def enable_wireframe(self) -> bool:
|
|
997
|
+
"""Whether to enable wireframe debugging mode in opengl."""
|
|
998
|
+
return self._d["enable_wireframe"]
|
|
999
|
+
|
|
1000
|
+
@enable_wireframe.setter
|
|
1001
|
+
def enable_wireframe(self, value: bool) -> None:
|
|
1002
|
+
self._set_boolean("enable_wireframe", value)
|
|
1003
|
+
|
|
1004
|
+
@property
|
|
1005
|
+
def force_window(self) -> bool:
|
|
1006
|
+
"""Whether to force window when using the opengl renderer."""
|
|
1007
|
+
return self._d["force_window"]
|
|
1008
|
+
|
|
1009
|
+
@force_window.setter
|
|
1010
|
+
def force_window(self, value: bool) -> None:
|
|
1011
|
+
self._set_boolean("force_window", value)
|
|
1012
|
+
|
|
1013
|
+
@property
|
|
1014
|
+
def no_latex_cleanup(self) -> bool:
|
|
1015
|
+
"""Prevents deletion of .aux, .dvi, and .log files produced by Tex and MathTex."""
|
|
1016
|
+
return self._d["no_latex_cleanup"]
|
|
1017
|
+
|
|
1018
|
+
@no_latex_cleanup.setter
|
|
1019
|
+
def no_latex_cleanup(self, value: bool) -> None:
|
|
1020
|
+
self._set_boolean("no_latex_cleanup", value)
|
|
1021
|
+
|
|
1022
|
+
@property
|
|
1023
|
+
def preview_command(self) -> str:
|
|
1024
|
+
return self._d["preview_command"]
|
|
1025
|
+
|
|
1026
|
+
@preview_command.setter
|
|
1027
|
+
def preview_command(self, value: str) -> None:
|
|
1028
|
+
self._set_str("preview_command", value)
|
|
1029
|
+
|
|
1030
|
+
@property
|
|
1031
|
+
def verbosity(self) -> str:
|
|
965
1032
|
"""Logger verbosity; "DEBUG", "INFO", "WARNING", "ERROR", or "CRITICAL" (-v)."""
|
|
966
1033
|
return self._d["verbosity"]
|
|
967
1034
|
|
|
968
1035
|
@verbosity.setter
|
|
969
1036
|
def verbosity(self, val: str) -> None:
|
|
970
|
-
"""Verbosity level of the logger."""
|
|
971
1037
|
self._set_from_list(
|
|
972
1038
|
"verbosity",
|
|
973
1039
|
val,
|
|
974
1040
|
["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
975
1041
|
)
|
|
976
|
-
|
|
1042
|
+
logger.setLevel(val)
|
|
977
1043
|
|
|
978
1044
|
@property
|
|
979
|
-
def format(self):
|
|
1045
|
+
def format(self) -> str:
|
|
980
1046
|
"""File format; "png", "gif", "mp4", "webm" or "mov"."""
|
|
981
1047
|
return self._d["format"]
|
|
982
1048
|
|
|
983
1049
|
@format.setter
|
|
984
1050
|
def format(self, val: str) -> None:
|
|
985
|
-
"""File format the renderer will output."""
|
|
986
1051
|
self._set_from_list(
|
|
987
1052
|
"format",
|
|
988
1053
|
val,
|
|
989
1054
|
[None, "png", "gif", "mp4", "mov", "webm"],
|
|
990
1055
|
)
|
|
1056
|
+
self.resolve_movie_file_extension(self.transparent)
|
|
991
1057
|
if self.format == "webm":
|
|
992
|
-
|
|
1058
|
+
logger.warning(
|
|
993
1059
|
"Output format set as webm, this can be slower than other formats",
|
|
994
1060
|
)
|
|
995
1061
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1062
|
+
@property
|
|
1063
|
+
def ffmpeg_loglevel(self) -> str:
|
|
1064
|
+
"""Verbosity level of ffmpeg (no flag)."""
|
|
1065
|
+
return self._d["ffmpeg_loglevel"]
|
|
1066
|
+
|
|
1067
|
+
@ffmpeg_loglevel.setter
|
|
1068
|
+
def ffmpeg_loglevel(self, val: str) -> None:
|
|
1069
|
+
self._set_from_list(
|
|
999
1070
|
"ffmpeg_loglevel",
|
|
1000
1071
|
val,
|
|
1001
1072
|
["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
1002
|
-
)
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
pixel_width
|
|
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
|
-
|
|
1073
|
+
)
|
|
1074
|
+
logging.getLogger("libav").setLevel(self.ffmpeg_loglevel)
|
|
1075
|
+
|
|
1076
|
+
@property
|
|
1077
|
+
def media_embed(self) -> bool:
|
|
1078
|
+
"""Whether to embed videos in Jupyter notebook."""
|
|
1079
|
+
return self._d["media_embed"]
|
|
1080
|
+
|
|
1081
|
+
@media_embed.setter
|
|
1082
|
+
def media_embed(self, value: bool) -> None:
|
|
1083
|
+
self._set_boolean("media_embed", value)
|
|
1084
|
+
|
|
1085
|
+
@property
|
|
1086
|
+
def media_width(self) -> str:
|
|
1087
|
+
"""Media width in Jupyter notebook."""
|
|
1088
|
+
return self._d["media_width"]
|
|
1089
|
+
|
|
1090
|
+
@media_width.setter
|
|
1091
|
+
def media_width(self, value: str) -> None:
|
|
1092
|
+
self._set_str("media_width", value)
|
|
1093
|
+
|
|
1094
|
+
@property
|
|
1095
|
+
def pixel_width(self) -> int:
|
|
1096
|
+
"""Frame width in pixels (--resolution, -r)."""
|
|
1097
|
+
return self._d["pixel_width"]
|
|
1098
|
+
|
|
1099
|
+
@pixel_width.setter
|
|
1100
|
+
def pixel_width(self, value: int) -> None:
|
|
1101
|
+
self._set_pos_number("pixel_width", value, False)
|
|
1102
|
+
|
|
1103
|
+
@property
|
|
1104
|
+
def pixel_height(self) -> int:
|
|
1105
|
+
"""Frame height in pixels (--resolution, -r)."""
|
|
1106
|
+
return self._d["pixel_height"]
|
|
1107
|
+
|
|
1108
|
+
@pixel_height.setter
|
|
1109
|
+
def pixel_height(self, value: int) -> None:
|
|
1110
|
+
self._set_pos_number("pixel_height", value, False)
|
|
1111
|
+
|
|
1112
|
+
@property
|
|
1113
|
+
def aspect_ratio(self) -> int:
|
|
1114
|
+
"""Aspect ratio (width / height) in pixels (--resolution, -r)."""
|
|
1115
|
+
return self._d["pixel_width"] / self._d["pixel_height"]
|
|
1116
|
+
|
|
1117
|
+
@property
|
|
1118
|
+
def frame_height(self) -> float:
|
|
1119
|
+
"""Frame height in logical units (no flag)."""
|
|
1120
|
+
return self._d["frame_height"]
|
|
1121
|
+
|
|
1122
|
+
@frame_height.setter
|
|
1123
|
+
def frame_height(self, value: float) -> None:
|
|
1124
|
+
self._d.__setitem__("frame_height", value)
|
|
1125
|
+
|
|
1126
|
+
@property
|
|
1127
|
+
def frame_width(self) -> float:
|
|
1128
|
+
"""Frame width in logical units (no flag)."""
|
|
1129
|
+
return self._d["frame_width"]
|
|
1130
|
+
|
|
1131
|
+
@frame_width.setter
|
|
1132
|
+
def frame_width(self, value: float) -> None:
|
|
1133
|
+
self._d.__setitem__("frame_width", value)
|
|
1134
|
+
|
|
1135
|
+
@property
|
|
1136
|
+
def frame_y_radius(self) -> float:
|
|
1137
|
+
"""Half the frame height (no flag)."""
|
|
1138
|
+
return self._d["frame_height"] / 2
|
|
1139
|
+
|
|
1140
|
+
@frame_y_radius.setter
|
|
1141
|
+
def frame_y_radius(self, value: float) -> None:
|
|
1142
|
+
self._d.__setitem__("frame_y_radius", value) or self._d.__setitem__(
|
|
1143
|
+
"frame_height", 2 * value
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
@property
|
|
1147
|
+
def frame_x_radius(self) -> float:
|
|
1148
|
+
"""Half the frame width (no flag)."""
|
|
1149
|
+
return self._d["frame_width"] / 2
|
|
1150
|
+
|
|
1151
|
+
@frame_x_radius.setter
|
|
1152
|
+
def frame_x_radius(self, value: float) -> None:
|
|
1153
|
+
self._d.__setitem__("frame_x_radius", value) or self._d.__setitem__(
|
|
1154
|
+
"frame_width", 2 * value
|
|
1155
|
+
)
|
|
1156
|
+
|
|
1157
|
+
@property
|
|
1158
|
+
def top(self) -> Vector3D:
|
|
1159
|
+
"""Coordinate at the center top of the frame."""
|
|
1160
|
+
return self.frame_y_radius * constants.UP
|
|
1161
|
+
|
|
1162
|
+
@property
|
|
1163
|
+
def bottom(self) -> Vector3D:
|
|
1164
|
+
"""Coordinate at the center bottom of the frame."""
|
|
1165
|
+
return self.frame_y_radius * constants.DOWN
|
|
1166
|
+
|
|
1167
|
+
@property
|
|
1168
|
+
def left_side(self) -> Vector3D:
|
|
1169
|
+
"""Coordinate at the middle left of the frame."""
|
|
1170
|
+
return self.frame_x_radius * constants.LEFT
|
|
1171
|
+
|
|
1172
|
+
@property
|
|
1173
|
+
def right_side(self) -> Vector3D:
|
|
1174
|
+
"""Coordinate at the middle right of the frame."""
|
|
1175
|
+
return self.frame_x_radius * constants.RIGHT
|
|
1176
|
+
|
|
1177
|
+
@property
|
|
1178
|
+
def frame_rate(self) -> float:
|
|
1179
|
+
"""Frame rate in frames per second."""
|
|
1180
|
+
return self._d["frame_rate"]
|
|
1181
|
+
|
|
1182
|
+
@frame_rate.setter
|
|
1183
|
+
def frame_rate(self, value: float) -> None:
|
|
1184
|
+
self._d.__setitem__("frame_rate", value)
|
|
1185
|
+
|
|
1186
|
+
# TODO: This was parsed before maybe add ManimColor(val), but results in circular import
|
|
1187
|
+
@property
|
|
1188
|
+
def background_color(self) -> ManimColor:
|
|
1189
|
+
"""Background color of the scene (-c)."""
|
|
1190
|
+
return self._d["background_color"]
|
|
1191
|
+
|
|
1192
|
+
@background_color.setter
|
|
1193
|
+
def background_color(self, value: Any) -> None:
|
|
1194
|
+
self._d.__setitem__("background_color", ManimColor(value))
|
|
1195
|
+
|
|
1196
|
+
@property
|
|
1197
|
+
def from_animation_number(self) -> int:
|
|
1198
|
+
"""Start rendering animations at this number (-n)."""
|
|
1199
|
+
return self._d["from_animation_number"]
|
|
1200
|
+
|
|
1201
|
+
@from_animation_number.setter
|
|
1202
|
+
def from_animation_number(self, value: int) -> None:
|
|
1203
|
+
self._d.__setitem__("from_animation_number", value)
|
|
1204
|
+
|
|
1205
|
+
@property
|
|
1206
|
+
def upto_animation_number(self) -> int:
|
|
1207
|
+
"""Stop rendering animations at this number. Use -1 to avoid skipping (-n)."""
|
|
1208
|
+
return self._d["upto_animation_number"]
|
|
1209
|
+
|
|
1210
|
+
@upto_animation_number.setter
|
|
1211
|
+
def upto_animation_number(self, value: int) -> None:
|
|
1212
|
+
self._set_pos_number("upto_animation_number", value, True)
|
|
1213
|
+
|
|
1214
|
+
@property
|
|
1215
|
+
def max_files_cached(self) -> int:
|
|
1216
|
+
"""Maximum number of files cached. Use -1 for infinity (no flag)."""
|
|
1217
|
+
return self._d["max_files_cached"]
|
|
1218
|
+
|
|
1219
|
+
@max_files_cached.setter
|
|
1220
|
+
def max_files_cached(self, value: int) -> None:
|
|
1221
|
+
self._set_pos_number("max_files_cached", value, True)
|
|
1222
|
+
|
|
1223
|
+
@property
|
|
1224
|
+
def window_monitor(self) -> int:
|
|
1225
|
+
"""The monitor on which the scene will be rendered."""
|
|
1226
|
+
return self._d["window_monitor"]
|
|
1227
|
+
|
|
1228
|
+
@window_monitor.setter
|
|
1229
|
+
def window_monitor(self, value: int) -> None:
|
|
1230
|
+
self._set_pos_number("window_monitor", value, True)
|
|
1231
|
+
|
|
1232
|
+
@property
|
|
1233
|
+
def flush_cache(self) -> bool:
|
|
1234
|
+
"""Whether to delete all the cached partial movie files."""
|
|
1235
|
+
return self._d["flush_cache"]
|
|
1236
|
+
|
|
1237
|
+
@flush_cache.setter
|
|
1238
|
+
def flush_cache(self, value: bool) -> None:
|
|
1239
|
+
self._set_boolean("flush_cache", value)
|
|
1240
|
+
|
|
1241
|
+
@property
|
|
1242
|
+
def disable_caching(self) -> bool:
|
|
1243
|
+
"""Whether to use scene caching."""
|
|
1244
|
+
return self._d["disable_caching"]
|
|
1245
|
+
|
|
1246
|
+
@disable_caching.setter
|
|
1247
|
+
def disable_caching(self, value: bool) -> None:
|
|
1248
|
+
self._set_boolean("disable_caching", value)
|
|
1249
|
+
|
|
1250
|
+
@property
|
|
1251
|
+
def disable_caching_warning(self) -> bool:
|
|
1252
|
+
"""Whether a warning is raised if there are too much submobjects to hash."""
|
|
1253
|
+
return self._d["disable_caching_warning"]
|
|
1254
|
+
|
|
1255
|
+
@disable_caching_warning.setter
|
|
1256
|
+
def disable_caching_warning(self, value: bool) -> None:
|
|
1257
|
+
self._set_boolean("disable_caching_warning", value)
|
|
1258
|
+
|
|
1259
|
+
@property
|
|
1260
|
+
def movie_file_extension(self) -> str:
|
|
1261
|
+
"""Either .mp4, .webm or .mov."""
|
|
1262
|
+
return self._d["movie_file_extension"]
|
|
1263
|
+
|
|
1264
|
+
@movie_file_extension.setter
|
|
1265
|
+
def movie_file_extension(self, value: str) -> None:
|
|
1266
|
+
self._set_from_list("movie_file_extension", value, [".mp4", ".mov", ".webm"])
|
|
1267
|
+
|
|
1268
|
+
@property
|
|
1269
|
+
def background_opacity(self) -> float:
|
|
1270
|
+
"""A number between 0.0 (fully transparent) and 1.0 (fully opaque)."""
|
|
1271
|
+
return self._d["background_opacity"]
|
|
1272
|
+
|
|
1273
|
+
@background_opacity.setter
|
|
1274
|
+
def background_opacity(self, value: float) -> None:
|
|
1275
|
+
self._set_between("background_opacity", value, 0, 1)
|
|
1276
|
+
if self.background_opacity < 1:
|
|
1277
|
+
self.resolve_movie_file_extension(is_transparent=True)
|
|
1278
|
+
|
|
1279
|
+
@property
|
|
1280
|
+
def frame_size(self) -> tuple[int, int]:
|
|
1281
|
+
"""Tuple with (pixel width, pixel height) (no flag)."""
|
|
1282
|
+
return (self._d["pixel_width"], self._d["pixel_height"])
|
|
1283
|
+
|
|
1284
|
+
@frame_size.setter
|
|
1285
|
+
def frame_size(self, value: tuple[int, int]) -> None:
|
|
1286
|
+
self._d.__setitem__("pixel_width", value[0]) or self._d.__setitem__(
|
|
1287
|
+
"pixel_height", value[1]
|
|
1288
|
+
)
|
|
1289
|
+
|
|
1290
|
+
@property
|
|
1291
|
+
def quality(self) -> str | None:
|
|
1171
1292
|
"""Video quality (-q)."""
|
|
1172
1293
|
keys = ["pixel_width", "pixel_height", "frame_rate"]
|
|
1173
1294
|
q = {k: self[k] for k in keys}
|
|
1174
1295
|
for qual in constants.QUALITIES:
|
|
1175
|
-
if all(
|
|
1296
|
+
if all(q[k] == constants.QUALITIES[qual][k] for k in keys):
|
|
1176
1297
|
return qual
|
|
1177
1298
|
return None
|
|
1178
1299
|
|
|
1179
1300
|
@quality.setter
|
|
1180
|
-
def quality(self,
|
|
1181
|
-
if
|
|
1301
|
+
def quality(self, value: str | None) -> None:
|
|
1302
|
+
if value is None:
|
|
1182
1303
|
return
|
|
1183
|
-
if
|
|
1304
|
+
if value not in constants.QUALITIES:
|
|
1184
1305
|
raise KeyError(f"quality must be one of {list(constants.QUALITIES.keys())}")
|
|
1185
|
-
q = constants.QUALITIES[
|
|
1306
|
+
q = constants.QUALITIES[value]
|
|
1186
1307
|
self.frame_size = q["pixel_width"], q["pixel_height"]
|
|
1187
1308
|
self.frame_rate = q["frame_rate"]
|
|
1188
1309
|
|
|
1189
1310
|
@property
|
|
1190
|
-
def transparent(self):
|
|
1191
|
-
"""Whether the background opacity is
|
|
1192
|
-
return self._d["background_opacity"]
|
|
1311
|
+
def transparent(self) -> bool:
|
|
1312
|
+
"""Whether the background opacity is less than 1.0 (-t)."""
|
|
1313
|
+
return self._d["background_opacity"] < 1.0
|
|
1193
1314
|
|
|
1194
1315
|
@transparent.setter
|
|
1195
|
-
def transparent(self,
|
|
1196
|
-
self._d["background_opacity"] = float(not
|
|
1197
|
-
self.resolve_movie_file_extension(
|
|
1316
|
+
def transparent(self, value: bool) -> None:
|
|
1317
|
+
self._d["background_opacity"] = float(not value)
|
|
1318
|
+
self.resolve_movie_file_extension(value)
|
|
1198
1319
|
|
|
1199
1320
|
@property
|
|
1200
|
-
def dry_run(self):
|
|
1321
|
+
def dry_run(self) -> bool:
|
|
1201
1322
|
"""Whether dry run is enabled."""
|
|
1202
1323
|
return self._d["dry_run"]
|
|
1203
1324
|
|
|
@@ -1211,7 +1332,7 @@ class ManimConfig(MutableMapping):
|
|
|
1211
1332
|
self.format = None
|
|
1212
1333
|
|
|
1213
1334
|
@property
|
|
1214
|
-
def renderer(self):
|
|
1335
|
+
def renderer(self) -> RendererType:
|
|
1215
1336
|
"""The currently active renderer.
|
|
1216
1337
|
|
|
1217
1338
|
Populated with one of the available renderers in :class:`.RendererType`.
|
|
@@ -1237,15 +1358,15 @@ class ManimConfig(MutableMapping):
|
|
|
1237
1358
|
return self._d["renderer"]
|
|
1238
1359
|
|
|
1239
1360
|
@renderer.setter
|
|
1240
|
-
def renderer(self,
|
|
1361
|
+
def renderer(self, value: str | RendererType) -> None:
|
|
1241
1362
|
"""The setter of the renderer property.
|
|
1242
1363
|
|
|
1243
1364
|
Takes care of switching inheritance bases using the
|
|
1244
1365
|
:class:`.ConvertToOpenGL` metaclass.
|
|
1245
1366
|
"""
|
|
1246
|
-
if isinstance(
|
|
1247
|
-
|
|
1248
|
-
renderer = RendererType(
|
|
1367
|
+
if isinstance(value, str):
|
|
1368
|
+
value = value.lower()
|
|
1369
|
+
renderer = RendererType(value)
|
|
1249
1370
|
try:
|
|
1250
1371
|
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
|
1251
1372
|
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
|
|
@@ -1279,25 +1400,35 @@ class ManimConfig(MutableMapping):
|
|
|
1279
1400
|
|
|
1280
1401
|
self._set_from_enum("renderer", renderer, RendererType)
|
|
1281
1402
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
)
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1403
|
+
@property
|
|
1404
|
+
def media_dir(self) -> str:
|
|
1405
|
+
"""Main output directory. See :meth:`ManimConfig.get_dir`."""
|
|
1406
|
+
return self._d["media_dir"]
|
|
1407
|
+
|
|
1408
|
+
@media_dir.setter
|
|
1409
|
+
def media_dir(self, value: str | Path) -> None:
|
|
1410
|
+
self._set_dir("media_dir", value)
|
|
1411
|
+
|
|
1412
|
+
@property
|
|
1413
|
+
def window_position(self) -> str:
|
|
1414
|
+
"""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'."""
|
|
1415
|
+
return self._d["window_position"]
|
|
1416
|
+
|
|
1417
|
+
@window_position.setter
|
|
1418
|
+
def window_position(self, value: str) -> None:
|
|
1419
|
+
self._d.__setitem__("window_position", value)
|
|
1420
|
+
|
|
1421
|
+
@property
|
|
1422
|
+
def window_size(self) -> str:
|
|
1423
|
+
"""The size of the opengl window as 'width,height' or 'default' to automatically scale the window based on the display monitor."""
|
|
1424
|
+
return self._d["window_size"]
|
|
1425
|
+
|
|
1426
|
+
@window_size.setter
|
|
1427
|
+
def window_size(self, value: str) -> None:
|
|
1428
|
+
self._d.__setitem__("window_size", value)
|
|
1429
|
+
|
|
1430
|
+
def resolve_movie_file_extension(self, is_transparent: bool) -> None:
|
|
1431
|
+
prev_file_extension = self.movie_file_extension
|
|
1301
1432
|
if is_transparent:
|
|
1302
1433
|
self.movie_file_extension = ".webm" if self.format == "webm" else ".mov"
|
|
1303
1434
|
elif self.format == "webm":
|
|
@@ -1306,44 +1437,67 @@ class ManimConfig(MutableMapping):
|
|
|
1306
1437
|
self.movie_file_extension = ".mov"
|
|
1307
1438
|
else:
|
|
1308
1439
|
self.movie_file_extension = ".mp4"
|
|
1440
|
+
if self.movie_file_extension != prev_file_extension:
|
|
1441
|
+
logger.warning(
|
|
1442
|
+
f"Output format changed to '{self.movie_file_extension}' "
|
|
1443
|
+
"to support transparency",
|
|
1444
|
+
)
|
|
1445
|
+
|
|
1446
|
+
@property
|
|
1447
|
+
def enable_gui(self) -> bool:
|
|
1448
|
+
"""Enable GUI interaction."""
|
|
1449
|
+
return self._d["enable_gui"]
|
|
1450
|
+
|
|
1451
|
+
@enable_gui.setter
|
|
1452
|
+
def enable_gui(self, value: bool) -> None:
|
|
1453
|
+
self._set_boolean("enable_gui", value)
|
|
1454
|
+
|
|
1455
|
+
@property
|
|
1456
|
+
def gui_location(self) -> tuple[Any]:
|
|
1457
|
+
"""Location parameters for the GUI window (e.g., screen coordinates or layout settings)."""
|
|
1458
|
+
return self._d["gui_location"]
|
|
1309
1459
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
)
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
use_projection_fill_shaders
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
)
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1460
|
+
@gui_location.setter
|
|
1461
|
+
def gui_location(self, value: tuple[Any]) -> None:
|
|
1462
|
+
self._set_tuple("gui_location", value)
|
|
1463
|
+
|
|
1464
|
+
@property
|
|
1465
|
+
def fullscreen(self) -> bool:
|
|
1466
|
+
"""Expand the window to its maximum possible size."""
|
|
1467
|
+
return self._d["fullscreen"]
|
|
1468
|
+
|
|
1469
|
+
@fullscreen.setter
|
|
1470
|
+
def fullscreen(self, value: bool) -> None:
|
|
1471
|
+
self._set_boolean("fullscreen", value)
|
|
1472
|
+
|
|
1473
|
+
@property
|
|
1474
|
+
def use_projection_fill_shaders(self) -> bool:
|
|
1475
|
+
"""Use shaders for OpenGLVMobject fill which are compatible with transformation matrices."""
|
|
1476
|
+
return self._d["use_projection_fill_shaders"]
|
|
1477
|
+
|
|
1478
|
+
@use_projection_fill_shaders.setter
|
|
1479
|
+
def use_projection_fill_shaders(self, value: bool) -> None:
|
|
1480
|
+
self._set_boolean("use_projection_fill_shaders", value)
|
|
1481
|
+
|
|
1482
|
+
@property
|
|
1483
|
+
def use_projection_stroke_shaders(self) -> bool:
|
|
1484
|
+
"""Use shaders for OpenGLVMobject stroke which are compatible with transformation matrices."""
|
|
1485
|
+
return self._d["use_projection_stroke_shaders"]
|
|
1486
|
+
|
|
1487
|
+
@use_projection_stroke_shaders.setter
|
|
1488
|
+
def use_projection_stroke_shaders(self, value: bool) -> None:
|
|
1489
|
+
self._set_boolean("use_projection_stroke_shaders", value)
|
|
1490
|
+
|
|
1491
|
+
@property
|
|
1492
|
+
def zero_pad(self) -> int:
|
|
1493
|
+
"""PNG zero padding. A number between 0 (no zero padding) and 9 (9 columns minimum)."""
|
|
1494
|
+
return self._d["zero_pad"]
|
|
1495
|
+
|
|
1496
|
+
@zero_pad.setter
|
|
1497
|
+
def zero_pad(self, value: int) -> None:
|
|
1498
|
+
self._set_int_between("zero_pad", value, 0, 9)
|
|
1499
|
+
|
|
1500
|
+
def get_dir(self, key: str, **kwargs: Any) -> Path:
|
|
1347
1501
|
"""Resolve a config option that stores a directory.
|
|
1348
1502
|
|
|
1349
1503
|
Config options that store directories may depend on one another. This
|
|
@@ -1494,110 +1648,146 @@ class ManimConfig(MutableMapping):
|
|
|
1494
1648
|
) from exc
|
|
1495
1649
|
return Path(path) if path else None
|
|
1496
1650
|
|
|
1497
|
-
def _set_dir(self, key: str, val: str | Path):
|
|
1651
|
+
def _set_dir(self, key: str, val: str | Path) -> None:
|
|
1498
1652
|
if isinstance(val, Path):
|
|
1499
1653
|
self._d.__setitem__(key, str(val))
|
|
1500
1654
|
else:
|
|
1501
1655
|
self._d.__setitem__(key, val)
|
|
1502
1656
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
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
|
-
@property
|
|
1576
|
-
def
|
|
1657
|
+
@property
|
|
1658
|
+
def assets_dir(self) -> str:
|
|
1659
|
+
"""Directory to locate video assets (no flag)."""
|
|
1660
|
+
return self._d["assets_dir"]
|
|
1661
|
+
|
|
1662
|
+
@assets_dir.setter
|
|
1663
|
+
def assets_dir(self, value: str | Path) -> None:
|
|
1664
|
+
self._set_dir("assets_dir", value)
|
|
1665
|
+
|
|
1666
|
+
@property
|
|
1667
|
+
def log_dir(self) -> str:
|
|
1668
|
+
"""Directory to place logs. See :meth:`ManimConfig.get_dir`."""
|
|
1669
|
+
return self._d["log_dir"]
|
|
1670
|
+
|
|
1671
|
+
@log_dir.setter
|
|
1672
|
+
def log_dir(self, value: str | Path) -> None:
|
|
1673
|
+
self._set_dir("log_dir", value)
|
|
1674
|
+
|
|
1675
|
+
@property
|
|
1676
|
+
def video_dir(self) -> str:
|
|
1677
|
+
"""Directory to place videos (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1678
|
+
return self._d["video_dir"]
|
|
1679
|
+
|
|
1680
|
+
@video_dir.setter
|
|
1681
|
+
def video_dir(self, value: str | Path) -> None:
|
|
1682
|
+
self._set_dir("video_dir", value)
|
|
1683
|
+
|
|
1684
|
+
@property
|
|
1685
|
+
def sections_dir(self) -> str:
|
|
1686
|
+
"""Directory to place section videos (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1687
|
+
return self._d["sections_dir"]
|
|
1688
|
+
|
|
1689
|
+
@sections_dir.setter
|
|
1690
|
+
def sections_dir(self, value: str | Path) -> None:
|
|
1691
|
+
self._set_dir("sections_dir", value)
|
|
1692
|
+
|
|
1693
|
+
@property
|
|
1694
|
+
def images_dir(self) -> str:
|
|
1695
|
+
"""Directory to place images (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1696
|
+
return self._d["images_dir"]
|
|
1697
|
+
|
|
1698
|
+
@images_dir.setter
|
|
1699
|
+
def images_dir(self, value: str | Path) -> None:
|
|
1700
|
+
self._set_dir("images_dir", value)
|
|
1701
|
+
|
|
1702
|
+
@property
|
|
1703
|
+
def text_dir(self) -> str:
|
|
1704
|
+
"""Directory to place text (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1705
|
+
return self._d["text_dir"]
|
|
1706
|
+
|
|
1707
|
+
@text_dir.setter
|
|
1708
|
+
def text_dir(self, value: str | Path) -> None:
|
|
1709
|
+
self._set_dir("text_dir", value)
|
|
1710
|
+
|
|
1711
|
+
@property
|
|
1712
|
+
def tex_dir(self) -> str:
|
|
1713
|
+
"""Directory to place tex (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1714
|
+
return self._d["tex_dir"]
|
|
1715
|
+
|
|
1716
|
+
@tex_dir.setter
|
|
1717
|
+
def tex_dir(self, value: str | Path) -> None:
|
|
1718
|
+
self._set_dir("tex_dir", value)
|
|
1719
|
+
|
|
1720
|
+
@property
|
|
1721
|
+
def partial_movie_dir(self) -> str:
|
|
1722
|
+
"""Directory to place partial movie files (no flag). See :meth:`ManimConfig.get_dir`."""
|
|
1723
|
+
return self._d["partial_movie_dir"]
|
|
1724
|
+
|
|
1725
|
+
@partial_movie_dir.setter
|
|
1726
|
+
def partial_movie_dir(self, value: str | Path) -> None:
|
|
1727
|
+
self._set_dir("partial_movie_dir", value)
|
|
1728
|
+
|
|
1729
|
+
@property
|
|
1730
|
+
def custom_folders(self) -> str:
|
|
1731
|
+
"""Whether to use custom folder output."""
|
|
1732
|
+
return self._d["custom_folders"]
|
|
1733
|
+
|
|
1734
|
+
@custom_folders.setter
|
|
1735
|
+
def custom_folders(self, value: str | Path) -> None:
|
|
1736
|
+
self._set_dir("custom_folders", value)
|
|
1737
|
+
|
|
1738
|
+
@property
|
|
1739
|
+
def input_file(self) -> str:
|
|
1740
|
+
"""Input file name."""
|
|
1741
|
+
return self._d["input_file"]
|
|
1742
|
+
|
|
1743
|
+
@input_file.setter
|
|
1744
|
+
def input_file(self, value: str | Path) -> None:
|
|
1745
|
+
self._set_dir("input_file", value)
|
|
1746
|
+
|
|
1747
|
+
@property
|
|
1748
|
+
def output_file(self) -> str:
|
|
1749
|
+
"""Output file name (-o)."""
|
|
1750
|
+
return self._d["output_file"]
|
|
1751
|
+
|
|
1752
|
+
@output_file.setter
|
|
1753
|
+
def output_file(self, value: str | Path) -> None:
|
|
1754
|
+
self._set_dir("output_file", value)
|
|
1755
|
+
|
|
1756
|
+
@property
|
|
1757
|
+
def scene_names(self) -> list[str]:
|
|
1758
|
+
"""Scenes to play from file."""
|
|
1759
|
+
return self._d["scene_names"]
|
|
1760
|
+
|
|
1761
|
+
@scene_names.setter
|
|
1762
|
+
def scene_names(self, value: list[str]) -> None:
|
|
1763
|
+
self._d.__setitem__("scene_names", value)
|
|
1764
|
+
|
|
1765
|
+
@property
|
|
1766
|
+
def tex_template(self) -> TexTemplate:
|
|
1577
1767
|
"""Template used when rendering Tex. See :class:`.TexTemplate`."""
|
|
1578
1768
|
if not hasattr(self, "_tex_template") or not self._tex_template:
|
|
1579
1769
|
fn = self._d["tex_template_file"]
|
|
1580
1770
|
if fn:
|
|
1581
|
-
self._tex_template =
|
|
1771
|
+
self._tex_template = TexTemplate.from_file(fn)
|
|
1582
1772
|
else:
|
|
1583
1773
|
self._tex_template = TexTemplate()
|
|
1584
1774
|
return self._tex_template
|
|
1585
1775
|
|
|
1586
1776
|
@tex_template.setter
|
|
1587
|
-
def tex_template(self, val:
|
|
1588
|
-
if isinstance(val,
|
|
1777
|
+
def tex_template(self, val: TexTemplate) -> None:
|
|
1778
|
+
if isinstance(val, TexTemplate):
|
|
1589
1779
|
self._tex_template = val
|
|
1590
1780
|
|
|
1591
1781
|
@property
|
|
1592
|
-
def tex_template_file(self):
|
|
1593
|
-
"""File to read Tex template from (no flag). See :class:`.
|
|
1782
|
+
def tex_template_file(self) -> Path:
|
|
1783
|
+
"""File to read Tex template from (no flag). See :class:`.TexTemplate`."""
|
|
1594
1784
|
return self._d["tex_template_file"]
|
|
1595
1785
|
|
|
1596
1786
|
@tex_template_file.setter
|
|
1597
1787
|
def tex_template_file(self, val: str) -> None:
|
|
1598
1788
|
if val:
|
|
1599
1789
|
if not os.access(val, os.R_OK):
|
|
1600
|
-
|
|
1790
|
+
logger.warning(
|
|
1601
1791
|
f"Custom TeX template {val} not found or not readable.",
|
|
1602
1792
|
)
|
|
1603
1793
|
else:
|
|
@@ -1606,17 +1796,19 @@ class ManimConfig(MutableMapping):
|
|
|
1606
1796
|
self._d["tex_template_file"] = val # actually set the falsy value
|
|
1607
1797
|
|
|
1608
1798
|
@property
|
|
1609
|
-
def plugins(self):
|
|
1799
|
+
def plugins(self) -> list[str]:
|
|
1610
1800
|
"""List of plugins to enable."""
|
|
1611
1801
|
return self._d["plugins"]
|
|
1612
1802
|
|
|
1613
1803
|
@plugins.setter
|
|
1614
|
-
def plugins(self, value):
|
|
1804
|
+
def plugins(self, value: list[str]):
|
|
1615
1805
|
self._d["plugins"] = value
|
|
1616
1806
|
|
|
1617
1807
|
|
|
1808
|
+
# TODO: to be used in the future - see PR #620
|
|
1809
|
+
# https://github.com/ManimCommunity/manim/pull/620
|
|
1618
1810
|
class ManimFrame(Mapping):
|
|
1619
|
-
_OPTS: set[str] = {
|
|
1811
|
+
_OPTS: ClassVar[set[str]] = {
|
|
1620
1812
|
"pixel_width",
|
|
1621
1813
|
"pixel_height",
|
|
1622
1814
|
"aspect_ratio",
|
|
@@ -1629,7 +1821,7 @@ class ManimFrame(Mapping):
|
|
|
1629
1821
|
"left_side",
|
|
1630
1822
|
"right_side",
|
|
1631
1823
|
}
|
|
1632
|
-
_CONSTANTS: dict[str,
|
|
1824
|
+
_CONSTANTS: ClassVar[dict[str, Vector3D]] = {
|
|
1633
1825
|
"UP": np.array((0.0, 1.0, 0.0)),
|
|
1634
1826
|
"DOWN": np.array((0.0, -1.0, 0.0)),
|
|
1635
1827
|
"RIGHT": np.array((1.0, 0.0, 0.0)),
|
|
@@ -1646,6 +1838,8 @@ class ManimFrame(Mapping):
|
|
|
1646
1838
|
"DR": np.array((1.0, -1.0, 0.0)),
|
|
1647
1839
|
}
|
|
1648
1840
|
|
|
1841
|
+
_c: ManimConfig
|
|
1842
|
+
|
|
1649
1843
|
def __init__(self, c: ManimConfig) -> None:
|
|
1650
1844
|
if not isinstance(c, ManimConfig):
|
|
1651
1845
|
raise TypeError("argument must be instance of 'ManimConfig'")
|
|
@@ -1662,20 +1856,20 @@ class ManimFrame(Mapping):
|
|
|
1662
1856
|
else:
|
|
1663
1857
|
raise KeyError(key)
|
|
1664
1858
|
|
|
1665
|
-
def __iter__(self) -> Iterable:
|
|
1859
|
+
def __iter__(self) -> Iterable[str]:
|
|
1666
1860
|
return iter(list(self._OPTS) + list(self._CONSTANTS))
|
|
1667
1861
|
|
|
1668
1862
|
def __len__(self) -> int:
|
|
1669
1863
|
return len(self._OPTS)
|
|
1670
1864
|
|
|
1671
1865
|
# make this truly immutable
|
|
1672
|
-
def __setattr__(self, attr, val) ->
|
|
1866
|
+
def __setattr__(self, attr: Any, val: Any) -> NoReturn:
|
|
1673
1867
|
raise TypeError("'ManimFrame' object does not support item assignment")
|
|
1674
1868
|
|
|
1675
|
-
def __setitem__(self, key, val) ->
|
|
1869
|
+
def __setitem__(self, key: Any, val: Any) -> NoReturn:
|
|
1676
1870
|
raise TypeError("'ManimFrame' object does not support item assignment")
|
|
1677
1871
|
|
|
1678
|
-
def __delitem__(self, key) ->
|
|
1872
|
+
def __delitem__(self, key: Any) -> NoReturn:
|
|
1679
1873
|
raise TypeError("'ManimFrame' object does not support item deletion")
|
|
1680
1874
|
|
|
1681
1875
|
|