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/renderer/cairo_renderer.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
from typing import Any
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
|
|
@@ -9,28 +9,38 @@ from manim.utils.hashing import get_hash_from_play_call
|
|
|
9
9
|
|
|
10
10
|
from .. import config, logger
|
|
11
11
|
from ..camera.camera import Camera
|
|
12
|
-
from ..mobject.mobject import Mobject
|
|
12
|
+
from ..mobject.mobject import Mobject, _AnimationBuilder
|
|
13
13
|
from ..scene.scene_file_writer import SceneFileWriter
|
|
14
14
|
from ..utils.exceptions import EndSceneEarlyException
|
|
15
15
|
from ..utils.iterables import list_update
|
|
16
16
|
|
|
17
|
-
if
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from manim.animation.animation import Animation
|
|
18
19
|
from manim.scene.scene import Scene
|
|
19
20
|
|
|
21
|
+
from ..typing import PixelArray
|
|
22
|
+
|
|
23
|
+
__all__ = ["CairoRenderer"]
|
|
24
|
+
|
|
20
25
|
|
|
21
26
|
class CairoRenderer:
|
|
22
27
|
"""A renderer using Cairo.
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
Attributes
|
|
30
|
+
----------
|
|
31
|
+
num_plays : int
|
|
32
|
+
Number of play() functions in the scene.
|
|
33
|
+
|
|
34
|
+
time : float
|
|
35
|
+
Time elapsed since initialisation of scene.
|
|
26
36
|
"""
|
|
27
37
|
|
|
28
38
|
def __init__(
|
|
29
39
|
self,
|
|
30
|
-
file_writer_class=SceneFileWriter,
|
|
31
|
-
camera_class=None,
|
|
32
|
-
skip_animations=False,
|
|
33
|
-
**kwargs,
|
|
40
|
+
file_writer_class: type[SceneFileWriter] = SceneFileWriter,
|
|
41
|
+
camera_class: type[Camera] | None = None,
|
|
42
|
+
skip_animations: bool = False,
|
|
43
|
+
**kwargs: Any,
|
|
34
44
|
):
|
|
35
45
|
# All of the following are set to EITHER the value passed via kwargs,
|
|
36
46
|
# OR the value stored in the global config dict at the time of
|
|
@@ -40,18 +50,23 @@ class CairoRenderer:
|
|
|
40
50
|
self.camera = camera_cls()
|
|
41
51
|
self._original_skipping_status = skip_animations
|
|
42
52
|
self.skip_animations = skip_animations
|
|
43
|
-
self.animations_hashes = []
|
|
53
|
+
self.animations_hashes: list[str | None] = []
|
|
44
54
|
self.num_plays = 0
|
|
45
|
-
self.time = 0
|
|
46
|
-
self.static_image = None
|
|
55
|
+
self.time = 0.0
|
|
56
|
+
self.static_image: PixelArray | None = None
|
|
47
57
|
|
|
48
|
-
def init_scene(self, scene):
|
|
58
|
+
def init_scene(self, scene: Scene) -> None:
|
|
49
59
|
self.file_writer: Any = self._file_writer_class(
|
|
50
60
|
self,
|
|
51
61
|
scene.__class__.__name__,
|
|
52
62
|
)
|
|
53
63
|
|
|
54
|
-
def play(
|
|
64
|
+
def play(
|
|
65
|
+
self,
|
|
66
|
+
scene: Scene,
|
|
67
|
+
*args: Animation | Mobject | _AnimationBuilder,
|
|
68
|
+
**kwargs: Any,
|
|
69
|
+
) -> None:
|
|
55
70
|
# Reset skip_animations to the original state.
|
|
56
71
|
# Needed when rendering only some animations, and skipping others.
|
|
57
72
|
self.skip_animations = self._original_skipping_status
|
|
@@ -68,6 +83,7 @@ class CairoRenderer:
|
|
|
68
83
|
logger.info("Caching disabled.")
|
|
69
84
|
hash_current_animation = f"uncached_{self.num_plays:05}"
|
|
70
85
|
else:
|
|
86
|
+
assert scene.animations is not None
|
|
71
87
|
hash_current_animation = get_hash_from_play_call(
|
|
72
88
|
scene,
|
|
73
89
|
self.camera,
|
|
@@ -108,12 +124,12 @@ class CairoRenderer:
|
|
|
108
124
|
|
|
109
125
|
def update_frame( # TODO Description in Docstring
|
|
110
126
|
self,
|
|
111
|
-
scene,
|
|
112
|
-
mobjects:
|
|
127
|
+
scene: Scene,
|
|
128
|
+
mobjects: Iterable[Mobject] | None = None,
|
|
113
129
|
include_submobjects: bool = True,
|
|
114
130
|
ignore_skipping: bool = True,
|
|
115
|
-
**kwargs,
|
|
116
|
-
):
|
|
131
|
+
**kwargs: Any,
|
|
132
|
+
) -> None:
|
|
117
133
|
"""Update the frame.
|
|
118
134
|
|
|
119
135
|
Parameters
|
|
@@ -128,7 +144,6 @@ class CairoRenderer:
|
|
|
128
144
|
ignore_skipping
|
|
129
145
|
|
|
130
146
|
**kwargs
|
|
131
|
-
|
|
132
147
|
"""
|
|
133
148
|
if self.skip_animations and not ignore_skipping:
|
|
134
149
|
return
|
|
@@ -145,25 +160,28 @@ class CairoRenderer:
|
|
|
145
160
|
kwargs["include_submobjects"] = include_submobjects
|
|
146
161
|
self.camera.capture_mobjects(mobjects, **kwargs)
|
|
147
162
|
|
|
148
|
-
def render(
|
|
163
|
+
def render(
|
|
164
|
+
self,
|
|
165
|
+
scene: Scene,
|
|
166
|
+
time: float,
|
|
167
|
+
moving_mobjects: Iterable[Mobject] | None = None,
|
|
168
|
+
) -> None:
|
|
149
169
|
self.update_frame(scene, moving_mobjects)
|
|
150
170
|
self.add_frame(self.get_frame())
|
|
151
171
|
|
|
152
|
-
def get_frame(self):
|
|
153
|
-
"""
|
|
154
|
-
Gets the current frame as NumPy array.
|
|
172
|
+
def get_frame(self) -> PixelArray:
|
|
173
|
+
"""Gets the current frame as NumPy array.
|
|
155
174
|
|
|
156
175
|
Returns
|
|
157
176
|
-------
|
|
158
|
-
|
|
177
|
+
PixelArray
|
|
159
178
|
NumPy array of pixel values of each pixel in screen.
|
|
160
|
-
The shape of the array is height x width x 3
|
|
179
|
+
The shape of the array is height x width x 3.
|
|
161
180
|
"""
|
|
162
181
|
return np.array(self.camera.pixel_array)
|
|
163
182
|
|
|
164
|
-
def add_frame(self, frame:
|
|
165
|
-
"""
|
|
166
|
-
Adds a frame to the video_file_stream
|
|
183
|
+
def add_frame(self, frame: PixelArray, num_frames: int = 1) -> None:
|
|
184
|
+
"""Adds a frame to the video_file_stream
|
|
167
185
|
|
|
168
186
|
Parameters
|
|
169
187
|
----------
|
|
@@ -176,10 +194,9 @@ class CairoRenderer:
|
|
|
176
194
|
if self.skip_animations:
|
|
177
195
|
return
|
|
178
196
|
self.time += num_frames * dt
|
|
179
|
-
|
|
180
|
-
self.file_writer.write_frame(frame)
|
|
197
|
+
self.file_writer.write_frame(frame, num_frames=num_frames)
|
|
181
198
|
|
|
182
|
-
def freeze_current_frame(self, duration: float):
|
|
199
|
+
def freeze_current_frame(self, duration: float) -> None:
|
|
183
200
|
"""Adds a static frame to the movie for a given duration. The static frame is the current frame.
|
|
184
201
|
|
|
185
202
|
Parameters
|
|
@@ -193,19 +210,18 @@ class CairoRenderer:
|
|
|
193
210
|
num_frames=int(duration / dt),
|
|
194
211
|
)
|
|
195
212
|
|
|
196
|
-
def show_frame(self):
|
|
197
|
-
"""
|
|
198
|
-
Opens the current frame in the Default Image Viewer
|
|
213
|
+
def show_frame(self, scene: Scene) -> None:
|
|
214
|
+
"""Opens the current frame in the Default Image Viewer
|
|
199
215
|
of your system.
|
|
200
216
|
"""
|
|
201
|
-
self.update_frame(ignore_skipping=True)
|
|
217
|
+
self.update_frame(scene, ignore_skipping=True)
|
|
202
218
|
self.camera.get_image().show()
|
|
203
219
|
|
|
204
220
|
def save_static_frame_data(
|
|
205
221
|
self,
|
|
206
222
|
scene: Scene,
|
|
207
|
-
static_mobjects:
|
|
208
|
-
) ->
|
|
223
|
+
static_mobjects: Iterable[Mobject],
|
|
224
|
+
) -> PixelArray | None:
|
|
209
225
|
"""Compute and save the static frame, that will be reused at each frame
|
|
210
226
|
to avoid unnecessarily computing static mobjects.
|
|
211
227
|
|
|
@@ -214,12 +230,12 @@ class CairoRenderer:
|
|
|
214
230
|
scene
|
|
215
231
|
The scene played.
|
|
216
232
|
static_mobjects
|
|
217
|
-
Static mobjects of the scene. If None, self.static_image is set to None
|
|
233
|
+
Static mobjects of the scene. If None, self.static_image is set to None.
|
|
218
234
|
|
|
219
235
|
Returns
|
|
220
236
|
-------
|
|
221
|
-
|
|
222
|
-
The static image computed.
|
|
237
|
+
PixelArray | None
|
|
238
|
+
The static image computed. The return value is None if there are no static mobjects in the scene.
|
|
223
239
|
"""
|
|
224
240
|
self.static_image = None
|
|
225
241
|
if not static_mobjects:
|
|
@@ -228,9 +244,8 @@ class CairoRenderer:
|
|
|
228
244
|
self.static_image = self.get_frame()
|
|
229
245
|
return self.static_image
|
|
230
246
|
|
|
231
|
-
def update_skipping_status(self):
|
|
232
|
-
"""
|
|
233
|
-
This method is used internally to check if the current
|
|
247
|
+
def update_skipping_status(self) -> None:
|
|
248
|
+
"""This method is used internally to check if the current
|
|
234
249
|
animation needs to be skipped or not. It also checks if
|
|
235
250
|
the number of animations that were played correspond to
|
|
236
251
|
the number of animations that need to be played, and
|
|
@@ -242,18 +257,18 @@ class CairoRenderer:
|
|
|
242
257
|
if config["save_last_frame"]:
|
|
243
258
|
self.skip_animations = True
|
|
244
259
|
if (
|
|
245
|
-
config
|
|
246
|
-
and self.num_plays < config
|
|
260
|
+
config.from_animation_number > 0
|
|
261
|
+
and self.num_plays < config.from_animation_number
|
|
247
262
|
):
|
|
248
263
|
self.skip_animations = True
|
|
249
264
|
if (
|
|
250
|
-
config
|
|
251
|
-
and self.num_plays > config
|
|
265
|
+
config.upto_animation_number >= 0
|
|
266
|
+
and self.num_plays > config.upto_animation_number
|
|
252
267
|
):
|
|
253
268
|
self.skip_animations = True
|
|
254
269
|
raise EndSceneEarlyException()
|
|
255
270
|
|
|
256
|
-
def scene_finished(self, scene):
|
|
271
|
+
def scene_finished(self, scene: Scene) -> None:
|
|
257
272
|
# If no animations in scene, render an image instead
|
|
258
273
|
if self.num_plays:
|
|
259
274
|
self.file_writer.finish()
|
|
@@ -267,4 +282,4 @@ class CairoRenderer:
|
|
|
267
282
|
if config["save_last_frame"]:
|
|
268
283
|
self.static_image = None
|
|
269
284
|
self.update_frame(scene)
|
|
270
|
-
self.file_writer.
|
|
285
|
+
self.file_writer.save_image(self.camera.get_image())
|
|
@@ -1,21 +1,21 @@
|
|
|
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
|
|
7
|
-
|
|
8
|
-
if sys.version_info < (3, 8):
|
|
9
|
-
from backports.cached_property import cached_property
|
|
10
|
-
else:
|
|
11
|
-
from functools import cached_property
|
|
6
|
+
from functools import cached_property
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
12
8
|
|
|
13
9
|
import moderngl
|
|
14
10
|
import numpy as np
|
|
15
11
|
from PIL import Image
|
|
16
12
|
|
|
17
13
|
from manim import config, logger
|
|
18
|
-
from manim.mobject.opengl.opengl_mobject import
|
|
14
|
+
from manim.mobject.opengl.opengl_mobject import (
|
|
15
|
+
OpenGLMobject,
|
|
16
|
+
OpenGLPoint,
|
|
17
|
+
_AnimationBuilder,
|
|
18
|
+
)
|
|
19
19
|
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
|
|
20
20
|
from manim.utils.caching import handle_caching_play
|
|
21
21
|
from manim.utils.color import color_to_rgba
|
|
@@ -39,6 +39,17 @@ from .vectorized_mobject_rendering import (
|
|
|
39
39
|
render_opengl_vectorized_mobject_stroke,
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from typing_extensions import Self
|
|
44
|
+
|
|
45
|
+
from manim.animation.animation import Animation
|
|
46
|
+
from manim.mobject.mobject import Mobject
|
|
47
|
+
from manim.scene.scene import Scene
|
|
48
|
+
from manim.typing import Point3D
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
__all__ = ["OpenGLCamera", "OpenGLRenderer"]
|
|
52
|
+
|
|
42
53
|
|
|
43
54
|
class OpenGLCamera(OpenGLMobject):
|
|
44
55
|
euler_angles = _Data()
|
|
@@ -65,12 +76,12 @@ class OpenGLCamera(OpenGLMobject):
|
|
|
65
76
|
if self.orthographic:
|
|
66
77
|
self.projection_matrix = opengl.orthographic_projection_matrix()
|
|
67
78
|
self.unformatted_projection_matrix = opengl.orthographic_projection_matrix(
|
|
68
|
-
|
|
79
|
+
format_=False,
|
|
69
80
|
)
|
|
70
81
|
else:
|
|
71
82
|
self.projection_matrix = opengl.perspective_projection_matrix()
|
|
72
83
|
self.unformatted_projection_matrix = opengl.perspective_projection_matrix(
|
|
73
|
-
|
|
84
|
+
format_=False,
|
|
74
85
|
)
|
|
75
86
|
|
|
76
87
|
if frame_shape is None:
|
|
@@ -104,7 +115,7 @@ class OpenGLCamera(OpenGLMobject):
|
|
|
104
115
|
self.euler_angles = euler_angles
|
|
105
116
|
self.refresh_rotation_matrix()
|
|
106
117
|
|
|
107
|
-
def get_position(self):
|
|
118
|
+
def get_position(self) -> Point3D:
|
|
108
119
|
return self.model_matrix[:, 3][:3]
|
|
109
120
|
|
|
110
121
|
def set_position(self, position):
|
|
@@ -125,7 +136,7 @@ class OpenGLCamera(OpenGLMobject):
|
|
|
125
136
|
self.set_height(self.frame_shape[1], stretch=True)
|
|
126
137
|
self.move_to(self.center_point)
|
|
127
138
|
|
|
128
|
-
def to_default_state(self):
|
|
139
|
+
def to_default_state(self) -> Self:
|
|
129
140
|
self.center()
|
|
130
141
|
self.set_height(config["frame_height"])
|
|
131
142
|
self.set_width(config["frame_width"])
|
|
@@ -168,28 +179,28 @@ class OpenGLCamera(OpenGLMobject):
|
|
|
168
179
|
self.refresh_rotation_matrix()
|
|
169
180
|
return self
|
|
170
181
|
|
|
171
|
-
def set_theta(self, theta):
|
|
182
|
+
def set_theta(self, theta: float) -> Self:
|
|
172
183
|
return self.set_euler_angles(theta=theta)
|
|
173
184
|
|
|
174
|
-
def set_phi(self, phi):
|
|
185
|
+
def set_phi(self, phi: float) -> Self:
|
|
175
186
|
return self.set_euler_angles(phi=phi)
|
|
176
187
|
|
|
177
|
-
def set_gamma(self, gamma):
|
|
188
|
+
def set_gamma(self, gamma: float) -> Self:
|
|
178
189
|
return self.set_euler_angles(gamma=gamma)
|
|
179
190
|
|
|
180
|
-
def increment_theta(self, dtheta):
|
|
191
|
+
def increment_theta(self, dtheta: float) -> Self:
|
|
181
192
|
self.euler_angles[0] += dtheta
|
|
182
193
|
self.refresh_rotation_matrix()
|
|
183
194
|
return self
|
|
184
195
|
|
|
185
|
-
def increment_phi(self, dphi):
|
|
196
|
+
def increment_phi(self, dphi: float) -> Self:
|
|
186
197
|
phi = self.euler_angles[1]
|
|
187
198
|
new_phi = clip(phi + dphi, -PI / 2, PI / 2)
|
|
188
199
|
self.euler_angles[1] = new_phi
|
|
189
200
|
self.refresh_rotation_matrix()
|
|
190
201
|
return self
|
|
191
202
|
|
|
192
|
-
def increment_gamma(self, dgamma):
|
|
203
|
+
def increment_gamma(self, dgamma: float) -> Self:
|
|
193
204
|
self.euler_angles[2] += dgamma
|
|
194
205
|
self.refresh_rotation_matrix()
|
|
195
206
|
return self
|
|
@@ -201,15 +212,15 @@ class OpenGLCamera(OpenGLMobject):
|
|
|
201
212
|
# Assumes first point is at the center
|
|
202
213
|
return self.points[0]
|
|
203
214
|
|
|
204
|
-
def get_width(self):
|
|
215
|
+
def get_width(self) -> float:
|
|
205
216
|
points = self.points
|
|
206
217
|
return points[2, 0] - points[1, 0]
|
|
207
218
|
|
|
208
|
-
def get_height(self):
|
|
219
|
+
def get_height(self) -> float:
|
|
209
220
|
points = self.points
|
|
210
221
|
return points[4, 1] - points[3, 1]
|
|
211
222
|
|
|
212
|
-
def get_focal_distance(self):
|
|
223
|
+
def get_focal_distance(self) -> float:
|
|
213
224
|
return self.focal_distance * self.get_height()
|
|
214
225
|
|
|
215
226
|
def interpolate(self, *args, **kwargs):
|
|
@@ -217,11 +228,12 @@ class OpenGLCamera(OpenGLMobject):
|
|
|
217
228
|
self.refresh_rotation_matrix()
|
|
218
229
|
|
|
219
230
|
|
|
220
|
-
points_per_curve = 3
|
|
221
|
-
|
|
222
|
-
|
|
223
231
|
class OpenGLRenderer:
|
|
224
|
-
def __init__(
|
|
232
|
+
def __init__(
|
|
233
|
+
self,
|
|
234
|
+
file_writer_class: type[SceneFileWriter] = SceneFileWriter,
|
|
235
|
+
skip_animations: bool = False,
|
|
236
|
+
) -> None:
|
|
225
237
|
# Measured in pixel widths, used for vector graphics
|
|
226
238
|
self.anti_alias_width = 1.5
|
|
227
239
|
self._file_writer_class = file_writer_class
|
|
@@ -237,12 +249,14 @@ class OpenGLRenderer:
|
|
|
237
249
|
self.camera = OpenGLCamera()
|
|
238
250
|
self.pressed_keys = set()
|
|
239
251
|
|
|
252
|
+
self.window = None
|
|
253
|
+
|
|
240
254
|
# Initialize texture map.
|
|
241
255
|
self.path_to_texture_id = {}
|
|
242
256
|
|
|
243
257
|
self.background_color = config["background_color"]
|
|
244
258
|
|
|
245
|
-
def init_scene(self, scene):
|
|
259
|
+
def init_scene(self, scene: Scene) -> None:
|
|
246
260
|
self.partial_movie_files = []
|
|
247
261
|
self.file_writer: Any = self._file_writer_class(
|
|
248
262
|
self,
|
|
@@ -250,32 +264,31 @@ class OpenGLRenderer:
|
|
|
250
264
|
)
|
|
251
265
|
self.scene = scene
|
|
252
266
|
self.background_color = config["background_color"]
|
|
253
|
-
if
|
|
254
|
-
|
|
255
|
-
from .opengl_renderer_window import Window
|
|
267
|
+
if self.should_create_window():
|
|
268
|
+
from .opengl_renderer_window import Window
|
|
256
269
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
270
|
+
self.window = Window(self)
|
|
271
|
+
self.context = self.window.ctx
|
|
272
|
+
self.frame_buffer_object = self.context.detect_framebuffer()
|
|
273
|
+
else:
|
|
274
|
+
# self.window = None
|
|
275
|
+
try:
|
|
276
|
+
self.context = moderngl.create_context(standalone=True)
|
|
277
|
+
except Exception:
|
|
278
|
+
self.context = moderngl.create_context(
|
|
279
|
+
standalone=True,
|
|
280
|
+
backend="egl",
|
|
281
|
+
)
|
|
282
|
+
self.frame_buffer_object = self.get_frame_buffer_object(self.context, 0)
|
|
283
|
+
self.frame_buffer_object.use()
|
|
284
|
+
self.context.enable(moderngl.BLEND)
|
|
285
|
+
self.context.wireframe = config["enable_wireframe"]
|
|
286
|
+
self.context.blend_func = (
|
|
287
|
+
moderngl.SRC_ALPHA,
|
|
288
|
+
moderngl.ONE_MINUS_SRC_ALPHA,
|
|
289
|
+
moderngl.ONE,
|
|
290
|
+
moderngl.ONE,
|
|
291
|
+
)
|
|
279
292
|
|
|
280
293
|
def should_create_window(self):
|
|
281
294
|
if config["force_window"]:
|
|
@@ -342,10 +355,8 @@ class OpenGLRenderer:
|
|
|
342
355
|
shader_wrapper.uniforms.items(),
|
|
343
356
|
self.perspective_uniforms.items(),
|
|
344
357
|
):
|
|
345
|
-
|
|
358
|
+
with contextlib.suppress(KeyError):
|
|
346
359
|
shader.set_uniform(name, value)
|
|
347
|
-
except KeyError:
|
|
348
|
-
pass
|
|
349
360
|
try:
|
|
350
361
|
shader.set_uniform(
|
|
351
362
|
"u_view_matrix", self.scene.camera.formatted_view_matrix
|
|
@@ -391,9 +402,8 @@ class OpenGLRenderer:
|
|
|
391
402
|
|
|
392
403
|
return self.path_to_texture_id[repr(path)]
|
|
393
404
|
|
|
394
|
-
def update_skipping_status(self):
|
|
395
|
-
"""
|
|
396
|
-
This method is used internally to check if the current
|
|
405
|
+
def update_skipping_status(self) -> None:
|
|
406
|
+
"""This method is used internally to check if the current
|
|
397
407
|
animation needs to be skipped or not. It also checks if
|
|
398
408
|
the number of animations that were played correspond to
|
|
399
409
|
the number of animations that need to be played, and
|
|
@@ -403,19 +413,24 @@ class OpenGLRenderer:
|
|
|
403
413
|
if self.file_writer.sections[-1].skip_animations:
|
|
404
414
|
self.skip_animations = True
|
|
405
415
|
if (
|
|
406
|
-
config
|
|
407
|
-
and self.num_plays < config
|
|
416
|
+
config.from_animation_number > 0
|
|
417
|
+
and self.num_plays < config.from_animation_number
|
|
408
418
|
):
|
|
409
419
|
self.skip_animations = True
|
|
410
420
|
if (
|
|
411
|
-
config
|
|
412
|
-
and self.num_plays > config
|
|
421
|
+
config.upto_animation_number >= 0
|
|
422
|
+
and self.num_plays > config.upto_animation_number
|
|
413
423
|
):
|
|
414
424
|
self.skip_animations = True
|
|
415
425
|
raise EndSceneEarlyException()
|
|
416
426
|
|
|
417
427
|
@handle_caching_play
|
|
418
|
-
def play(
|
|
428
|
+
def play(
|
|
429
|
+
self,
|
|
430
|
+
scene: Scene,
|
|
431
|
+
*args: Animation | Mobject | _AnimationBuilder,
|
|
432
|
+
**kwargs: Any,
|
|
433
|
+
) -> None:
|
|
419
434
|
# TODO: Handle data locking / unlocking.
|
|
420
435
|
self.animation_start_time = time.time()
|
|
421
436
|
self.file_writer.begin_animation(not self.skip_animations)
|
|
@@ -426,8 +441,9 @@ class OpenGLRenderer:
|
|
|
426
441
|
self.update_frame(scene)
|
|
427
442
|
|
|
428
443
|
if not self.skip_animations:
|
|
429
|
-
|
|
430
|
-
self.
|
|
444
|
+
self.file_writer.write_frame(
|
|
445
|
+
self, num_frames=int(config.frame_rate * scene.duration)
|
|
446
|
+
)
|
|
431
447
|
|
|
432
448
|
if self.window is not None:
|
|
433
449
|
self.window.swap_buffers()
|
|
@@ -442,11 +458,13 @@ class OpenGLRenderer:
|
|
|
442
458
|
self.time += scene.duration
|
|
443
459
|
self.num_plays += 1
|
|
444
460
|
|
|
445
|
-
def clear_screen(self):
|
|
461
|
+
def clear_screen(self) -> None:
|
|
446
462
|
self.frame_buffer_object.clear(*self.background_color)
|
|
447
463
|
self.window.swap_buffers()
|
|
448
464
|
|
|
449
|
-
def render(
|
|
465
|
+
def render(
|
|
466
|
+
self, scene: Scene, frame_offset, moving_mobjects: list[Mobject]
|
|
467
|
+
) -> None:
|
|
450
468
|
self.update_frame(scene)
|
|
451
469
|
|
|
452
470
|
if self.skip_animations:
|
|
@@ -487,7 +505,7 @@ class OpenGLRenderer:
|
|
|
487
505
|
if self.should_save_last_frame():
|
|
488
506
|
config.save_last_frame = True
|
|
489
507
|
self.update_frame(scene)
|
|
490
|
-
self.file_writer.
|
|
508
|
+
self.file_writer.save_image(self.get_image())
|
|
491
509
|
|
|
492
510
|
def should_save_last_frame(self):
|
|
493
511
|
if config["save_last_frame"]:
|
|
@@ -568,12 +586,14 @@ class OpenGLRenderer:
|
|
|
568
586
|
# Returns offset from the bottom left corner in pixels.
|
|
569
587
|
# top_left flag should be set to True when using a GUI framework
|
|
570
588
|
# where the (0,0) is at the top left: e.g. PySide6
|
|
571
|
-
def pixel_coords_to_space_coords(
|
|
589
|
+
def pixel_coords_to_space_coords(
|
|
590
|
+
self, px: float, py: float, relative: bool = False, top_left: bool = False
|
|
591
|
+
) -> Point3D:
|
|
572
592
|
pixel_shape = self.get_pixel_shape()
|
|
573
593
|
if pixel_shape is None:
|
|
574
594
|
return np.array([0, 0, 0])
|
|
575
595
|
pw, ph = pixel_shape
|
|
576
|
-
|
|
596
|
+
fh = config["frame_height"]
|
|
577
597
|
fc = self.camera.get_center()
|
|
578
598
|
if relative:
|
|
579
599
|
return 2 * np.array([px / pw, py / ph, 0])
|