manim 0.18.1__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/__main__.py +45 -12
- manim/_config/__init__.py +2 -2
- manim/_config/cli_colors.py +8 -4
- manim/_config/default.cfg +0 -2
- manim/_config/logger_utils.py +5 -0
- manim/_config/utils.py +29 -38
- manim/animation/animation.py +148 -8
- manim/animation/composition.py +16 -13
- manim/animation/creation.py +184 -8
- manim/animation/fading.py +5 -8
- manim/animation/indication.py +93 -26
- manim/animation/movement.py +21 -3
- manim/animation/rotation.py +2 -1
- manim/animation/specialized.py +3 -5
- manim/animation/speedmodifier.py +3 -3
- manim/animation/transform.py +4 -5
- manim/animation/updaters/mobject_update_utils.py +17 -14
- manim/camera/camera.py +2 -2
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +52 -36
- manim/cli/checkhealth/checks.py +92 -76
- manim/cli/checkhealth/commands.py +12 -5
- manim/cli/default_group.py +148 -24
- manim/cli/init/commands.py +28 -23
- manim/cli/plugins/commands.py +13 -3
- manim/cli/render/commands.py +47 -42
- manim/cli/render/global_options.py +43 -9
- manim/cli/render/render_options.py +84 -19
- manim/constants.py +11 -4
- manim/mobject/frame.py +0 -1
- manim/mobject/geometry/arc.py +109 -75
- manim/mobject/geometry/boolean_ops.py +20 -17
- manim/mobject/geometry/labeled.py +300 -77
- manim/mobject/geometry/line.py +120 -60
- manim/mobject/geometry/polygram.py +109 -25
- manim/mobject/geometry/shape_matchers.py +35 -15
- manim/mobject/geometry/tips.py +36 -27
- manim/mobject/graph.py +48 -40
- manim/mobject/graphing/coordinate_systems.py +110 -45
- manim/mobject/graphing/functions.py +16 -10
- manim/mobject/graphing/number_line.py +23 -9
- manim/mobject/graphing/probability.py +2 -10
- manim/mobject/graphing/scale.py +6 -5
- manim/mobject/matrix.py +17 -19
- manim/mobject/mobject.py +149 -103
- manim/mobject/opengl/opengl_geometry.py +4 -8
- manim/mobject/opengl/opengl_mobject.py +506 -343
- manim/mobject/opengl/opengl_point_cloud_mobject.py +3 -7
- manim/mobject/opengl/opengl_surface.py +1 -2
- manim/mobject/opengl/opengl_vectorized_mobject.py +27 -65
- manim/mobject/svg/brace.py +61 -13
- manim/mobject/svg/svg_mobject.py +2 -1
- manim/mobject/table.py +11 -12
- manim/mobject/text/code_mobject.py +186 -550
- manim/mobject/text/numbers.py +7 -7
- manim/mobject/text/tex_mobject.py +22 -13
- manim/mobject/text/text_mobject.py +29 -20
- manim/mobject/three_d/polyhedra.py +98 -1
- manim/mobject/three_d/three_dimensions.py +59 -31
- manim/mobject/types/image_mobject.py +37 -23
- manim/mobject/types/point_cloud_mobject.py +103 -67
- manim/mobject/types/vectorized_mobject.py +387 -214
- manim/mobject/value_tracker.py +2 -1
- manim/mobject/vector_field.py +2 -4
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +2 -3
- manim/plugins/plugins_flags.py +3 -3
- manim/renderer/cairo_renderer.py +11 -11
- manim/renderer/opengl_renderer.py +19 -20
- manim/renderer/shader.py +2 -3
- manim/renderer/shader_wrapper.py +3 -2
- manim/scene/moving_camera_scene.py +23 -0
- manim/scene/scene.py +72 -41
- manim/scene/scene_file_writer.py +313 -164
- manim/scene/section.py +15 -15
- manim/scene/three_d_scene.py +8 -15
- manim/scene/vector_space_scene.py +3 -6
- manim/typing.py +326 -66
- manim/utils/bezier.py +1658 -381
- manim/utils/caching.py +11 -5
- manim/utils/color/AS2700.py +2 -0
- manim/utils/color/BS381.py +2 -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 +2 -0
- manim/utils/color/__init__.py +8 -5
- manim/utils/color/core.py +818 -301
- manim/utils/color/manim_colors.py +7 -9
- manim/utils/commands.py +40 -19
- manim/utils/config_ops.py +18 -13
- manim/utils/debug.py +8 -6
- manim/utils/deprecation.py +92 -43
- manim/utils/docbuild/autoaliasattr_directive.py +45 -8
- manim/utils/docbuild/autocolor_directive.py +12 -13
- manim/utils/docbuild/manim_directive.py +35 -29
- manim/utils/docbuild/module_parsing.py +74 -27
- manim/utils/family.py +3 -3
- manim/utils/family_ops.py +12 -4
- manim/utils/file_ops.py +22 -16
- manim/utils/hashing.py +7 -7
- manim/utils/images.py +10 -4
- manim/utils/ipython_magic.py +12 -8
- manim/utils/iterables.py +161 -119
- manim/utils/module_ops.py +55 -19
- manim/utils/opengl.py +68 -23
- manim/utils/parameter_parsing.py +3 -2
- manim/utils/paths.py +11 -5
- manim/utils/polylabel.py +168 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +69 -32
- manim/utils/simple_functions.py +24 -15
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +48 -37
- 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 +20 -14
- manim/utils/tex.py +4 -2
- manim/utils/tex_file_writing.py +45 -45
- manim/utils/tex_templates.py +1 -1
- manim/utils/unit.py +6 -5
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/METADATA +16 -9
- manim-0.19.0.dist-info/RECORD +221 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
- manim-0.18.1.dist-info/RECORD +0 -217
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE.community +0 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
manim/mobject/value_tracker.py
CHANGED
|
@@ -165,7 +165,8 @@ class ComplexValueTracker(ValueTracker):
|
|
|
165
165
|
"""Get the current value of this value tracker as a complex number.
|
|
166
166
|
|
|
167
167
|
The value is internally stored as a points array [a, b, 0]. This can be accessed directly
|
|
168
|
-
to represent the value geometrically, see the usage example.
|
|
168
|
+
to represent the value geometrically, see the usage example.
|
|
169
|
+
"""
|
|
169
170
|
return complex(*self.points[0, :2])
|
|
170
171
|
|
|
171
172
|
def set_value(self, z):
|
manim/mobject/vector_field.py
CHANGED
|
@@ -10,8 +10,9 @@ __all__ = [
|
|
|
10
10
|
|
|
11
11
|
import itertools as it
|
|
12
12
|
import random
|
|
13
|
+
from collections.abc import Iterable, Sequence
|
|
13
14
|
from math import ceil, floor
|
|
14
|
-
from typing import Callable
|
|
15
|
+
from typing import Callable
|
|
15
16
|
|
|
16
17
|
import numpy as np
|
|
17
18
|
from PIL import Image
|
|
@@ -352,7 +353,6 @@ class VectorField(VGroup):
|
|
|
352
353
|
This vector field.
|
|
353
354
|
|
|
354
355
|
"""
|
|
355
|
-
|
|
356
356
|
self.stop_submobject_movement()
|
|
357
357
|
self.submob_movement_updater = lambda mob, dt: mob.nudge_submobjects(
|
|
358
358
|
dt * speed,
|
|
@@ -950,7 +950,6 @@ class StreamLines(VectorField):
|
|
|
950
950
|
self.wait(stream_lines.virtual_time / stream_lines.flow_speed)
|
|
951
951
|
|
|
952
952
|
"""
|
|
953
|
-
|
|
954
953
|
for line in self.stream_lines:
|
|
955
954
|
run_time = line.duration / flow_speed
|
|
956
955
|
line.anim = line_animation_class(
|
|
@@ -1010,7 +1009,6 @@ class StreamLines(VectorField):
|
|
|
1010
1009
|
self.play(stream_lines.end_animation())
|
|
1011
1010
|
|
|
1012
1011
|
"""
|
|
1013
|
-
|
|
1014
1012
|
if self.flow_animation is None:
|
|
1015
1013
|
raise ValueError("You have to start the animation before fading it out.")
|
|
1016
1014
|
|
manim/opengl/__init__.py
CHANGED
manim/plugins/__init__.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from manim import config, logger
|
|
4
|
-
|
|
5
|
-
from .plugins_flags import get_plugins, list_plugins
|
|
3
|
+
from manim._config import config, logger
|
|
4
|
+
from manim.plugins.plugins_flags import get_plugins, list_plugins
|
|
6
5
|
|
|
7
6
|
__all__ = [
|
|
8
7
|
"get_plugins",
|
manim/plugins/plugins_flags.py
CHANGED
|
@@ -10,7 +10,7 @@ if sys.version_info < (3, 10):
|
|
|
10
10
|
else:
|
|
11
11
|
from importlib.metadata import entry_points
|
|
12
12
|
|
|
13
|
-
from manim import console
|
|
13
|
+
from manim._config import console
|
|
14
14
|
|
|
15
15
|
__all__ = ["list_plugins"]
|
|
16
16
|
|
|
@@ -27,5 +27,5 @@ def list_plugins() -> None:
|
|
|
27
27
|
console.print("[green bold]Plugins:[/green bold]", justify="left")
|
|
28
28
|
|
|
29
29
|
plugins = get_plugins()
|
|
30
|
-
for
|
|
31
|
-
console.print(f" • {
|
|
30
|
+
for plugin_name in plugins:
|
|
31
|
+
console.print(f" • {plugin_name}")
|
manim/renderer/cairo_renderer.py
CHANGED
|
@@ -8,18 +8,19 @@ from manim.utils.hashing import get_hash_from_play_call
|
|
|
8
8
|
|
|
9
9
|
from .. import config, logger
|
|
10
10
|
from ..camera.camera import Camera
|
|
11
|
-
from ..mobject.mobject import Mobject
|
|
11
|
+
from ..mobject.mobject import Mobject, _AnimationBuilder
|
|
12
12
|
from ..scene.scene_file_writer import SceneFileWriter
|
|
13
13
|
from ..utils.exceptions import EndSceneEarlyException
|
|
14
14
|
from ..utils.iterables import list_update
|
|
15
15
|
|
|
16
16
|
if typing.TYPE_CHECKING:
|
|
17
|
-
import
|
|
18
|
-
from typing import Any, Iterable
|
|
17
|
+
from typing import Any
|
|
19
18
|
|
|
20
19
|
from manim.animation.animation import Animation
|
|
21
20
|
from manim.scene.scene import Scene
|
|
22
21
|
|
|
22
|
+
from ..typing import PixelArray
|
|
23
|
+
|
|
23
24
|
__all__ = ["CairoRenderer"]
|
|
24
25
|
|
|
25
26
|
|
|
@@ -59,7 +60,7 @@ class CairoRenderer:
|
|
|
59
60
|
def play(
|
|
60
61
|
self,
|
|
61
62
|
scene: Scene,
|
|
62
|
-
*args: Animation |
|
|
63
|
+
*args: Animation | Mobject | _AnimationBuilder,
|
|
63
64
|
**kwargs,
|
|
64
65
|
):
|
|
65
66
|
# Reset skip_animations to the original state.
|
|
@@ -159,7 +160,7 @@ class CairoRenderer:
|
|
|
159
160
|
self.update_frame(scene, moving_mobjects)
|
|
160
161
|
self.add_frame(self.get_frame())
|
|
161
162
|
|
|
162
|
-
def get_frame(self):
|
|
163
|
+
def get_frame(self) -> PixelArray:
|
|
163
164
|
"""
|
|
164
165
|
Gets the current frame as NumPy array.
|
|
165
166
|
|
|
@@ -186,8 +187,7 @@ class CairoRenderer:
|
|
|
186
187
|
if self.skip_animations:
|
|
187
188
|
return
|
|
188
189
|
self.time += num_frames * dt
|
|
189
|
-
|
|
190
|
-
self.file_writer.write_frame(frame)
|
|
190
|
+
self.file_writer.write_frame(frame, num_frames=num_frames)
|
|
191
191
|
|
|
192
192
|
def freeze_current_frame(self, duration: float):
|
|
193
193
|
"""Adds a static frame to the movie for a given duration. The static frame is the current frame.
|
|
@@ -252,13 +252,13 @@ class CairoRenderer:
|
|
|
252
252
|
if config["save_last_frame"]:
|
|
253
253
|
self.skip_animations = True
|
|
254
254
|
if (
|
|
255
|
-
config
|
|
256
|
-
and self.num_plays < config
|
|
255
|
+
config.from_animation_number > 0
|
|
256
|
+
and self.num_plays < config.from_animation_number
|
|
257
257
|
):
|
|
258
258
|
self.skip_animations = True
|
|
259
259
|
if (
|
|
260
|
-
config
|
|
261
|
-
and self.num_plays > config
|
|
260
|
+
config.upto_animation_number >= 0
|
|
261
|
+
and self.num_plays > config.upto_animation_number
|
|
262
262
|
):
|
|
263
263
|
self.skip_animations = True
|
|
264
264
|
raise EndSceneEarlyException()
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import itertools as it
|
|
4
|
-
import sys
|
|
5
5
|
import time
|
|
6
|
+
from functools import cached_property
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
8
|
-
if sys.version_info < (3, 8):
|
|
9
|
-
from backports.cached_property import cached_property
|
|
10
|
-
else:
|
|
11
|
-
from functools import cached_property
|
|
12
|
-
|
|
13
9
|
import moderngl
|
|
14
10
|
import numpy as np
|
|
15
11
|
from PIL import Image
|
|
@@ -67,12 +63,12 @@ class OpenGLCamera(OpenGLMobject):
|
|
|
67
63
|
if self.orthographic:
|
|
68
64
|
self.projection_matrix = opengl.orthographic_projection_matrix()
|
|
69
65
|
self.unformatted_projection_matrix = opengl.orthographic_projection_matrix(
|
|
70
|
-
|
|
66
|
+
format_=False,
|
|
71
67
|
)
|
|
72
68
|
else:
|
|
73
69
|
self.projection_matrix = opengl.perspective_projection_matrix()
|
|
74
70
|
self.unformatted_projection_matrix = opengl.perspective_projection_matrix(
|
|
75
|
-
|
|
71
|
+
format_=False,
|
|
76
72
|
)
|
|
77
73
|
|
|
78
74
|
if frame_shape is None:
|
|
@@ -220,7 +216,11 @@ class OpenGLCamera(OpenGLMobject):
|
|
|
220
216
|
|
|
221
217
|
|
|
222
218
|
class OpenGLRenderer:
|
|
223
|
-
def __init__(
|
|
219
|
+
def __init__(
|
|
220
|
+
self,
|
|
221
|
+
file_writer_class: type[SceneFileWriter] = SceneFileWriter,
|
|
222
|
+
skip_animations: bool = False,
|
|
223
|
+
) -> None:
|
|
224
224
|
# Measured in pixel widths, used for vector graphics
|
|
225
225
|
self.anti_alias_width = 1.5
|
|
226
226
|
self._file_writer_class = file_writer_class
|
|
@@ -341,10 +341,8 @@ class OpenGLRenderer:
|
|
|
341
341
|
shader_wrapper.uniforms.items(),
|
|
342
342
|
self.perspective_uniforms.items(),
|
|
343
343
|
):
|
|
344
|
-
|
|
344
|
+
with contextlib.suppress(KeyError):
|
|
345
345
|
shader.set_uniform(name, value)
|
|
346
|
-
except KeyError:
|
|
347
|
-
pass
|
|
348
346
|
try:
|
|
349
347
|
shader.set_uniform(
|
|
350
348
|
"u_view_matrix", self.scene.camera.formatted_view_matrix
|
|
@@ -390,7 +388,7 @@ class OpenGLRenderer:
|
|
|
390
388
|
|
|
391
389
|
return self.path_to_texture_id[repr(path)]
|
|
392
390
|
|
|
393
|
-
def update_skipping_status(self):
|
|
391
|
+
def update_skipping_status(self) -> None:
|
|
394
392
|
"""
|
|
395
393
|
This method is used internally to check if the current
|
|
396
394
|
animation needs to be skipped or not. It also checks if
|
|
@@ -402,13 +400,13 @@ class OpenGLRenderer:
|
|
|
402
400
|
if self.file_writer.sections[-1].skip_animations:
|
|
403
401
|
self.skip_animations = True
|
|
404
402
|
if (
|
|
405
|
-
config
|
|
406
|
-
and self.num_plays < config
|
|
403
|
+
config.from_animation_number > 0
|
|
404
|
+
and self.num_plays < config.from_animation_number
|
|
407
405
|
):
|
|
408
406
|
self.skip_animations = True
|
|
409
407
|
if (
|
|
410
|
-
config
|
|
411
|
-
and self.num_plays > config
|
|
408
|
+
config.upto_animation_number >= 0
|
|
409
|
+
and self.num_plays > config.upto_animation_number
|
|
412
410
|
):
|
|
413
411
|
self.skip_animations = True
|
|
414
412
|
raise EndSceneEarlyException()
|
|
@@ -425,8 +423,9 @@ class OpenGLRenderer:
|
|
|
425
423
|
self.update_frame(scene)
|
|
426
424
|
|
|
427
425
|
if not self.skip_animations:
|
|
428
|
-
|
|
429
|
-
self.
|
|
426
|
+
self.file_writer.write_frame(
|
|
427
|
+
self, num_frames=int(config.frame_rate * scene.duration)
|
|
428
|
+
)
|
|
430
429
|
|
|
431
430
|
if self.window is not None:
|
|
432
431
|
self.window.swap_buffers()
|
|
@@ -572,7 +571,7 @@ class OpenGLRenderer:
|
|
|
572
571
|
if pixel_shape is None:
|
|
573
572
|
return np.array([0, 0, 0])
|
|
574
573
|
pw, ph = pixel_shape
|
|
575
|
-
|
|
574
|
+
fh = config["frame_height"]
|
|
576
575
|
fc = self.camera.get_center()
|
|
577
576
|
if relative:
|
|
578
577
|
return 2 * np.array([px / pw, py / ph, 0])
|
manim/renderer/shader.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import inspect
|
|
4
5
|
import re
|
|
5
6
|
import textwrap
|
|
@@ -382,10 +383,8 @@ class Shader:
|
|
|
382
383
|
shader_program_cache[self.name] = self.shader_program
|
|
383
384
|
|
|
384
385
|
def set_uniform(self, name, value):
|
|
385
|
-
|
|
386
|
+
with contextlib.suppress(KeyError):
|
|
386
387
|
self.shader_program[name] = value
|
|
387
|
-
except KeyError:
|
|
388
|
-
pass
|
|
389
388
|
|
|
390
389
|
|
|
391
390
|
class FullScreenQuad(Mesh):
|
manim/renderer/shader_wrapper.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import copy
|
|
4
|
+
import logging
|
|
4
5
|
import re
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
7
8
|
import moderngl
|
|
8
9
|
import numpy as np
|
|
9
10
|
|
|
10
|
-
from .. import logger
|
|
11
|
-
|
|
12
11
|
# Mobjects that should be rendered with
|
|
13
12
|
# the same shader will be organized and
|
|
14
13
|
# clumped together based on keeping track
|
|
@@ -17,6 +16,8 @@ from .. import logger
|
|
|
17
16
|
|
|
18
17
|
__all__ = ["ShaderWrapper"]
|
|
19
18
|
|
|
19
|
+
logger = logging.getLogger("manim")
|
|
20
|
+
|
|
20
21
|
|
|
21
22
|
def get_shader_dir():
|
|
22
23
|
return Path(__file__).parent / "shaders"
|
|
@@ -64,6 +64,25 @@ Examples
|
|
|
64
64
|
self.play(Restore(self.camera.frame))
|
|
65
65
|
self.wait()
|
|
66
66
|
|
|
67
|
+
.. manim:: SlidingMultipleScenes
|
|
68
|
+
|
|
69
|
+
class SlidingMultipleScenes(MovingCameraScene):
|
|
70
|
+
def construct(self):
|
|
71
|
+
def create_scene(number):
|
|
72
|
+
frame = Rectangle(width=16,height=9)
|
|
73
|
+
circ = Circle().shift(LEFT)
|
|
74
|
+
text = Tex(f"This is Scene {str(number)}").next_to(circ, RIGHT)
|
|
75
|
+
frame.add(circ,text)
|
|
76
|
+
return frame
|
|
77
|
+
|
|
78
|
+
group = VGroup(*(create_scene(i) for i in range(4))).arrange_in_grid(buff=4)
|
|
79
|
+
self.add(group)
|
|
80
|
+
self.camera.auto_zoom(group[0], animate=False)
|
|
81
|
+
for scene in group:
|
|
82
|
+
self.play(self.camera.auto_zoom(scene))
|
|
83
|
+
self.wait()
|
|
84
|
+
|
|
85
|
+
self.play(self.camera.auto_zoom(group, margin=2))
|
|
67
86
|
"""
|
|
68
87
|
|
|
69
88
|
from __future__ import annotations
|
|
@@ -83,8 +102,12 @@ class MovingCameraScene(Scene):
|
|
|
83
102
|
This is a Scene, with special configurations and properties that
|
|
84
103
|
make it suitable for cases where the camera must be moved around.
|
|
85
104
|
|
|
105
|
+
Note: Examples are included in the moving_camera_scene module
|
|
106
|
+
documentation, see below in the 'see also' section.
|
|
107
|
+
|
|
86
108
|
.. SEEALSO::
|
|
87
109
|
|
|
110
|
+
:mod:`.moving_camera_scene`
|
|
88
111
|
:class:`.MovingCamera`
|
|
89
112
|
"""
|
|
90
113
|
|
manim/scene/scene.py
CHANGED
|
@@ -52,7 +52,10 @@ from ..utils.file_ops import open_media_file
|
|
|
52
52
|
from ..utils.iterables import list_difference_update, list_update
|
|
53
53
|
|
|
54
54
|
if TYPE_CHECKING:
|
|
55
|
-
from
|
|
55
|
+
from collections.abc import Sequence
|
|
56
|
+
from typing import Callable
|
|
57
|
+
|
|
58
|
+
from manim.mobject.mobject import _AnimationBuilder
|
|
56
59
|
|
|
57
60
|
|
|
58
61
|
class RerunSceneHandler(FileSystemEventHandler):
|
|
@@ -99,12 +102,12 @@ class Scene:
|
|
|
99
102
|
|
|
100
103
|
def __init__(
|
|
101
104
|
self,
|
|
102
|
-
renderer=None,
|
|
103
|
-
camera_class=Camera,
|
|
104
|
-
always_update_mobjects=False,
|
|
105
|
-
random_seed=None,
|
|
106
|
-
skip_animations=False,
|
|
107
|
-
):
|
|
105
|
+
renderer: CairoRenderer | OpenGLRenderer | None = None,
|
|
106
|
+
camera_class: type[Camera] = Camera,
|
|
107
|
+
always_update_mobjects: bool = False,
|
|
108
|
+
random_seed: int | None = None,
|
|
109
|
+
skip_animations: bool = False,
|
|
110
|
+
) -> None:
|
|
108
111
|
self.camera_class = camera_class
|
|
109
112
|
self.always_update_mobjects = always_update_mobjects
|
|
110
113
|
self.random_seed = random_seed
|
|
@@ -157,6 +160,11 @@ class Scene:
|
|
|
157
160
|
def camera(self):
|
|
158
161
|
return self.renderer.camera
|
|
159
162
|
|
|
163
|
+
@property
|
|
164
|
+
def time(self) -> float:
|
|
165
|
+
"""The time since the start of the scene."""
|
|
166
|
+
return self.renderer.time
|
|
167
|
+
|
|
160
168
|
def __deepcopy__(self, clone_from_id):
|
|
161
169
|
cls = self.__class__
|
|
162
170
|
result = cls.__new__(cls)
|
|
@@ -229,7 +237,7 @@ class Scene:
|
|
|
229
237
|
self.construct()
|
|
230
238
|
except EndSceneEarlyException:
|
|
231
239
|
pass
|
|
232
|
-
except RerunSceneException
|
|
240
|
+
except RerunSceneException:
|
|
233
241
|
self.remove(*self.mobjects)
|
|
234
242
|
self.renderer.clear_screen()
|
|
235
243
|
self.renderer.num_plays = 0
|
|
@@ -307,14 +315,14 @@ class Scene:
|
|
|
307
315
|
def next_section(
|
|
308
316
|
self,
|
|
309
317
|
name: str = "unnamed",
|
|
310
|
-
|
|
318
|
+
section_type: str = DefaultSectionType.NORMAL,
|
|
311
319
|
skip_animations: bool = False,
|
|
312
320
|
) -> None:
|
|
313
321
|
"""Create separation here; the last section gets finished and a new one gets created.
|
|
314
322
|
``skip_animations`` skips the rendering of all animations in this section.
|
|
315
323
|
Refer to :doc:`the documentation</tutorials/output_and_config>` on how to use sections.
|
|
316
324
|
"""
|
|
317
|
-
self.renderer.file_writer.next_section(name,
|
|
325
|
+
self.renderer.file_writer.next_section(name, section_type, skip_animations)
|
|
318
326
|
|
|
319
327
|
def __str__(self):
|
|
320
328
|
return self.__class__.__name__
|
|
@@ -480,7 +488,7 @@ class Scene:
|
|
|
480
488
|
self.moving_mobjects += mobjects
|
|
481
489
|
return self
|
|
482
490
|
|
|
483
|
-
def add_mobjects_from_animations(self, animations):
|
|
491
|
+
def add_mobjects_from_animations(self, animations: list[Animation]) -> None:
|
|
484
492
|
curr_mobjects = self.get_mobject_family_members()
|
|
485
493
|
for animation in animations:
|
|
486
494
|
if animation.is_introducer():
|
|
@@ -619,7 +627,7 @@ class Scene:
|
|
|
619
627
|
|
|
620
628
|
def restructure_mobjects(
|
|
621
629
|
self,
|
|
622
|
-
to_remove: Mobject,
|
|
630
|
+
to_remove: Sequence[Mobject],
|
|
623
631
|
mobject_list_name: str = "mobjects",
|
|
624
632
|
extract_families: bool = True,
|
|
625
633
|
):
|
|
@@ -679,7 +687,6 @@ class Scene:
|
|
|
679
687
|
list
|
|
680
688
|
The list of mobjects with the mobjects to remove removed.
|
|
681
689
|
"""
|
|
682
|
-
|
|
683
690
|
new_mobjects = []
|
|
684
691
|
|
|
685
692
|
def add_safe_mobjects_from_list(list_to_examine, set_to_remove):
|
|
@@ -873,7 +880,7 @@ class Scene:
|
|
|
873
880
|
|
|
874
881
|
def compile_animations(
|
|
875
882
|
self,
|
|
876
|
-
*args: Animation |
|
|
883
|
+
*args: Animation | Mobject | _AnimationBuilder,
|
|
877
884
|
**kwargs,
|
|
878
885
|
):
|
|
879
886
|
"""
|
|
@@ -898,16 +905,16 @@ class Scene:
|
|
|
898
905
|
for arg in arg_anims:
|
|
899
906
|
try:
|
|
900
907
|
animations.append(prepare_animation(arg))
|
|
901
|
-
except TypeError:
|
|
908
|
+
except TypeError as e:
|
|
902
909
|
if inspect.ismethod(arg):
|
|
903
910
|
raise TypeError(
|
|
904
911
|
"Passing Mobject methods to Scene.play is no longer"
|
|
905
912
|
" supported. Use Mobject.animate instead.",
|
|
906
|
-
)
|
|
913
|
+
) from e
|
|
907
914
|
else:
|
|
908
915
|
raise TypeError(
|
|
909
916
|
f"Unexpected argument {arg} passed to Scene.play().",
|
|
910
|
-
)
|
|
917
|
+
) from e
|
|
911
918
|
|
|
912
919
|
for animation in animations:
|
|
913
920
|
for k, v in kwargs.items():
|
|
@@ -1015,6 +1022,35 @@ class Scene:
|
|
|
1015
1022
|
)
|
|
1016
1023
|
return time_progression
|
|
1017
1024
|
|
|
1025
|
+
@classmethod
|
|
1026
|
+
def validate_run_time(
|
|
1027
|
+
cls,
|
|
1028
|
+
run_time: float,
|
|
1029
|
+
method: Callable[[Any, ...], Any],
|
|
1030
|
+
parameter_name: str = "run_time",
|
|
1031
|
+
) -> float:
|
|
1032
|
+
method_name = f"{cls.__name__}.{method.__name__}()"
|
|
1033
|
+
if run_time <= 0:
|
|
1034
|
+
raise ValueError(
|
|
1035
|
+
f"{method_name} has a {parameter_name} of "
|
|
1036
|
+
f"{run_time:g} <= 0 seconds which Manim cannot render. "
|
|
1037
|
+
f"The {parameter_name} must be a positive number."
|
|
1038
|
+
)
|
|
1039
|
+
|
|
1040
|
+
# config.frame_rate holds the number of frames per second
|
|
1041
|
+
fps = config.frame_rate
|
|
1042
|
+
seconds_per_frame = 1 / fps
|
|
1043
|
+
if run_time < seconds_per_frame:
|
|
1044
|
+
logger.warning(
|
|
1045
|
+
f"The original {parameter_name} of {method_name}, "
|
|
1046
|
+
f"{run_time:g} seconds, is too short for the current frame "
|
|
1047
|
+
f"rate of {fps:g} FPS. Rendering with the shortest possible "
|
|
1048
|
+
f"{parameter_name} of {seconds_per_frame:g} seconds instead."
|
|
1049
|
+
)
|
|
1050
|
+
run_time = seconds_per_frame
|
|
1051
|
+
|
|
1052
|
+
return run_time
|
|
1053
|
+
|
|
1018
1054
|
def get_run_time(self, animations: list[Animation]):
|
|
1019
1055
|
"""
|
|
1020
1056
|
Gets the total run time for a list of animations.
|
|
@@ -1030,16 +1066,13 @@ class Scene:
|
|
|
1030
1066
|
float
|
|
1031
1067
|
The total ``run_time`` of all of the animations in the list.
|
|
1032
1068
|
"""
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
else:
|
|
1038
|
-
return np.max([animation.run_time for animation in animations])
|
|
1069
|
+
run_time = max(animation.run_time for animation in animations)
|
|
1070
|
+
run_time = self.validate_run_time(run_time, self.play, "total run_time")
|
|
1071
|
+
return run_time
|
|
1039
1072
|
|
|
1040
1073
|
def play(
|
|
1041
1074
|
self,
|
|
1042
|
-
*args: Animation |
|
|
1075
|
+
*args: Animation | Mobject | _AnimationBuilder,
|
|
1043
1076
|
subcaption=None,
|
|
1044
1077
|
subcaption_duration=None,
|
|
1045
1078
|
subcaption_offset=0,
|
|
@@ -1088,15 +1121,15 @@ class Scene:
|
|
|
1088
1121
|
)
|
|
1089
1122
|
return
|
|
1090
1123
|
|
|
1091
|
-
start_time = self.
|
|
1124
|
+
start_time = self.time
|
|
1092
1125
|
self.renderer.play(self, *args, **kwargs)
|
|
1093
|
-
run_time = self.
|
|
1126
|
+
run_time = self.time - start_time
|
|
1094
1127
|
if subcaption:
|
|
1095
1128
|
if subcaption_duration is None:
|
|
1096
1129
|
subcaption_duration = run_time
|
|
1097
1130
|
# The start of the subcaption needs to be offset by the
|
|
1098
1131
|
# run_time of the animation because it is added after
|
|
1099
|
-
# the animation has already been played (and Scene.
|
|
1132
|
+
# the animation has already been played (and Scene.time
|
|
1100
1133
|
# has already been updated).
|
|
1101
1134
|
self.add_subcaption(
|
|
1102
1135
|
content=subcaption,
|
|
@@ -1131,6 +1164,7 @@ class Scene:
|
|
|
1131
1164
|
--------
|
|
1132
1165
|
:class:`.Wait`, :meth:`.should_mobjects_update`
|
|
1133
1166
|
"""
|
|
1167
|
+
duration = self.validate_run_time(duration, self.wait, "duration")
|
|
1134
1168
|
self.play(
|
|
1135
1169
|
Wait(
|
|
1136
1170
|
run_time=duration,
|
|
@@ -1154,6 +1188,7 @@ class Scene:
|
|
|
1154
1188
|
--------
|
|
1155
1189
|
:meth:`.wait`, :class:`.Wait`
|
|
1156
1190
|
"""
|
|
1191
|
+
duration = self.validate_run_time(duration, self.pause, "duration")
|
|
1157
1192
|
self.wait(duration=duration, frozen_frame=True)
|
|
1158
1193
|
|
|
1159
1194
|
def wait_until(self, stop_condition: Callable[[], bool], max_time: float = 60):
|
|
@@ -1167,11 +1202,12 @@ class Scene:
|
|
|
1167
1202
|
max_time
|
|
1168
1203
|
The maximum wait time in seconds.
|
|
1169
1204
|
"""
|
|
1205
|
+
max_time = self.validate_run_time(max_time, self.wait_until, "max_time")
|
|
1170
1206
|
self.wait(max_time, stop_condition=stop_condition)
|
|
1171
1207
|
|
|
1172
1208
|
def compile_animation_data(
|
|
1173
1209
|
self,
|
|
1174
|
-
*animations: Animation |
|
|
1210
|
+
*animations: Animation | Mobject | _AnimationBuilder,
|
|
1175
1211
|
**play_kwargs,
|
|
1176
1212
|
):
|
|
1177
1213
|
"""Given a list of animations, compile the corresponding
|
|
@@ -1205,16 +1241,16 @@ class Scene:
|
|
|
1205
1241
|
self.moving_mobjects = []
|
|
1206
1242
|
self.static_mobjects = []
|
|
1207
1243
|
|
|
1244
|
+
self.duration = self.get_run_time(self.animations)
|
|
1208
1245
|
if len(self.animations) == 1 and isinstance(self.animations[0], Wait):
|
|
1209
1246
|
if self.should_update_mobjects():
|
|
1210
1247
|
self.update_mobjects(dt=0) # Any problems with this?
|
|
1211
1248
|
self.stop_condition = self.animations[0].stop_condition
|
|
1212
1249
|
else:
|
|
1213
|
-
self.duration = self.animations[0].duration
|
|
1214
1250
|
# Static image logic when the wait is static is done by the renderer, not here.
|
|
1215
1251
|
self.animations[0].is_static_wait = True
|
|
1216
1252
|
return None
|
|
1217
|
-
|
|
1253
|
+
|
|
1218
1254
|
return self
|
|
1219
1255
|
|
|
1220
1256
|
def begin_animations(self) -> None:
|
|
@@ -1298,9 +1334,7 @@ class Scene:
|
|
|
1298
1334
|
return True
|
|
1299
1335
|
|
|
1300
1336
|
def interactive_embed(self):
|
|
1301
|
-
"""
|
|
1302
|
-
Like embed(), but allows for screen interaction.
|
|
1303
|
-
"""
|
|
1337
|
+
"""Like embed(), but allows for screen interaction."""
|
|
1304
1338
|
if not self.check_interactive_embed_is_valid():
|
|
1305
1339
|
return
|
|
1306
1340
|
self.interactive_mode = True
|
|
@@ -1508,7 +1542,7 @@ class Scene:
|
|
|
1508
1542
|
r"""Adds an entry in the corresponding subcaption file
|
|
1509
1543
|
at the current time stamp.
|
|
1510
1544
|
|
|
1511
|
-
The current time stamp is obtained from ``Scene.
|
|
1545
|
+
The current time stamp is obtained from ``Scene.time``.
|
|
1512
1546
|
|
|
1513
1547
|
Parameters
|
|
1514
1548
|
----------
|
|
@@ -1538,18 +1572,15 @@ class Scene:
|
|
|
1538
1572
|
|
|
1539
1573
|
# second option: within the call to Scene.play
|
|
1540
1574
|
self.play(
|
|
1541
|
-
Transform(square, circle),
|
|
1542
|
-
subcaption="The square transforms."
|
|
1575
|
+
Transform(square, circle), subcaption="The square transforms."
|
|
1543
1576
|
)
|
|
1544
1577
|
|
|
1545
1578
|
"""
|
|
1546
1579
|
subtitle = srt.Subtitle(
|
|
1547
1580
|
index=len(self.renderer.file_writer.subcaptions),
|
|
1548
1581
|
content=content,
|
|
1549
|
-
start=datetime.timedelta(seconds=float(self.
|
|
1550
|
-
end=datetime.timedelta(
|
|
1551
|
-
seconds=float(self.renderer.time + offset + duration)
|
|
1552
|
-
),
|
|
1582
|
+
start=datetime.timedelta(seconds=float(self.time + offset)),
|
|
1583
|
+
end=datetime.timedelta(seconds=float(self.time + offset + duration)),
|
|
1553
1584
|
)
|
|
1554
1585
|
self.renderer.file_writer.subcaptions.append(subtitle)
|
|
1555
1586
|
|
|
@@ -1597,7 +1628,7 @@ class Scene:
|
|
|
1597
1628
|
"""
|
|
1598
1629
|
if self.renderer.skip_animations:
|
|
1599
1630
|
return
|
|
1600
|
-
time = self.
|
|
1631
|
+
time = self.time + time_offset
|
|
1601
1632
|
self.renderer.file_writer.add_sound(sound_file, time, gain, **kwargs)
|
|
1602
1633
|
|
|
1603
1634
|
def on_mouse_motion(self, point, d_point):
|