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.
Files changed (163) hide show
  1. manim/__init__.py +11 -6
  2. manim/__main__.py +62 -19
  3. manim/_config/__init__.py +10 -9
  4. manim/_config/cli_colors.py +26 -9
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +23 -13
  7. manim/_config/utils.py +662 -468
  8. manim/animation/animation.py +164 -18
  9. manim/animation/changing.py +34 -23
  10. manim/animation/composition.py +265 -67
  11. manim/animation/creation.py +208 -26
  12. manim/animation/fading.py +16 -18
  13. manim/animation/growing.py +35 -15
  14. manim/animation/indication.py +150 -76
  15. manim/animation/movement.py +56 -22
  16. manim/animation/numbers.py +64 -6
  17. manim/animation/rotation.py +78 -7
  18. manim/animation/specialized.py +6 -7
  19. manim/animation/speedmodifier.py +13 -10
  20. manim/animation/transform.py +14 -11
  21. manim/animation/transform_matching_parts.py +3 -4
  22. manim/animation/updaters/mobject_update_utils.py +152 -30
  23. manim/animation/updaters/update.py +10 -7
  24. manim/camera/camera.py +182 -118
  25. manim/camera/mapping_camera.py +34 -3
  26. manim/camera/moving_camera.py +95 -74
  27. manim/camera/multi_camera.py +23 -15
  28. manim/camera/three_d_camera.py +70 -52
  29. manim/cli/__init__.py +17 -0
  30. manim/cli/cfg/group.py +76 -44
  31. manim/cli/checkhealth/checks.py +192 -0
  32. manim/cli/checkhealth/commands.py +90 -0
  33. manim/cli/default_group.py +158 -25
  34. manim/cli/init/commands.py +33 -25
  35. manim/cli/plugins/commands.py +16 -3
  36. manim/cli/render/commands.py +72 -60
  37. manim/cli/render/ease_of_access_options.py +4 -3
  38. manim/cli/render/global_options.py +59 -17
  39. manim/cli/render/output_options.py +6 -5
  40. manim/cli/render/render_options.py +98 -33
  41. manim/constants.py +109 -59
  42. manim/data_structures.py +31 -0
  43. manim/mobject/frame.py +8 -5
  44. manim/mobject/geometry/__init__.py +1 -0
  45. manim/mobject/geometry/arc.py +277 -135
  46. manim/mobject/geometry/boolean_ops.py +32 -31
  47. manim/mobject/geometry/labeled.py +376 -0
  48. manim/mobject/geometry/line.py +192 -87
  49. manim/mobject/geometry/polygram.py +224 -58
  50. manim/mobject/geometry/shape_matchers.py +61 -25
  51. manim/mobject/geometry/tips.py +122 -48
  52. manim/mobject/graph.py +1027 -419
  53. manim/mobject/graphing/coordinate_systems.py +533 -278
  54. manim/mobject/graphing/functions.py +53 -32
  55. manim/mobject/graphing/number_line.py +123 -65
  56. manim/mobject/graphing/probability.py +88 -62
  57. manim/mobject/graphing/scale.py +33 -19
  58. manim/mobject/logo.py +118 -28
  59. manim/mobject/matrix.py +87 -83
  60. manim/mobject/mobject.py +912 -442
  61. manim/mobject/opengl/dot_cloud.py +16 -5
  62. manim/mobject/opengl/opengl_compatibility.py +4 -2
  63. manim/mobject/opengl/opengl_geometry.py +254 -153
  64. manim/mobject/opengl/opengl_image_mobject.py +3 -1
  65. manim/mobject/opengl/opengl_mobject.py +779 -482
  66. manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
  67. manim/mobject/opengl/opengl_surface.py +14 -92
  68. manim/mobject/opengl/opengl_three_dimensions.py +12 -8
  69. manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
  70. manim/mobject/svg/brace.py +173 -41
  71. manim/mobject/svg/svg_mobject.py +139 -53
  72. manim/mobject/table.py +61 -68
  73. manim/mobject/text/code_mobject.py +193 -539
  74. manim/mobject/text/numbers.py +81 -34
  75. manim/mobject/text/tex_mobject.py +130 -78
  76. manim/mobject/text/text_mobject.py +288 -164
  77. manim/mobject/three_d/polyhedra.py +111 -13
  78. manim/mobject/three_d/three_d_utils.py +17 -8
  79. manim/mobject/three_d/three_dimensions.py +239 -106
  80. manim/mobject/types/image_mobject.py +50 -30
  81. manim/mobject/types/point_cloud_mobject.py +120 -75
  82. manim/mobject/types/vectorized_mobject.py +841 -408
  83. manim/mobject/value_tracker.py +105 -38
  84. manim/mobject/vector_field.py +50 -31
  85. manim/opengl/__init__.py +3 -3
  86. manim/plugins/__init__.py +14 -1
  87. manim/plugins/plugins_flags.py +10 -14
  88. manim/renderer/cairo_renderer.py +65 -50
  89. manim/renderer/opengl_renderer.py +89 -69
  90. manim/renderer/opengl_renderer_window.py +39 -18
  91. manim/renderer/shader.py +123 -87
  92. manim/renderer/shader_wrapper.py +44 -28
  93. manim/renderer/vectorized_mobject_rendering.py +38 -10
  94. manim/scene/moving_camera_scene.py +32 -3
  95. manim/scene/scene.py +507 -242
  96. manim/scene/scene_file_writer.py +371 -220
  97. manim/scene/section.py +20 -16
  98. manim/scene/three_d_scene.py +14 -22
  99. manim/scene/vector_space_scene.py +223 -129
  100. manim/scene/zoomed_scene.py +46 -41
  101. manim/typing.py +990 -0
  102. manim/utils/bezier.py +1823 -371
  103. manim/utils/caching.py +12 -5
  104. manim/utils/color/AS2700.py +236 -0
  105. manim/utils/color/BS381.py +318 -0
  106. manim/utils/color/DVIPSNAMES.py +96 -0
  107. manim/utils/color/SVGNAMES.py +179 -0
  108. manim/utils/color/X11.py +533 -0
  109. manim/utils/color/XKCD.py +952 -0
  110. manim/utils/color/__init__.py +61 -0
  111. manim/utils/color/core.py +1667 -0
  112. manim/utils/color/manim_colors.py +218 -0
  113. manim/utils/commands.py +48 -20
  114. manim/utils/config_ops.py +39 -19
  115. manim/utils/debug.py +8 -7
  116. manim/utils/deprecation.py +86 -39
  117. manim/utils/docbuild/__init__.py +17 -0
  118. manim/utils/docbuild/autoaliasattr_directive.py +236 -0
  119. manim/utils/docbuild/autocolor_directive.py +99 -0
  120. manim/utils/docbuild/manim_directive.py +94 -41
  121. manim/utils/docbuild/module_parsing.py +245 -0
  122. manim/utils/exceptions.py +6 -0
  123. manim/utils/family.py +5 -3
  124. manim/utils/family_ops.py +17 -4
  125. manim/utils/file_ops.py +27 -17
  126. manim/utils/hashing.py +55 -45
  127. manim/utils/images.py +13 -7
  128. manim/utils/ipython_magic.py +13 -7
  129. manim/utils/iterables.py +163 -120
  130. manim/utils/module_ops.py +66 -24
  131. manim/utils/opengl.py +77 -24
  132. manim/utils/parameter_parsing.py +32 -0
  133. manim/utils/paths.py +30 -33
  134. manim/utils/polylabel.py +235 -0
  135. manim/utils/qhull.py +218 -0
  136. manim/utils/rate_functions.py +98 -32
  137. manim/utils/simple_functions.py +25 -33
  138. manim/utils/sounds.py +7 -1
  139. manim/utils/space_ops.py +188 -115
  140. manim/utils/testing/__init__.py +17 -0
  141. manim/utils/testing/_frames_testers.py +13 -8
  142. manim/utils/testing/_show_diff.py +5 -3
  143. manim/utils/testing/_test_class_makers.py +34 -18
  144. manim/utils/testing/frames_comparison.py +37 -19
  145. manim/utils/tex.py +130 -198
  146. manim/utils/tex_file_writing.py +77 -47
  147. manim/utils/tex_templates.py +2 -1
  148. manim/utils/unit.py +6 -5
  149. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
  150. manim-0.19.1.dist-info/RECORD +220 -0
  151. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
  152. manim-0.19.1.dist-info/entry_points.txt +3 -0
  153. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
  154. manim/cli/new/group.py +0 -189
  155. manim/communitycolors.py +0 -9
  156. manim/gui/__init__.py +0 -0
  157. manim/gui/gui.py +0 -82
  158. manim/plugins/import_plugins.py +0 -43
  159. manim/utils/color.py +0 -552
  160. manim-0.17.0.dist-info/RECORD +0 -206
  161. manim-0.17.0.dist-info/entry_points.txt +0 -4
  162. /manim/cli/{new → checkhealth}/__init__.py +0 -0
  163. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- import typing
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 typing.TYPE_CHECKING:
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
- num_plays : Number of play() functions in the scene.
25
- time: time elapsed since initialisation of scene.
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(self, scene, *args, **kwargs):
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: typing.Iterable[Mobject] | None = None,
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(self, scene, time, moving_mobjects):
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
- np.array
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: np.ndarray, num_frames: int = 1):
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
- for _ in range(num_frames):
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: typing.Iterable[Mobject],
208
- ) -> typing.Iterable[Mobject] | None:
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
- typing.Iterable[Mobject]
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["from_animation_number"]
246
- and self.num_plays < config["from_animation_number"]
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["upto_animation_number"]
251
- and self.num_plays > config["upto_animation_number"]
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.save_final_image(self.camera.get_image())
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 typing import Any
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 OpenGLMobject, OpenGLPoint
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
- format=False,
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
- format=False,
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__(self, file_writer_class=SceneFileWriter, skip_animations=False):
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 not hasattr(self, "window"):
254
- if self.should_create_window():
255
- from .opengl_renderer_window import Window
267
+ if self.should_create_window():
268
+ from .opengl_renderer_window import Window
256
269
 
257
- self.window = Window(self)
258
- self.context = self.window.ctx
259
- self.frame_buffer_object = self.context.detect_framebuffer()
260
- else:
261
- self.window = None
262
- try:
263
- self.context = moderngl.create_context(standalone=True)
264
- except Exception:
265
- self.context = moderngl.create_context(
266
- standalone=True,
267
- backend="egl",
268
- )
269
- self.frame_buffer_object = self.get_frame_buffer_object(self.context, 0)
270
- self.frame_buffer_object.use()
271
- self.context.enable(moderngl.BLEND)
272
- self.context.wireframe = config["enable_wireframe"]
273
- self.context.blend_func = (
274
- moderngl.SRC_ALPHA,
275
- moderngl.ONE_MINUS_SRC_ALPHA,
276
- moderngl.ONE,
277
- moderngl.ONE,
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
- try:
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["from_animation_number"]
407
- and self.num_plays < config["from_animation_number"]
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["upto_animation_number"]
412
- and self.num_plays > config["upto_animation_number"]
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(self, scene, *args, **kwargs):
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
- for _ in range(int(config.frame_rate * scene.duration)):
430
- self.file_writer.write_frame(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(self, scene, frame_offset, moving_mobjects):
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.save_final_image(self.get_image())
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(self, px, py, relative=False, top_left=False):
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
- fw, fh = config["frame_width"], config["frame_height"]
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])