manim 0.18.0.post0__py3-none-any.whl → 0.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of manim might be problematic. Click here for more details.

Files changed (146) hide show
  1. manim/__init__.py +3 -6
  2. manim/__main__.py +61 -20
  3. manim/_config/__init__.py +6 -3
  4. manim/_config/cli_colors.py +16 -8
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +14 -8
  7. manim/_config/utils.py +651 -472
  8. manim/animation/animation.py +152 -5
  9. manim/animation/composition.py +80 -39
  10. manim/animation/creation.py +196 -14
  11. manim/animation/fading.py +5 -9
  12. manim/animation/indication.py +103 -47
  13. manim/animation/movement.py +22 -5
  14. manim/animation/rotation.py +3 -2
  15. manim/animation/specialized.py +4 -6
  16. manim/animation/speedmodifier.py +10 -5
  17. manim/animation/transform.py +4 -5
  18. manim/animation/transform_matching_parts.py +1 -1
  19. manim/animation/updaters/mobject_update_utils.py +17 -14
  20. manim/camera/camera.py +15 -6
  21. manim/cli/__init__.py +17 -0
  22. manim/cli/cfg/group.py +70 -44
  23. manim/cli/checkhealth/checks.py +93 -75
  24. manim/cli/checkhealth/commands.py +14 -5
  25. manim/cli/default_group.py +157 -25
  26. manim/cli/init/commands.py +32 -24
  27. manim/cli/plugins/commands.py +16 -3
  28. manim/cli/render/commands.py +72 -60
  29. manim/cli/render/ease_of_access_options.py +4 -3
  30. manim/cli/render/global_options.py +51 -15
  31. manim/cli/render/output_options.py +6 -5
  32. manim/cli/render/render_options.py +97 -32
  33. manim/constants.py +65 -19
  34. manim/gui/gui.py +2 -0
  35. manim/mobject/frame.py +0 -1
  36. manim/mobject/geometry/arc.py +112 -78
  37. manim/mobject/geometry/boolean_ops.py +32 -25
  38. manim/mobject/geometry/labeled.py +300 -77
  39. manim/mobject/geometry/line.py +132 -64
  40. manim/mobject/geometry/polygram.py +126 -30
  41. manim/mobject/geometry/shape_matchers.py +35 -15
  42. manim/mobject/geometry/tips.py +38 -29
  43. manim/mobject/graph.py +414 -133
  44. manim/mobject/graphing/coordinate_systems.py +126 -64
  45. manim/mobject/graphing/functions.py +25 -15
  46. manim/mobject/graphing/number_line.py +24 -10
  47. manim/mobject/graphing/probability.py +2 -10
  48. manim/mobject/graphing/scale.py +6 -5
  49. manim/mobject/matrix.py +17 -19
  50. manim/mobject/mobject.py +314 -165
  51. manim/mobject/opengl/opengl_compatibility.py +2 -0
  52. manim/mobject/opengl/opengl_geometry.py +30 -9
  53. manim/mobject/opengl/opengl_image_mobject.py +2 -0
  54. manim/mobject/opengl/opengl_mobject.py +509 -343
  55. manim/mobject/opengl/opengl_point_cloud_mobject.py +5 -7
  56. manim/mobject/opengl/opengl_surface.py +3 -2
  57. manim/mobject/opengl/opengl_three_dimensions.py +2 -0
  58. manim/mobject/opengl/opengl_vectorized_mobject.py +46 -79
  59. manim/mobject/svg/brace.py +63 -13
  60. manim/mobject/svg/svg_mobject.py +4 -3
  61. manim/mobject/table.py +11 -13
  62. manim/mobject/text/code_mobject.py +186 -548
  63. manim/mobject/text/numbers.py +9 -7
  64. manim/mobject/text/tex_mobject.py +23 -14
  65. manim/mobject/text/text_mobject.py +70 -24
  66. manim/mobject/three_d/polyhedra.py +98 -1
  67. manim/mobject/three_d/three_d_utils.py +4 -4
  68. manim/mobject/three_d/three_dimensions.py +62 -34
  69. manim/mobject/types/image_mobject.py +42 -24
  70. manim/mobject/types/point_cloud_mobject.py +105 -67
  71. manim/mobject/types/vectorized_mobject.py +496 -228
  72. manim/mobject/value_tracker.py +5 -4
  73. manim/mobject/vector_field.py +5 -5
  74. manim/opengl/__init__.py +3 -3
  75. manim/plugins/__init__.py +14 -1
  76. manim/plugins/plugins_flags.py +14 -8
  77. manim/renderer/cairo_renderer.py +20 -10
  78. manim/renderer/opengl_renderer.py +21 -23
  79. manim/renderer/opengl_renderer_window.py +2 -0
  80. manim/renderer/shader.py +2 -3
  81. manim/renderer/shader_wrapper.py +5 -2
  82. manim/renderer/vectorized_mobject_rendering.py +5 -0
  83. manim/scene/moving_camera_scene.py +23 -0
  84. manim/scene/scene.py +90 -43
  85. manim/scene/scene_file_writer.py +316 -165
  86. manim/scene/section.py +17 -15
  87. manim/scene/three_d_scene.py +13 -21
  88. manim/scene/vector_space_scene.py +22 -9
  89. manim/typing.py +830 -70
  90. manim/utils/bezier.py +1667 -399
  91. manim/utils/caching.py +13 -5
  92. manim/utils/color/AS2700.py +2 -0
  93. manim/utils/color/BS381.py +3 -0
  94. manim/utils/color/DVIPSNAMES.py +96 -0
  95. manim/utils/color/SVGNAMES.py +179 -0
  96. manim/utils/color/X11.py +3 -0
  97. manim/utils/color/XKCD.py +3 -0
  98. manim/utils/color/__init__.py +8 -5
  99. manim/utils/color/core.py +844 -309
  100. manim/utils/color/manim_colors.py +7 -9
  101. manim/utils/commands.py +48 -20
  102. manim/utils/config_ops.py +18 -13
  103. manim/utils/debug.py +8 -7
  104. manim/utils/deprecation.py +90 -40
  105. manim/utils/docbuild/__init__.py +17 -0
  106. manim/utils/docbuild/autoaliasattr_directive.py +234 -0
  107. manim/utils/docbuild/autocolor_directive.py +21 -17
  108. manim/utils/docbuild/manim_directive.py +50 -35
  109. manim/utils/docbuild/module_parsing.py +245 -0
  110. manim/utils/exceptions.py +6 -0
  111. manim/utils/family.py +5 -3
  112. manim/utils/family_ops.py +17 -4
  113. manim/utils/file_ops.py +26 -16
  114. manim/utils/hashing.py +9 -7
  115. manim/utils/images.py +10 -4
  116. manim/utils/ipython_magic.py +14 -8
  117. manim/utils/iterables.py +161 -119
  118. manim/utils/module_ops.py +57 -19
  119. manim/utils/opengl.py +83 -24
  120. manim/utils/parameter_parsing.py +32 -0
  121. manim/utils/paths.py +21 -23
  122. manim/utils/polylabel.py +168 -0
  123. manim/utils/qhull.py +218 -0
  124. manim/utils/rate_functions.py +74 -39
  125. manim/utils/simple_functions.py +24 -15
  126. manim/utils/sounds.py +7 -1
  127. manim/utils/space_ops.py +125 -69
  128. manim/utils/testing/__init__.py +17 -0
  129. manim/utils/testing/_frames_testers.py +13 -8
  130. manim/utils/testing/_show_diff.py +5 -3
  131. manim/utils/testing/_test_class_makers.py +33 -18
  132. manim/utils/testing/frames_comparison.py +27 -19
  133. manim/utils/tex.py +127 -197
  134. manim/utils/tex_file_writing.py +47 -45
  135. manim/utils/tex_templates.py +2 -1
  136. manim/utils/unit.py +6 -5
  137. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE.community +1 -1
  138. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/METADATA +40 -39
  139. manim-0.19.0.dist-info/RECORD +221 -0
  140. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
  141. manim/cli/new/__init__.py +0 -0
  142. manim/cli/new/group.py +0 -189
  143. manim/plugins/import_plugins.py +0 -43
  144. manim-0.18.0.post0.dist-info/RECORD +0 -217
  145. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
  146. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
@@ -5,9 +5,11 @@ import inspect
5
5
  import itertools as it
6
6
  import random
7
7
  import sys
8
+ import types
9
+ from collections.abc import Iterable, Iterator, Sequence
8
10
  from functools import partialmethod, wraps
9
11
  from math import ceil
10
- from typing import Iterable, Sequence
12
+ from typing import TYPE_CHECKING, Any, Callable, TypeVar
11
13
 
12
14
  import moderngl
13
15
  import numpy as np
@@ -44,10 +46,36 @@ from manim.utils.space_ops import (
44
46
  rotation_matrix_transpose,
45
47
  )
46
48
 
47
-
48
- def affects_shader_info_id(func):
49
+ if TYPE_CHECKING:
50
+ import numpy.typing as npt
51
+ from typing_extensions import Self, TypeAlias
52
+
53
+ from manim.renderer.shader_wrapper import ShaderWrapper
54
+ from manim.typing import (
55
+ ManimFloat,
56
+ MappingFunction,
57
+ MatrixMN,
58
+ MultiMappingFunction,
59
+ PathFuncType,
60
+ Point3D,
61
+ Point3D_Array,
62
+ Point3DLike,
63
+ Point3DLike_Array,
64
+ Vector3D,
65
+ )
66
+
67
+ TimeBasedUpdater: TypeAlias = Callable[["Mobject", float], object]
68
+ NonTimeBasedUpdater: TypeAlias = Callable[["Mobject"], object]
69
+ Updater: TypeAlias = NonTimeBasedUpdater | TimeBasedUpdater
70
+
71
+ T = TypeVar("T")
72
+
73
+
74
+ def affects_shader_info_id(
75
+ func: Callable[[OpenGLMobject], OpenGLMobject],
76
+ ) -> Callable[[OpenGLMobject], OpenGLMobject]:
49
77
  @wraps(func)
50
- def wrapper(self):
78
+ def wrapper(self: OpenGLMobject) -> OpenGLMobject:
51
79
  for mob in self.get_family():
52
80
  func(mob)
53
81
  mob.refresh_shader_wrapper_id()
@@ -56,6 +84,9 @@ def affects_shader_info_id(func):
56
84
  return wrapper
57
85
 
58
86
 
87
+ __all__ = ["OpenGLMobject", "OpenGLGroup", "OpenGLPoint", "_AnimationBuilder"]
88
+
89
+
59
90
  class OpenGLMobject:
60
91
  """Mathematical Object: base class for objects that can be displayed on screen.
61
92
 
@@ -90,26 +121,26 @@ class OpenGLMobject:
90
121
 
91
122
  def __init__(
92
123
  self,
93
- color=WHITE,
94
- opacity=1,
95
- dim=3, # TODO, get rid of this
124
+ color: ParsableManimColor | Iterable[ParsableManimColor] = WHITE,
125
+ opacity: float = 1,
126
+ dim: int = 3, # TODO, get rid of this
96
127
  # Lighting parameters
97
128
  # Positive gloss up to 1 makes it reflect the light.
98
- gloss=0.0,
129
+ gloss: float = 0.0,
99
130
  # Positive shadow up to 1 makes a side opposite the light darker
100
- shadow=0.0,
131
+ shadow: float = 0.0,
101
132
  # For shaders
102
- render_primitive=moderngl.TRIANGLES,
103
- texture_paths=None,
104
- depth_test=False,
133
+ render_primitive: int = moderngl.TRIANGLES,
134
+ texture_paths: dict[str, str] | None = None,
135
+ depth_test: bool = False,
105
136
  # If true, the mobject will not get rotated according to camera position
106
- is_fixed_in_frame=False,
107
- is_fixed_orientation=False,
137
+ is_fixed_in_frame: bool = False,
138
+ is_fixed_orientation: bool = False,
108
139
  # Must match in attributes of vert shader
109
140
  # Event listener
110
- listen_to_events=False,
111
- model_matrix=None,
112
- should_render=True,
141
+ listen_to_events: bool = False,
142
+ model_matrix: MatrixMN | None = None,
143
+ should_render: bool = True,
113
144
  name: str | None = None,
114
145
  **kwargs,
115
146
  ):
@@ -162,15 +193,73 @@ class OpenGLMobject:
162
193
 
163
194
  self.should_render = should_render
164
195
 
196
+ def _assert_valid_submobjects(self, submobjects: Iterable[OpenGLMobject]) -> Self:
197
+ """Check that all submobjects are actually instances of
198
+ :class:`OpenGLMobject`, and that none of them is
199
+ ``self`` (an :class:`OpenGLMobject` cannot contain itself).
200
+
201
+ This is an auxiliary function called when adding OpenGLMobjects to the
202
+ :attr:`submobjects` list.
203
+
204
+ This function is intended to be overridden by subclasses such as
205
+ :class:`OpenGLVMobject`, which should assert that only other
206
+ OpenGLVMobjects may be added into it.
207
+
208
+ Parameters
209
+ ----------
210
+ submobjects
211
+ The list containing values to validate.
212
+
213
+ Returns
214
+ -------
215
+ :class:`OpenGLMobject`
216
+ The OpenGLMobject itself.
217
+
218
+ Raises
219
+ ------
220
+ TypeError
221
+ If any of the values in `submobjects` is not an
222
+ :class:`OpenGLMobject`.
223
+ ValueError
224
+ If there was an attempt to add an :class:`OpenGLMobject` as its own
225
+ submobject.
226
+ """
227
+ return self._assert_valid_submobjects_internal(submobjects, OpenGLMobject)
228
+
229
+ def _assert_valid_submobjects_internal(
230
+ self, submobjects: Iterable[OpenGLMobject], mob_class: type[OpenGLMobject]
231
+ ) -> Self:
232
+ for i, submob in enumerate(submobjects):
233
+ if not isinstance(submob, mob_class):
234
+ error_message = (
235
+ f"Only values of type {mob_class.__name__} can be added "
236
+ f"as submobjects of {type(self).__name__}, but the value "
237
+ f"{submob} (at index {i}) is of type "
238
+ f"{type(submob).__name__}."
239
+ )
240
+ # Intended for subclasses such as OpenGLVMobject, which
241
+ # cannot have regular OpenGLMobjects as submobjects
242
+ if isinstance(submob, OpenGLMobject):
243
+ error_message += (
244
+ " You can try adding this value into a Group instead."
245
+ )
246
+ raise TypeError(error_message)
247
+ if submob is self:
248
+ raise ValueError(
249
+ f"Cannot add {type(self).__name__} as a submobject of "
250
+ f"itself (at index {i})."
251
+ )
252
+ return self
253
+
165
254
  @classmethod
166
- def __init_subclass__(cls, **kwargs):
255
+ def __init_subclass__(cls, **kwargs) -> None:
167
256
  super().__init_subclass__(**kwargs)
168
257
  cls._original__init__ = cls.__init__
169
258
 
170
- def __str__(self):
259
+ def __str__(self) -> str:
171
260
  return self.__class__.__name__
172
261
 
173
- def __repr__(self):
262
+ def __repr__(self) -> str:
174
263
  return str(self.name)
175
264
 
176
265
  def __sub__(self, other):
@@ -186,7 +275,7 @@ class OpenGLMobject:
186
275
  return NotImplemented
187
276
 
188
277
  @classmethod
189
- def set_default(cls, **kwargs):
278
+ def set_default(cls, **kwargs) -> None:
190
279
  """Sets the default values of keyword arguments.
191
280
 
192
281
  If this method is called without any additional keyword
@@ -233,28 +322,31 @@ class OpenGLMobject:
233
322
  else:
234
323
  cls.__init__ = cls._original__init__
235
324
 
236
- def init_data(self):
325
+ def init_data(self) -> None:
237
326
  """Initializes the ``points``, ``bounding_box`` and ``rgbas`` attributes and groups them into self.data.
238
- Subclasses can inherit and overwrite this method to extend `self.data`."""
327
+ Subclasses can inherit and overwrite this method to extend `self.data`.
328
+ """
239
329
  self.points = np.zeros((0, 3))
240
330
  self.bounding_box = np.zeros((3, 3))
241
331
  self.rgbas = np.zeros((1, 4))
242
332
 
243
- def init_colors(self):
333
+ def init_colors(self) -> object:
244
334
  """Initializes the colors.
245
335
 
246
- Gets called upon creation"""
336
+ Gets called upon creation
337
+ """
247
338
  self.set_color(self.color, self.opacity)
248
339
 
249
- def init_points(self):
340
+ def init_points(self) -> object:
250
341
  """Initializes :attr:`points` and therefore the shape.
251
342
 
252
343
  Gets called upon creation. This is an empty method that can be implemented by
253
- subclasses."""
344
+ subclasses.
345
+ """
254
346
  # Typically implemented in subclass, unless purposefully left blank
255
347
  pass
256
348
 
257
- def set(self, **kwargs) -> OpenGLMobject:
349
+ def set(self, **kwargs) -> Self:
258
350
  """Sets attributes.
259
351
 
260
352
  Mainly to be used along with :attr:`animate` to
@@ -282,24 +374,23 @@ class OpenGLMobject:
282
374
 
283
375
 
284
376
  """
285
-
286
377
  for attr, value in kwargs.items():
287
378
  setattr(self, attr, value)
288
379
 
289
380
  return self
290
381
 
291
- def set_data(self, data):
382
+ def set_data(self, data: dict[str, Any]) -> Self:
292
383
  for key in data:
293
384
  self.data[key] = data[key].copy()
294
385
  return self
295
386
 
296
- def set_uniforms(self, uniforms):
387
+ def set_uniforms(self, uniforms: dict[str, Any]) -> Self:
297
388
  for key in uniforms:
298
389
  self.uniforms[key] = uniforms[key] # Copy?
299
390
  return self
300
391
 
301
392
  @property
302
- def animate(self):
393
+ def animate(self) -> _AnimationBuilder | Self:
303
394
  """Used to animate the application of a method.
304
395
 
305
396
  .. warning::
@@ -310,7 +401,9 @@ class OpenGLMobject:
310
401
 
311
402
  ::
312
403
 
313
- self.play(my_mobject.animate.shift(RIGHT), my_mobject.animate.rotate(PI))
404
+ self.play(
405
+ my_mobject.animate.shift(RIGHT), my_mobject.animate.rotate(PI)
406
+ )
314
407
 
315
408
  make use of method chaining for ``animate``, meaning::
316
409
 
@@ -387,7 +480,7 @@ class OpenGLMobject:
387
480
  return _AnimationBuilder(self)
388
481
 
389
482
  @property
390
- def width(self):
483
+ def width(self) -> float:
391
484
  """The width of the mobject.
392
485
 
393
486
  Returns
@@ -415,17 +508,16 @@ class OpenGLMobject:
415
508
  :meth:`length_over_dim`
416
509
 
417
510
  """
418
-
419
511
  # Get the length across the X dimension
420
512
  return self.length_over_dim(0)
421
513
 
422
514
  # Only these methods should directly affect points
423
515
  @width.setter
424
- def width(self, value):
516
+ def width(self, value: float) -> None:
425
517
  self.rescale_to_fit(value, 0, stretch=False)
426
518
 
427
519
  @property
428
- def height(self):
520
+ def height(self) -> float:
429
521
  """The height of the mobject.
430
522
 
431
523
  Returns
@@ -453,16 +545,15 @@ class OpenGLMobject:
453
545
  :meth:`length_over_dim`
454
546
 
455
547
  """
456
-
457
548
  # Get the length across the Y dimension
458
549
  return self.length_over_dim(1)
459
550
 
460
551
  @height.setter
461
- def height(self, value):
552
+ def height(self, value: float) -> None:
462
553
  self.rescale_to_fit(value, 1, stretch=False)
463
554
 
464
555
  @property
465
- def depth(self):
556
+ def depth(self) -> float:
466
557
  """The depth of the mobject.
467
558
 
468
559
  Returns
@@ -474,12 +565,11 @@ class OpenGLMobject:
474
565
  :meth:`length_over_dim`
475
566
 
476
567
  """
477
-
478
568
  # Get the length across the Z dimension
479
569
  return self.length_over_dim(2)
480
570
 
481
571
  @depth.setter
482
- def depth(self, value):
572
+ def depth(self, value: float) -> None:
483
573
  self.rescale_to_fit(value, 2, stretch=False)
484
574
 
485
575
  def resize_points(self, new_length, resize_func=resize_array):
@@ -488,7 +578,7 @@ class OpenGLMobject:
488
578
  self.refresh_bounding_box()
489
579
  return self
490
580
 
491
- def set_points(self, points):
581
+ def set_points(self, points: Point3DLike_Array) -> Self:
492
582
  if len(points) == len(self.points):
493
583
  self.points[:] = points
494
584
  elif isinstance(points, np.ndarray):
@@ -498,23 +588,26 @@ class OpenGLMobject:
498
588
  self.refresh_bounding_box()
499
589
  return self
500
590
 
501
- def apply_over_attr_arrays(self, func):
591
+ def apply_over_attr_arrays(
592
+ self, func: Callable[[npt.NDArray[T]], npt.NDArray[T]]
593
+ ) -> Self:
594
+ # TODO: OpenGLMobject.get_array_attrs() doesn't even exist!
502
595
  for attr in self.get_array_attrs():
503
596
  setattr(self, attr, func(getattr(self, attr)))
504
597
  return self
505
598
 
506
- def append_points(self, new_points):
599
+ def append_points(self, new_points: Point3DLike_Array) -> Self:
507
600
  self.points = np.vstack([self.points, new_points])
508
601
  self.refresh_bounding_box()
509
602
  return self
510
603
 
511
- def reverse_points(self):
604
+ def reverse_points(self) -> Self:
512
605
  for mob in self.get_family():
513
606
  for key in mob.data:
514
607
  mob.data[key] = mob.data[key][::-1]
515
608
  return self
516
609
 
517
- def get_midpoint(self) -> np.ndarray:
610
+ def get_midpoint(self) -> Point3D:
518
611
  """Get coordinates of the middle of the path that forms the :class:`~.OpenGLMobject`.
519
612
 
520
613
  Examples
@@ -537,13 +630,14 @@ class OpenGLMobject:
537
630
  """
538
631
  return self.point_from_proportion(0.5)
539
632
 
633
+ # TODO: name is inconsistent with Mobject.apply_points_function_about_point()
540
634
  def apply_points_function(
541
635
  self,
542
- func,
543
- about_point=None,
544
- about_edge=ORIGIN,
545
- works_on_bounding_box=False,
546
- ):
636
+ func: MultiMappingFunction,
637
+ about_point: Point3DLike | None = None,
638
+ about_edge: Vector3D | None = ORIGIN,
639
+ works_on_bounding_box: bool = False,
640
+ ) -> Self:
547
641
  if about_point is None and about_edge is not None:
548
642
  about_point = self.get_bounding_box_point(about_edge)
549
643
 
@@ -569,7 +663,7 @@ class OpenGLMobject:
569
663
 
570
664
  # Others related to points
571
665
 
572
- def match_points(self, mobject):
666
+ def match_points(self, mobject: OpenGLMobject) -> Self:
573
667
  """Edit points, positions, and submobjects to be identical
574
668
  to another :class:`~.OpenGLMobject`, while keeping the style unchanged.
575
669
 
@@ -587,29 +681,31 @@ class OpenGLMobject:
587
681
  self.wait(0.5)
588
682
  """
589
683
  self.set_points(mobject.points)
684
+ return self
590
685
 
591
- def clear_points(self):
686
+ def clear_points(self) -> Self:
592
687
  self.points = np.empty((0, 3))
688
+ return self
593
689
 
594
- def get_num_points(self):
690
+ def get_num_points(self) -> int:
595
691
  return len(self.points)
596
692
 
597
- def get_all_points(self):
693
+ def get_all_points(self) -> Point3D_Array:
598
694
  if self.submobjects:
599
695
  return np.vstack([sm.points for sm in self.get_family()])
600
696
  else:
601
697
  return self.points
602
698
 
603
- def has_points(self):
699
+ def has_points(self) -> bool:
604
700
  return self.get_num_points() > 0
605
701
 
606
- def get_bounding_box(self):
702
+ def get_bounding_box(self) -> npt.NDArray[float]:
607
703
  if self.needs_new_bounding_box:
608
704
  self.bounding_box = self.compute_bounding_box()
609
705
  self.needs_new_bounding_box = False
610
706
  return self.bounding_box
611
707
 
612
- def compute_bounding_box(self):
708
+ def compute_bounding_box(self) -> npt.NDArray[float]:
613
709
  all_points = np.vstack(
614
710
  [
615
711
  self.points,
@@ -629,7 +725,9 @@ class OpenGLMobject:
629
725
  mids = (mins + maxs) / 2
630
726
  return np.array([mins, mids, maxs])
631
727
 
632
- def refresh_bounding_box(self, recurse_down=False, recurse_up=True):
728
+ def refresh_bounding_box(
729
+ self, recurse_down: bool = False, recurse_up: bool = True
730
+ ) -> Self:
633
731
  for mob in self.get_family(recurse_down):
634
732
  mob.needs_new_bounding_box = True
635
733
  if recurse_up:
@@ -637,7 +735,9 @@ class OpenGLMobject:
637
735
  parent.refresh_bounding_box()
638
736
  return self
639
737
 
640
- def is_point_touching(self, point, buff=MED_SMALL_BUFF):
738
+ def is_point_touching(
739
+ self, point: Point3DLike, buff: float = MED_SMALL_BUFF
740
+ ) -> bool:
641
741
  bb = self.get_bounding_box()
642
742
  mins = bb[0] - buff
643
743
  maxs = bb[2] + buff
@@ -645,22 +745,22 @@ class OpenGLMobject:
645
745
 
646
746
  # Family matters
647
747
 
648
- def __getitem__(self, value):
748
+ def __getitem__(self, value: int | slice) -> OpenGLMobject:
649
749
  if isinstance(value, slice):
650
750
  GroupClass = self.get_group_class()
651
751
  return GroupClass(*self.split().__getitem__(value))
652
752
  return self.split().__getitem__(value)
653
753
 
654
- def __iter__(self):
754
+ def __iter__(self) -> Iterator[OpenGLMobject]:
655
755
  return iter(self.split())
656
756
 
657
- def __len__(self):
757
+ def __len__(self) -> int:
658
758
  return len(self.split())
659
759
 
660
- def split(self):
760
+ def split(self) -> Sequence[OpenGLMobject]:
661
761
  return self.submobjects
662
762
 
663
- def assemble_family(self):
763
+ def assemble_family(self) -> Self:
664
764
  sub_families = (sm.get_family() for sm in self.submobjects)
665
765
  self.family = [self, *uniq_chain(*sub_families)]
666
766
  self.refresh_has_updater_status()
@@ -669,18 +769,16 @@ class OpenGLMobject:
669
769
  parent.assemble_family()
670
770
  return self
671
771
 
672
- def get_family(self, recurse=True):
772
+ def get_family(self, recurse: bool = True) -> Sequence[OpenGLMobject]:
673
773
  if recurse and hasattr(self, "family"):
674
774
  return self.family
675
775
  else:
676
776
  return [self]
677
777
 
678
- def family_members_with_points(self):
778
+ def family_members_with_points(self) -> Sequence[OpenGLMobject]:
679
779
  return [m for m in self.get_family() if m.has_points()]
680
780
 
681
- def add(
682
- self, *mobjects: OpenGLMobject, update_parent: bool = False
683
- ) -> OpenGLMobject:
781
+ def add(self, *mobjects: OpenGLMobject, update_parent: bool = False) -> Self:
684
782
  """Add mobjects as submobjects.
685
783
 
686
784
  The mobjects are added to :attr:`submobjects`.
@@ -731,28 +829,33 @@ class OpenGLMobject:
731
829
  >>> len(outer.submobjects)
732
830
  1
733
831
 
832
+ Only OpenGLMobjects can be added::
833
+
834
+ >>> outer.add(3)
835
+ Traceback (most recent call last):
836
+ ...
837
+ TypeError: Only values of type OpenGLMobject can be added as submobjects of OpenGLMobject, but the value 3 (at index 0) is of type int.
838
+
734
839
  Adding an object to itself raises an error::
735
840
 
736
841
  >>> outer.add(outer)
737
842
  Traceback (most recent call last):
738
843
  ...
739
- ValueError: OpenGLMobject cannot contain self
844
+ ValueError: Cannot add OpenGLMobject as a submobject of itself (at index 0).
740
845
 
741
846
  """
742
847
  if update_parent:
743
848
  assert len(mobjects) == 1, "Can't set multiple parents."
744
849
  mobjects[0].parent = self
745
850
 
746
- if self in mobjects:
747
- raise ValueError("OpenGLMobject cannot contain self")
851
+ self._assert_valid_submobjects(mobjects)
852
+
748
853
  if any(mobjects.count(elem) > 1 for elem in mobjects):
749
854
  logger.warning(
750
855
  "Attempted adding some Mobject as a child more than once, "
751
856
  "this is not possible. Repetitions are ignored.",
752
857
  )
753
858
  for mobject in mobjects:
754
- if not isinstance(mobject, OpenGLMobject):
755
- raise TypeError("All submobjects must be of type OpenGLMobject")
756
859
  if mobject not in self.submobjects:
757
860
  self.submobjects.append(mobject)
758
861
  if self not in mobject.parents:
@@ -760,7 +863,9 @@ class OpenGLMobject:
760
863
  self.assemble_family()
761
864
  return self
762
865
 
763
- def insert(self, index: int, mobject: OpenGLMobject, update_parent: bool = False):
866
+ def insert(
867
+ self, index: int, mobject: OpenGLMobject, update_parent: bool = False
868
+ ) -> Self:
764
869
  """Inserts a mobject at a specific position into self.submobjects
765
870
 
766
871
  Effectively just calls ``self.submobjects.insert(index, mobject)``,
@@ -777,15 +882,10 @@ class OpenGLMobject:
777
882
  update_parent
778
883
  Whether or not to set ``mobject.parent`` to ``self``.
779
884
  """
780
-
781
885
  if update_parent:
782
886
  mobject.parent = self
783
887
 
784
- if mobject is self:
785
- raise ValueError("OpenGLMobject cannot contain self")
786
-
787
- if not isinstance(mobject, OpenGLMobject):
788
- raise TypeError("All submobjects must be of type OpenGLMobject")
888
+ self._assert_valid_submobjects([mobject])
789
889
 
790
890
  if mobject not in self.submobjects:
791
891
  self.submobjects.insert(index, mobject)
@@ -796,9 +896,7 @@ class OpenGLMobject:
796
896
  self.assemble_family()
797
897
  return self
798
898
 
799
- def remove(
800
- self, *mobjects: OpenGLMobject, update_parent: bool = False
801
- ) -> OpenGLMobject:
899
+ def remove(self, *mobjects: OpenGLMobject, update_parent: bool = False) -> Self:
802
900
  """Remove :attr:`submobjects`.
803
901
 
804
902
  The mobjects are removed from :attr:`submobjects`, if they exist.
@@ -832,7 +930,7 @@ class OpenGLMobject:
832
930
  self.assemble_family()
833
931
  return self
834
932
 
835
- def add_to_back(self, *mobjects: OpenGLMobject) -> OpenGLMobject:
933
+ def add_to_back(self, *mobjects: OpenGLMobject) -> Self:
836
934
  # NOTE: is the note true OpenGLMobjects?
837
935
  """Add all passed mobjects to the back of the submobjects.
838
936
 
@@ -877,10 +975,12 @@ class OpenGLMobject:
877
975
  :meth:`add`
878
976
 
879
977
  """
978
+ self._assert_valid_submobjects(mobjects)
880
979
  self.submobjects = list_update(mobjects, self.submobjects)
881
980
  return self
882
981
 
883
- def replace_submobject(self, index, new_submob):
982
+ def replace_submobject(self, index: int, new_submob: OpenGLMobject) -> Self:
983
+ self._assert_valid_submobjects([new_submob])
884
984
  old_submob = self.submobjects[index]
885
985
  if self in old_submob.parents:
886
986
  old_submob.parents.remove(self)
@@ -888,36 +988,11 @@ class OpenGLMobject:
888
988
  self.assemble_family()
889
989
  return self
890
990
 
891
- def invert(self, recursive=False):
892
- """Inverts the list of :attr:`submobjects`.
893
-
894
- Parameters
895
- ----------
896
- recursive
897
- If ``True``, all submobject lists of this mobject's family are inverted.
898
-
899
- Examples
900
- --------
901
-
902
- .. manim:: InvertSumobjectsExample
903
-
904
- class InvertSumobjectsExample(Scene):
905
- def construct(self):
906
- s = VGroup(*[Dot().shift(i*0.1*RIGHT) for i in range(-20,20)])
907
- s2 = s.copy()
908
- s2.invert()
909
- s2.shift(DOWN)
910
- self.play(Write(s), Write(s2))
911
- """
912
- if recursive:
913
- for submob in self.submobjects:
914
- submob.invert(recursive=True)
915
- list.reverse(self.submobjects)
916
- self.assemble_family()
917
-
918
991
  # Submobject organization
919
992
 
920
- def arrange(self, direction=RIGHT, center=True, **kwargs):
993
+ def arrange(
994
+ self, direction: Vector3D = RIGHT, center: bool = True, **kwargs
995
+ ) -> Self:
921
996
  """Sorts :class:`~.OpenGLMobject` next to each other on screen.
922
997
 
923
998
  Examples
@@ -946,14 +1021,14 @@ class OpenGLMobject:
946
1021
  rows: int | None = None,
947
1022
  cols: int | None = None,
948
1023
  buff: float | tuple[float, float] = MED_SMALL_BUFF,
949
- cell_alignment: np.ndarray = ORIGIN,
1024
+ cell_alignment: Vector3D = ORIGIN,
950
1025
  row_alignments: str | None = None, # "ucd"
951
1026
  col_alignments: str | None = None, # "lcr"
952
- row_heights: Iterable[float | None] | None = None,
953
- col_widths: Iterable[float | None] | None = None,
1027
+ row_heights: Sequence[float | None] | None = None,
1028
+ col_widths: Sequence[float | None] | None = None,
954
1029
  flow_order: str = "rd",
955
1030
  **kwargs,
956
- ) -> OpenGLMobject:
1031
+ ) -> Self:
957
1032
  """Arrange submobjects in a grid.
958
1033
 
959
1034
  Parameters
@@ -1049,16 +1124,27 @@ class OpenGLMobject:
1049
1124
  start_pos = self.get_center()
1050
1125
 
1051
1126
  # get cols / rows values if given (implicitly)
1052
- def init_size(num, alignments, sizes):
1127
+ def init_size(
1128
+ num: int | None,
1129
+ alignments: str | None,
1130
+ sizes: Sequence[float | None] | None,
1131
+ name: str,
1132
+ ) -> int:
1053
1133
  if num is not None:
1054
1134
  return num
1055
1135
  if alignments is not None:
1056
1136
  return len(alignments)
1057
1137
  if sizes is not None:
1058
1138
  return len(sizes)
1139
+ raise ValueError(
1140
+ f"At least one of the following parameters: '{name}s', "
1141
+ f"'{name}_alignments' or "
1142
+ f"'{name}_{'widths' if name == 'col' else 'heights'}', "
1143
+ "must not be None"
1144
+ )
1059
1145
 
1060
- cols = init_size(cols, col_alignments, col_widths)
1061
- rows = init_size(rows, row_alignments, row_heights)
1146
+ cols = init_size(cols, col_alignments, col_widths, "col")
1147
+ rows = init_size(rows, row_alignments, row_heights, "row")
1062
1148
 
1063
1149
  # calculate rows cols
1064
1150
  if rows is None and cols is None:
@@ -1082,16 +1168,19 @@ class OpenGLMobject:
1082
1168
  buff_x = buff_y = buff
1083
1169
 
1084
1170
  # Initialize alignments correctly
1085
- def init_alignments(alignments, num, mapping, name, dir):
1086
- if alignments is None:
1171
+ def init_alignments(
1172
+ str_alignments: str | None,
1173
+ num: int,
1174
+ mapping: dict[str, Vector3D],
1175
+ name: str,
1176
+ direction: Vector3D,
1177
+ ) -> Sequence[Vector3D]:
1178
+ if str_alignments is None:
1087
1179
  # Use cell_alignment as fallback
1088
- return [cell_alignment * dir] * num
1089
- if len(alignments) != num:
1180
+ return [cell_alignment * direction] * num
1181
+ if len(str_alignments) != num:
1090
1182
  raise ValueError(f"{name}_alignments has a mismatching size.")
1091
- alignments = list(alignments)
1092
- for i in range(num):
1093
- alignments[i] = mapping[alignments[i]]
1094
- return alignments
1183
+ return [mapping[letter] for letter in str_alignments]
1095
1184
 
1096
1185
  row_alignments = init_alignments(
1097
1186
  row_alignments,
@@ -1127,11 +1216,12 @@ class OpenGLMobject:
1127
1216
 
1128
1217
  # Reverse row_alignments and row_heights. Necessary since the
1129
1218
  # grid filling is handled bottom up for simplicity reasons.
1130
- def reverse(maybe_list):
1219
+ def reverse(maybe_list: Sequence[Any] | None) -> Sequence[Any] | None:
1131
1220
  if maybe_list is not None:
1132
1221
  maybe_list = list(maybe_list)
1133
1222
  maybe_list.reverse()
1134
1223
  return maybe_list
1224
+ return None
1135
1225
 
1136
1226
  row_alignments = reverse(row_alignments)
1137
1227
  row_heights = reverse(row_heights)
@@ -1152,7 +1242,12 @@ class OpenGLMobject:
1152
1242
  ]
1153
1243
 
1154
1244
  # Initialize row_heights / col_widths correctly using measurements as fallback
1155
- def init_sizes(sizes, num, measures, name):
1245
+ def init_sizes(
1246
+ sizes: Sequence[float | None] | None,
1247
+ num: int,
1248
+ measures: Sequence[float],
1249
+ name: str,
1250
+ ) -> Sequence[float]:
1156
1251
  if sizes is None:
1157
1252
  sizes = [None] * num
1158
1253
  if len(sizes) != num:
@@ -1185,7 +1280,9 @@ class OpenGLMobject:
1185
1280
  self.move_to(start_pos)
1186
1281
  return self
1187
1282
 
1188
- def get_grid(self, n_rows, n_cols, height=None, **kwargs):
1283
+ def get_grid(
1284
+ self, n_rows: int, n_cols: int, height: float | None = None, **kwargs
1285
+ ) -> OpenGLGroup:
1189
1286
  """
1190
1287
  Returns a new mobject containing multiple copies of this one
1191
1288
  arranged in a grid
@@ -1196,11 +1293,15 @@ class OpenGLMobject:
1196
1293
  grid.set_height(height)
1197
1294
  return grid
1198
1295
 
1199
- def duplicate(self, n: int):
1200
- """Returns an :class:`~.OpenGLVGroup` containing ``n`` copies of the mobject."""
1296
+ def duplicate(self, n: int) -> OpenGLGroup:
1297
+ """Returns an :class:`~.OpenGLGroup` containing ``n`` copies of the mobject."""
1201
1298
  return self.get_group_class()(*[self.copy() for _ in range(n)])
1202
1299
 
1203
- def sort(self, point_to_num_func=lambda p: p[0], submob_func=None):
1300
+ def sort(
1301
+ self,
1302
+ point_to_num_func: Callable[[Point3DLike], float] = lambda p: p[0],
1303
+ submob_func: Callable[[OpenGLMobject], Any] | None = None,
1304
+ ) -> Self:
1204
1305
  """Sorts the list of :attr:`submobjects` by a function defined by ``submob_func``."""
1205
1306
  if submob_func is not None:
1206
1307
  self.submobjects.sort(key=submob_func)
@@ -1208,7 +1309,7 @@ class OpenGLMobject:
1208
1309
  self.submobjects.sort(key=lambda m: point_to_num_func(m.get_center()))
1209
1310
  return self
1210
1311
 
1211
- def shuffle(self, recurse=False):
1312
+ def shuffle(self, recurse: bool = False) -> Self:
1212
1313
  """Shuffles the order of :attr:`submobjects`
1213
1314
 
1214
1315
  Examples
@@ -1231,7 +1332,7 @@ class OpenGLMobject:
1231
1332
  self.assemble_family()
1232
1333
  return self
1233
1334
 
1234
- def invert(self, recursive=False):
1335
+ def invert(self, recursive: bool = False) -> Self:
1235
1336
  """Inverts the list of :attr:`submobjects`.
1236
1337
 
1237
1338
  Parameters
@@ -1255,11 +1356,13 @@ class OpenGLMobject:
1255
1356
  if recursive:
1256
1357
  for submob in self.submobjects:
1257
1358
  submob.invert(recursive=True)
1258
- list.reverse(self.submobjects)
1359
+ self.submobjects.reverse()
1360
+ self.assemble_family()
1361
+ return self
1259
1362
 
1260
1363
  # Copying
1261
1364
 
1262
- def copy(self, shallow: bool = False):
1365
+ def copy(self, shallow: bool = False) -> OpenGLMobject:
1263
1366
  """Create and return an identical copy of the :class:`OpenGLMobject` including all
1264
1367
  :attr:`submobjects`.
1265
1368
 
@@ -1317,14 +1420,14 @@ class OpenGLMobject:
1317
1420
  # setattr(copy_mobject, attr, value.copy())
1318
1421
  return copy_mobject
1319
1422
 
1320
- def deepcopy(self):
1423
+ def deepcopy(self) -> OpenGLMobject:
1321
1424
  parents = self.parents
1322
1425
  self.parents = []
1323
1426
  result = copy.deepcopy(self)
1324
1427
  self.parents = parents
1325
1428
  return result
1326
1429
 
1327
- def generate_target(self, use_deepcopy: bool = False):
1430
+ def generate_target(self, use_deepcopy: bool = False) -> OpenGLMobject:
1328
1431
  self.target = None # Prevent exponential explosion
1329
1432
  if use_deepcopy:
1330
1433
  self.target = self.deepcopy()
@@ -1332,7 +1435,7 @@ class OpenGLMobject:
1332
1435
  self.target = self.copy()
1333
1436
  return self.target
1334
1437
 
1335
- def save_state(self, use_deepcopy: bool = False):
1438
+ def save_state(self, use_deepcopy: bool = False) -> Self:
1336
1439
  """Save the current state (position, color & size). Can be restored with :meth:`~.OpenGLMobject.restore`."""
1337
1440
  if hasattr(self, "saved_state"):
1338
1441
  # Prevent exponential growth of data
@@ -1343,7 +1446,7 @@ class OpenGLMobject:
1343
1446
  self.saved_state = self.copy()
1344
1447
  return self
1345
1448
 
1346
- def restore(self):
1449
+ def restore(self) -> Self:
1347
1450
  """Restores the state that was previously saved with :meth:`~.OpenGLMobject.save_state`."""
1348
1451
  if not hasattr(self, "saved_state") or self.save_state is None:
1349
1452
  raise Exception("Trying to restore without having saved")
@@ -1352,13 +1455,13 @@ class OpenGLMobject:
1352
1455
 
1353
1456
  # Updating
1354
1457
 
1355
- def init_updaters(self):
1458
+ def init_updaters(self) -> None:
1356
1459
  self.time_based_updaters = []
1357
1460
  self.non_time_updaters = []
1358
1461
  self.has_updaters = False
1359
1462
  self.updating_suspended = False
1360
1463
 
1361
- def update(self, dt=0, recurse=True):
1464
+ def update(self, dt: float = 0, recurse: bool = True) -> Self:
1362
1465
  if not self.has_updaters or self.updating_suspended:
1363
1466
  return self
1364
1467
  for updater in self.time_based_updaters:
@@ -1370,19 +1473,24 @@ class OpenGLMobject:
1370
1473
  submob.update(dt, recurse)
1371
1474
  return self
1372
1475
 
1373
- def get_time_based_updaters(self):
1476
+ def get_time_based_updaters(self) -> Sequence[TimeBasedUpdater]:
1374
1477
  return self.time_based_updaters
1375
1478
 
1376
- def has_time_based_updater(self):
1479
+ def has_time_based_updater(self) -> bool:
1377
1480
  return len(self.time_based_updaters) > 0
1378
1481
 
1379
- def get_updaters(self):
1482
+ def get_updaters(self) -> Sequence[Updater]:
1380
1483
  return self.time_based_updaters + self.non_time_updaters
1381
1484
 
1382
- def get_family_updaters(self):
1485
+ def get_family_updaters(self) -> Sequence[Updater]:
1383
1486
  return list(it.chain(*(sm.get_updaters() for sm in self.get_family())))
1384
1487
 
1385
- def add_updater(self, update_function, index=None, call_updater=False):
1488
+ def add_updater(
1489
+ self,
1490
+ update_function: Updater,
1491
+ index: int | None = None,
1492
+ call_updater: bool = False,
1493
+ ) -> Self:
1386
1494
  if "dt" in inspect.signature(update_function).parameters:
1387
1495
  updater_list = self.time_based_updaters
1388
1496
  else:
@@ -1398,14 +1506,14 @@ class OpenGLMobject:
1398
1506
  self.update()
1399
1507
  return self
1400
1508
 
1401
- def remove_updater(self, update_function):
1509
+ def remove_updater(self, update_function: Updater) -> Self:
1402
1510
  for updater_list in [self.time_based_updaters, self.non_time_updaters]:
1403
1511
  while update_function in updater_list:
1404
1512
  updater_list.remove(update_function)
1405
1513
  self.refresh_has_updater_status()
1406
1514
  return self
1407
1515
 
1408
- def clear_updaters(self, recurse=True):
1516
+ def clear_updaters(self, recurse: bool = True) -> Self:
1409
1517
  self.time_based_updaters = []
1410
1518
  self.non_time_updaters = []
1411
1519
  self.refresh_has_updater_status()
@@ -1414,20 +1522,20 @@ class OpenGLMobject:
1414
1522
  submob.clear_updaters()
1415
1523
  return self
1416
1524
 
1417
- def match_updaters(self, mobject):
1525
+ def match_updaters(self, mobject: OpenGLMobject) -> Self:
1418
1526
  self.clear_updaters()
1419
1527
  for updater in mobject.get_updaters():
1420
1528
  self.add_updater(updater)
1421
1529
  return self
1422
1530
 
1423
- def suspend_updating(self, recurse=True):
1531
+ def suspend_updating(self, recurse: bool = True) -> Self:
1424
1532
  self.updating_suspended = True
1425
1533
  if recurse:
1426
1534
  for submob in self.submobjects:
1427
1535
  submob.suspend_updating(recurse)
1428
1536
  return self
1429
1537
 
1430
- def resume_updating(self, recurse=True, call_updater=True):
1538
+ def resume_updating(self, recurse: bool = True, call_updater: bool = True) -> Self:
1431
1539
  self.updating_suspended = False
1432
1540
  if recurse:
1433
1541
  for submob in self.submobjects:
@@ -1438,13 +1546,13 @@ class OpenGLMobject:
1438
1546
  self.update(dt=0, recurse=recurse)
1439
1547
  return self
1440
1548
 
1441
- def refresh_has_updater_status(self):
1549
+ def refresh_has_updater_status(self) -> Self:
1442
1550
  self.has_updaters = any(mob.get_updaters() for mob in self.get_family())
1443
1551
  return self
1444
1552
 
1445
1553
  # Transforming operations
1446
1554
 
1447
- def shift(self, vector):
1555
+ def shift(self, vector: Vector3D) -> Self:
1448
1556
  self.apply_points_function(
1449
1557
  lambda points: points + vector,
1450
1558
  about_edge=None,
@@ -1458,7 +1566,7 @@ class OpenGLMobject:
1458
1566
  about_point: Sequence[float] | None = None,
1459
1567
  about_edge: Sequence[float] = ORIGIN,
1460
1568
  **kwargs,
1461
- ) -> OpenGLMobject:
1569
+ ) -> Self:
1462
1570
  r"""Scale the size by a factor.
1463
1571
 
1464
1572
  Default behavior is to scale about the center of the mobject.
@@ -1477,7 +1585,7 @@ class OpenGLMobject:
1477
1585
  if :math:`\alpha < 0`, the mobject is also flipped.
1478
1586
  kwargs
1479
1587
  Additional keyword arguments passed to
1480
- :meth:`apply_points_function_about_point`.
1588
+ :meth:`apply_points_function`.
1481
1589
 
1482
1590
  Returns
1483
1591
  -------
@@ -1514,24 +1622,24 @@ class OpenGLMobject:
1514
1622
  )
1515
1623
  return self
1516
1624
 
1517
- def stretch(self, factor, dim, **kwargs):
1518
- def func(points):
1625
+ def stretch(self, factor: float, dim: int, **kwargs) -> Self:
1626
+ def func(points: Point3D_Array) -> Point3D_Array:
1519
1627
  points[:, dim] *= factor
1520
1628
  return points
1521
1629
 
1522
1630
  self.apply_points_function(func, works_on_bounding_box=True, **kwargs)
1523
1631
  return self
1524
1632
 
1525
- def rotate_about_origin(self, angle, axis=OUT):
1633
+ def rotate_about_origin(self, angle: float, axis: Vector3D = OUT) -> Self:
1526
1634
  return self.rotate(angle, axis, about_point=ORIGIN)
1527
1635
 
1528
1636
  def rotate(
1529
1637
  self,
1530
- angle,
1531
- axis=OUT,
1638
+ angle: float,
1639
+ axis: Vector3D = OUT,
1532
1640
  about_point: Sequence[float] | None = None,
1533
1641
  **kwargs,
1534
- ):
1642
+ ) -> Self:
1535
1643
  """Rotates the :class:`~.OpenGLMobject` about a certain point."""
1536
1644
  rot_matrix_T = rotation_matrix_transpose(angle, axis)
1537
1645
  self.apply_points_function(
@@ -1541,7 +1649,7 @@ class OpenGLMobject:
1541
1649
  )
1542
1650
  return self
1543
1651
 
1544
- def flip(self, axis=UP, **kwargs):
1652
+ def flip(self, axis: Vector3D = UP, **kwargs) -> Self:
1545
1653
  """Flips/Mirrors an mobject about its center.
1546
1654
 
1547
1655
  Examples
@@ -1560,25 +1668,28 @@ class OpenGLMobject:
1560
1668
  """
1561
1669
  return self.rotate(TAU / 2, axis, **kwargs)
1562
1670
 
1563
- def apply_function(self, function, **kwargs):
1671
+ def apply_function(self, function: MappingFunction, **kwargs) -> Self:
1564
1672
  # Default to applying matrix about the origin, not mobjects center
1565
1673
  if len(kwargs) == 0:
1566
1674
  kwargs["about_point"] = ORIGIN
1567
- self.apply_points_function(
1568
- lambda points: np.array([function(p) for p in points]), **kwargs
1569
- )
1675
+
1676
+ def multi_mapping_function(points: Point3D_Array) -> Point3D_Array:
1677
+ result: Point3D_Array = np.apply_along_axis(function, 1, points)
1678
+ return result
1679
+
1680
+ self.apply_points_function(multi_mapping_function, **kwargs)
1570
1681
  return self
1571
1682
 
1572
- def apply_function_to_position(self, function):
1683
+ def apply_function_to_position(self, function: MappingFunction) -> Self:
1573
1684
  self.move_to(function(self.get_center()))
1574
1685
  return self
1575
1686
 
1576
- def apply_function_to_submobject_positions(self, function):
1687
+ def apply_function_to_submobject_positions(self, function: MappingFunction) -> Self:
1577
1688
  for submob in self.submobjects:
1578
1689
  submob.apply_function_to_position(function)
1579
1690
  return self
1580
1691
 
1581
- def apply_matrix(self, matrix, **kwargs):
1692
+ def apply_matrix(self, matrix: MatrixMN, **kwargs) -> Self:
1582
1693
  # Default to applying matrix about the origin, not mobjects center
1583
1694
  if ("about_point" not in kwargs) and ("about_edge" not in kwargs):
1584
1695
  kwargs["about_point"] = ORIGIN
@@ -1590,7 +1701,9 @@ class OpenGLMobject:
1590
1701
  )
1591
1702
  return self
1592
1703
 
1593
- def apply_complex_function(self, function, **kwargs):
1704
+ def apply_complex_function(
1705
+ self, function: Callable[[complex], complex], **kwargs
1706
+ ) -> Self:
1594
1707
  """Applies a complex function to a :class:`OpenGLMobject`.
1595
1708
  The x and y coordinates correspond to the real and imaginary parts respectively.
1596
1709
 
@@ -1624,7 +1737,7 @@ class OpenGLMobject:
1624
1737
 
1625
1738
  return self.apply_function(R3_func)
1626
1739
 
1627
- def hierarchical_model_matrix(self):
1740
+ def hierarchical_model_matrix(self) -> MatrixMN:
1628
1741
  if self.parent is None:
1629
1742
  return self.model_matrix
1630
1743
 
@@ -1635,7 +1748,12 @@ class OpenGLMobject:
1635
1748
  current_object = current_object.parent
1636
1749
  return np.linalg.multi_dot(list(reversed(model_matrices)))
1637
1750
 
1638
- def wag(self, direction=RIGHT, axis=DOWN, wag_factor=1.0):
1751
+ def wag(
1752
+ self,
1753
+ direction: Vector3D = RIGHT,
1754
+ axis: Vector3D = DOWN,
1755
+ wag_factor: float = 1.0,
1756
+ ) -> Self:
1639
1757
  for mob in self.family_members_with_points():
1640
1758
  alphas = np.dot(mob.points, np.transpose(axis))
1641
1759
  alphas -= min(alphas)
@@ -1652,12 +1770,16 @@ class OpenGLMobject:
1652
1770
 
1653
1771
  # Positioning methods
1654
1772
 
1655
- def center(self):
1773
+ def center(self) -> Self:
1656
1774
  """Moves the mobject to the center of the Scene."""
1657
1775
  self.shift(-self.get_center())
1658
1776
  return self
1659
1777
 
1660
- def align_on_border(self, direction, buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER):
1778
+ def align_on_border(
1779
+ self,
1780
+ direction: Vector3D,
1781
+ buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER,
1782
+ ) -> Self:
1661
1783
  """
1662
1784
  Direction just needs to be a vector pointing towards side or
1663
1785
  corner in the 2d plane.
@@ -1673,22 +1795,30 @@ class OpenGLMobject:
1673
1795
  self.shift(shift_val)
1674
1796
  return self
1675
1797
 
1676
- def to_corner(self, corner=LEFT + DOWN, buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER):
1798
+ def to_corner(
1799
+ self,
1800
+ corner: Vector3D = LEFT + DOWN,
1801
+ buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER,
1802
+ ) -> Self:
1677
1803
  return self.align_on_border(corner, buff)
1678
1804
 
1679
- def to_edge(self, edge=LEFT, buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER):
1805
+ def to_edge(
1806
+ self,
1807
+ edge: Vector3D = LEFT,
1808
+ buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER,
1809
+ ) -> Self:
1680
1810
  return self.align_on_border(edge, buff)
1681
1811
 
1682
1812
  def next_to(
1683
1813
  self,
1684
- mobject_or_point,
1685
- direction=RIGHT,
1686
- buff=DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
1687
- aligned_edge=ORIGIN,
1688
- submobject_to_align=None,
1689
- index_of_submobject_to_align=None,
1690
- coor_mask=np.array([1, 1, 1]),
1691
- ):
1814
+ mobject_or_point: OpenGLMobject | Point3DLike,
1815
+ direction: Vector3D = RIGHT,
1816
+ buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
1817
+ aligned_edge: Vector3D = ORIGIN,
1818
+ submobject_to_align: OpenGLMobject | None = None,
1819
+ index_of_submobject_to_align: int | None = None,
1820
+ coor_mask: Point3DLike = np.array([1, 1, 1]),
1821
+ ) -> Self:
1692
1822
  """Move this :class:`~.OpenGLMobject` next to another's :class:`~.OpenGLMobject` or coordinate.
1693
1823
 
1694
1824
  Examples
@@ -1730,7 +1860,7 @@ class OpenGLMobject:
1730
1860
  self.shift((target_point - point_to_align + buff * direction) * coor_mask)
1731
1861
  return self
1732
1862
 
1733
- def shift_onto_screen(self, **kwargs):
1863
+ def shift_onto_screen(self, **kwargs) -> Self:
1734
1864
  space_lengths = [config["frame_x_radius"], config["frame_y_radius"]]
1735
1865
  for vect in UP, DOWN, LEFT, RIGHT:
1736
1866
  dim = np.argmax(np.abs(vect))
@@ -1741,21 +1871,21 @@ class OpenGLMobject:
1741
1871
  self.to_edge(vect, **kwargs)
1742
1872
  return self
1743
1873
 
1744
- def is_off_screen(self):
1874
+ def is_off_screen(self) -> bool:
1745
1875
  if self.get_left()[0] > config.frame_x_radius:
1746
1876
  return True
1747
1877
  if self.get_right()[0] < config.frame_x_radius:
1748
1878
  return True
1749
1879
  if self.get_bottom()[1] > config.frame_y_radius:
1750
1880
  return True
1751
- if self.get_top()[1] < -config.frame_y_radius:
1752
- return True
1753
- return False
1881
+ return self.get_top()[1] < -config.frame_y_radius
1754
1882
 
1755
- def stretch_about_point(self, factor, dim, point):
1883
+ def stretch_about_point(self, factor: float, dim: int, point: Point3DLike) -> Self:
1756
1884
  return self.stretch(factor, dim, about_point=point)
1757
1885
 
1758
- def rescale_to_fit(self, length, dim, stretch=False, **kwargs):
1886
+ def rescale_to_fit(
1887
+ self, length: float, dim: int, stretch: bool = False, **kwargs
1888
+ ) -> Self:
1759
1889
  old_length = self.length_over_dim(dim)
1760
1890
  if old_length == 0:
1761
1891
  return self
@@ -1765,7 +1895,7 @@ class OpenGLMobject:
1765
1895
  self.scale(length / old_length, **kwargs)
1766
1896
  return self
1767
1897
 
1768
- def stretch_to_fit_width(self, width, **kwargs):
1898
+ def stretch_to_fit_width(self, width: float, **kwargs) -> Self:
1769
1899
  """Stretches the :class:`~.OpenGLMobject` to fit a width, not keeping height/depth proportional.
1770
1900
 
1771
1901
  Returns
@@ -1790,15 +1920,15 @@ class OpenGLMobject:
1790
1920
  """
1791
1921
  return self.rescale_to_fit(width, 0, stretch=True, **kwargs)
1792
1922
 
1793
- def stretch_to_fit_height(self, height, **kwargs):
1923
+ def stretch_to_fit_height(self, height: float, **kwargs) -> Self:
1794
1924
  """Stretches the :class:`~.OpenGLMobject` to fit a height, not keeping width/height proportional."""
1795
1925
  return self.rescale_to_fit(height, 1, stretch=True, **kwargs)
1796
1926
 
1797
- def stretch_to_fit_depth(self, depth, **kwargs):
1927
+ def stretch_to_fit_depth(self, depth: float, **kwargs) -> Self:
1798
1928
  """Stretches the :class:`~.OpenGLMobject` to fit a depth, not keeping width/height proportional."""
1799
1929
  return self.rescale_to_fit(depth, 1, stretch=True, **kwargs)
1800
1930
 
1801
- def set_width(self, width, stretch=False, **kwargs):
1931
+ def set_width(self, width: float, stretch: bool = False, **kwargs) -> Self:
1802
1932
  """Scales the :class:`~.OpenGLMobject` to fit a width while keeping height/depth proportional.
1803
1933
 
1804
1934
  Returns
@@ -1825,38 +1955,38 @@ class OpenGLMobject:
1825
1955
 
1826
1956
  scale_to_fit_width = set_width
1827
1957
 
1828
- def set_height(self, height, stretch=False, **kwargs):
1958
+ def set_height(self, height: float, stretch: bool = False, **kwargs) -> Self:
1829
1959
  """Scales the :class:`~.OpenGLMobject` to fit a height while keeping width/depth proportional."""
1830
1960
  return self.rescale_to_fit(height, 1, stretch=stretch, **kwargs)
1831
1961
 
1832
1962
  scale_to_fit_height = set_height
1833
1963
 
1834
- def set_depth(self, depth, stretch=False, **kwargs):
1964
+ def set_depth(self, depth: float, stretch: bool = False, **kwargs):
1835
1965
  """Scales the :class:`~.OpenGLMobject` to fit a depth while keeping width/height proportional."""
1836
1966
  return self.rescale_to_fit(depth, 2, stretch=stretch, **kwargs)
1837
1967
 
1838
1968
  scale_to_fit_depth = set_depth
1839
1969
 
1840
- def set_coord(self, value, dim, direction=ORIGIN):
1970
+ def set_coord(self, value: float, dim: int, direction: Vector3D = ORIGIN) -> Self:
1841
1971
  curr = self.get_coord(dim, direction)
1842
1972
  shift_vect = np.zeros(self.dim)
1843
1973
  shift_vect[dim] = value - curr
1844
1974
  self.shift(shift_vect)
1845
1975
  return self
1846
1976
 
1847
- def set_x(self, x, direction=ORIGIN):
1977
+ def set_x(self, x: float, direction: Vector3D = ORIGIN) -> Self:
1848
1978
  """Set x value of the center of the :class:`~.OpenGLMobject` (``int`` or ``float``)"""
1849
1979
  return self.set_coord(x, 0, direction)
1850
1980
 
1851
- def set_y(self, y, direction=ORIGIN):
1981
+ def set_y(self, y: float, direction: Vector3D = ORIGIN) -> Self:
1852
1982
  """Set y value of the center of the :class:`~.OpenGLMobject` (``int`` or ``float``)"""
1853
1983
  return self.set_coord(y, 1, direction)
1854
1984
 
1855
- def set_z(self, z, direction=ORIGIN):
1985
+ def set_z(self, z: float, direction: Vector3D = ORIGIN) -> Self:
1856
1986
  """Set z value of the center of the :class:`~.OpenGLMobject` (``int`` or ``float``)"""
1857
1987
  return self.set_coord(z, 2, direction)
1858
1988
 
1859
- def space_out_submobjects(self, factor=1.5, **kwargs):
1989
+ def space_out_submobjects(self, factor: float = 1.5, **kwargs) -> Self:
1860
1990
  self.scale(factor, **kwargs)
1861
1991
  for submob in self.submobjects:
1862
1992
  submob.scale(1.0 / factor)
@@ -1864,10 +1994,10 @@ class OpenGLMobject:
1864
1994
 
1865
1995
  def move_to(
1866
1996
  self,
1867
- point_or_mobject,
1868
- aligned_edge=ORIGIN,
1869
- coor_mask=np.array([1, 1, 1]),
1870
- ):
1997
+ point_or_mobject: Point3DLike | OpenGLMobject,
1998
+ aligned_edge: Vector3D = ORIGIN,
1999
+ coor_mask: Point3DLike = np.array([1, 1, 1]),
2000
+ ) -> Self:
1871
2001
  """Move center of the :class:`~.OpenGLMobject` to certain coordinate."""
1872
2002
  if isinstance(point_or_mobject, OpenGLMobject):
1873
2003
  target = point_or_mobject.get_bounding_box_point(aligned_edge)
@@ -1877,7 +2007,12 @@ class OpenGLMobject:
1877
2007
  self.shift((target - point_to_align) * coor_mask)
1878
2008
  return self
1879
2009
 
1880
- def replace(self, mobject, dim_to_match=0, stretch=False):
2010
+ def replace(
2011
+ self,
2012
+ mobject: OpenGLMobject,
2013
+ dim_to_match: int = 0,
2014
+ stretch: bool = False,
2015
+ ) -> Self:
1881
2016
  if not mobject.get_num_points() and not mobject.submobjects:
1882
2017
  self.scale(0)
1883
2018
  return self
@@ -1899,13 +2034,13 @@ class OpenGLMobject:
1899
2034
  dim_to_match: int = 0,
1900
2035
  stretch: bool = False,
1901
2036
  buff: float = MED_SMALL_BUFF,
1902
- ):
2037
+ ) -> Self:
1903
2038
  self.replace(mobject, dim_to_match, stretch)
1904
2039
  length = mobject.length_over_dim(dim_to_match)
1905
2040
  self.scale((length + buff) / length)
1906
2041
  return self
1907
2042
 
1908
- def put_start_and_end_on(self, start, end):
2043
+ def put_start_and_end_on(self, start: Point3DLike, end: Point3DLike) -> Self:
1909
2044
  curr_start, curr_end = self.get_start_and_end()
1910
2045
  curr_vect = curr_end - curr_start
1911
2046
  if np.all(curr_vect == 0):
@@ -1930,7 +2065,13 @@ class OpenGLMobject:
1930
2065
 
1931
2066
  # Color functions
1932
2067
 
1933
- def set_rgba_array(self, color=None, opacity=None, name="rgbas", recurse=True):
2068
+ def set_rgba_array(
2069
+ self,
2070
+ color: ParsableManimColor | Iterable[ParsableManimColor] | None = None,
2071
+ opacity: float | Iterable[float] | None = None,
2072
+ name: str = "rgbas",
2073
+ recurse: bool = True,
2074
+ ) -> Self:
1934
2075
  if color is not None:
1935
2076
  rgbs = np.array([color_to_rgb(c) for c in listify(color)])
1936
2077
  if opacity is not None:
@@ -1960,7 +2101,12 @@ class OpenGLMobject:
1960
2101
  mob.data[name] = rgbas.copy()
1961
2102
  return self
1962
2103
 
1963
- def set_rgba_array_direct(self, rgbas: np.ndarray, name="rgbas", recurse=True):
2104
+ def set_rgba_array_direct(
2105
+ self,
2106
+ rgbas: npt.NDArray[RGBA_Array_Float],
2107
+ name: str = "rgbas",
2108
+ recurse: bool = True,
2109
+ ) -> Self:
1964
2110
  """Directly set rgba data from `rgbas` and optionally do the same recursively
1965
2111
  with submobjects. This can be used if the `rgbas` have already been generated
1966
2112
  with the correct shape and simply need to be set.
@@ -1977,7 +2123,12 @@ class OpenGLMobject:
1977
2123
  for mob in self.get_family(recurse):
1978
2124
  mob.data[name] = rgbas.copy()
1979
2125
 
1980
- def set_color(self, color: ParsableManimColor | None, opacity=None, recurse=True):
2126
+ def set_color(
2127
+ self,
2128
+ color: ParsableManimColor | Iterable[ParsableManimColor] | None,
2129
+ opacity: float | Iterable[float] | None = None,
2130
+ recurse: bool = True,
2131
+ ) -> Self:
1981
2132
  self.set_rgba_array(color, opacity, recurse=False)
1982
2133
  # Recurse to submobjects differently from how set_rgba_array
1983
2134
  # in case they implement set_color differently
@@ -1990,24 +2141,25 @@ class OpenGLMobject:
1990
2141
  submob.set_color(color, recurse=True)
1991
2142
  return self
1992
2143
 
1993
- def set_opacity(self, opacity, recurse=True):
2144
+ def set_opacity(
2145
+ self, opacity: float | Iterable[float] | None, recurse: bool = True
2146
+ ) -> Self:
1994
2147
  self.set_rgba_array(color=None, opacity=opacity, recurse=False)
1995
2148
  if recurse:
1996
2149
  for submob in self.submobjects:
1997
2150
  submob.set_opacity(opacity, recurse=True)
1998
2151
  return self
1999
2152
 
2000
- def get_color(self):
2153
+ def get_color(self) -> str:
2001
2154
  return rgb_to_hex(self.rgbas[0, :3])
2002
2155
 
2003
- def get_opacity(self):
2156
+ def get_opacity(self) -> float:
2004
2157
  return self.rgbas[0, 3]
2005
2158
 
2006
- def set_color_by_gradient(self, *colors):
2007
- self.set_submobject_colors_by_gradient(*colors)
2008
- return self
2159
+ def set_color_by_gradient(self, *colors: ParsableManimColor) -> Self:
2160
+ return self.set_submobject_colors_by_gradient(*colors)
2009
2161
 
2010
- def set_submobject_colors_by_gradient(self, *colors):
2162
+ def set_submobject_colors_by_gradient(self, *colors: ParsableManimColor) -> Self:
2011
2163
  if len(colors) == 0:
2012
2164
  raise Exception("Need at least one color")
2013
2165
  elif len(colors) == 1:
@@ -2021,21 +2173,21 @@ class OpenGLMobject:
2021
2173
  mob.set_color(color)
2022
2174
  return self
2023
2175
 
2024
- def fade(self, darkness=0.5, recurse=True):
2025
- self.set_opacity(1.0 - darkness, recurse=recurse)
2176
+ def fade(self, darkness: float = 0.5, recurse: bool = True) -> Self:
2177
+ return self.set_opacity(1.0 - darkness, recurse=recurse)
2026
2178
 
2027
- def get_gloss(self):
2179
+ def get_gloss(self) -> float:
2028
2180
  return self.gloss
2029
2181
 
2030
- def set_gloss(self, gloss, recurse=True):
2182
+ def set_gloss(self, gloss: float, recurse: bool = True) -> Self:
2031
2183
  for mob in self.get_family(recurse):
2032
2184
  mob.gloss = gloss
2033
2185
  return self
2034
2186
 
2035
- def get_shadow(self):
2187
+ def get_shadow(self) -> float:
2036
2188
  return self.shadow
2037
2189
 
2038
- def set_shadow(self, shadow, recurse=True):
2190
+ def set_shadow(self, shadow: float, recurse: bool = True) -> Self:
2039
2191
  for mob in self.get_family(recurse):
2040
2192
  mob.shadow = shadow
2041
2193
  return self
@@ -2043,8 +2195,11 @@ class OpenGLMobject:
2043
2195
  # Background rectangle
2044
2196
 
2045
2197
  def add_background_rectangle(
2046
- self, color: ParsableManimColor | None = None, opacity: float = 0.75, **kwargs
2047
- ):
2198
+ self,
2199
+ color: ParsableManimColor | None = None,
2200
+ opacity: float = 0.75,
2201
+ **kwargs,
2202
+ ) -> Self:
2048
2203
  # TODO, this does not behave well when the mobject has points,
2049
2204
  # since it gets displayed on top
2050
2205
  """Add a BackgroundRectangle as submobject.
@@ -2082,39 +2237,39 @@ class OpenGLMobject:
2082
2237
  self.add_to_back(self.background_rectangle)
2083
2238
  return self
2084
2239
 
2085
- def add_background_rectangle_to_submobjects(self, **kwargs):
2240
+ def add_background_rectangle_to_submobjects(self, **kwargs) -> Self:
2086
2241
  for submobject in self.submobjects:
2087
2242
  submobject.add_background_rectangle(**kwargs)
2088
2243
  return self
2089
2244
 
2090
- def add_background_rectangle_to_family_members_with_points(self, **kwargs):
2245
+ def add_background_rectangle_to_family_members_with_points(self, **kwargs) -> Self:
2091
2246
  for mob in self.family_members_with_points():
2092
2247
  mob.add_background_rectangle(**kwargs)
2093
2248
  return self
2094
2249
 
2095
2250
  # Getters
2096
2251
 
2097
- def get_bounding_box_point(self, direction):
2252
+ def get_bounding_box_point(self, direction: Vector3D) -> Point3D:
2098
2253
  bb = self.get_bounding_box()
2099
2254
  indices = (np.sign(direction) + 1).astype(int)
2100
2255
  return np.array([bb[indices[i]][i] for i in range(3)])
2101
2256
 
2102
- def get_edge_center(self, direction) -> np.ndarray:
2257
+ def get_edge_center(self, direction: Vector3D) -> Point3D:
2103
2258
  """Get edge coordinates for certain direction."""
2104
2259
  return self.get_bounding_box_point(direction)
2105
2260
 
2106
- def get_corner(self, direction) -> np.ndarray:
2261
+ def get_corner(self, direction: Vector3D) -> Point3D:
2107
2262
  """Get corner coordinates for certain direction."""
2108
2263
  return self.get_bounding_box_point(direction)
2109
2264
 
2110
- def get_center(self) -> np.ndarray:
2265
+ def get_center(self) -> Point3D:
2111
2266
  """Get center coordinates."""
2112
2267
  return self.get_bounding_box()[1]
2113
2268
 
2114
- def get_center_of_mass(self):
2269
+ def get_center_of_mass(self) -> Point3D:
2115
2270
  return self.get_all_points().mean(0)
2116
2271
 
2117
- def get_boundary_point(self, direction):
2272
+ def get_boundary_point(self, direction: Vector3D) -> Point3D:
2118
2273
  all_points = self.get_all_points()
2119
2274
  boundary_directions = all_points - self.get_center()
2120
2275
  norms = np.linalg.norm(boundary_directions, axis=1)
@@ -2122,7 +2277,7 @@ class OpenGLMobject:
2122
2277
  index = np.argmax(np.dot(boundary_directions, np.array(direction).T))
2123
2278
  return all_points[index]
2124
2279
 
2125
- def get_continuous_bounding_box_point(self, direction):
2280
+ def get_continuous_bounding_box_point(self, direction: Vector3D) -> Point3D:
2126
2281
  dl, center, ur = self.get_bounding_box()
2127
2282
  corner_vect = ur - center
2128
2283
  return center + direction / np.max(
@@ -2136,86 +2291,86 @@ class OpenGLMobject:
2136
2291
  ),
2137
2292
  )
2138
2293
 
2139
- def get_top(self) -> np.ndarray:
2294
+ def get_top(self) -> Point3D:
2140
2295
  """Get top coordinates of a box bounding the :class:`~.OpenGLMobject`"""
2141
2296
  return self.get_edge_center(UP)
2142
2297
 
2143
- def get_bottom(self) -> np.ndarray:
2298
+ def get_bottom(self) -> Point3D:
2144
2299
  """Get bottom coordinates of a box bounding the :class:`~.OpenGLMobject`"""
2145
2300
  return self.get_edge_center(DOWN)
2146
2301
 
2147
- def get_right(self) -> np.ndarray:
2302
+ def get_right(self) -> Point3D:
2148
2303
  """Get right coordinates of a box bounding the :class:`~.OpenGLMobject`"""
2149
2304
  return self.get_edge_center(RIGHT)
2150
2305
 
2151
- def get_left(self) -> np.ndarray:
2306
+ def get_left(self) -> Point3D:
2152
2307
  """Get left coordinates of a box bounding the :class:`~.OpenGLMobject`"""
2153
2308
  return self.get_edge_center(LEFT)
2154
2309
 
2155
- def get_zenith(self) -> np.ndarray:
2310
+ def get_zenith(self) -> Point3D:
2156
2311
  """Get zenith coordinates of a box bounding a 3D :class:`~.OpenGLMobject`."""
2157
2312
  return self.get_edge_center(OUT)
2158
2313
 
2159
- def get_nadir(self) -> np.ndarray:
2314
+ def get_nadir(self) -> Point3D:
2160
2315
  """Get nadir (opposite the zenith) coordinates of a box bounding a 3D :class:`~.OpenGLMobject`."""
2161
2316
  return self.get_edge_center(IN)
2162
2317
 
2163
- def length_over_dim(self, dim):
2318
+ def length_over_dim(self, dim: int) -> float:
2164
2319
  bb = self.get_bounding_box()
2165
2320
  return abs((bb[2] - bb[0])[dim])
2166
2321
 
2167
- def get_width(self):
2322
+ def get_width(self) -> float:
2168
2323
  """Returns the width of the mobject."""
2169
2324
  return self.length_over_dim(0)
2170
2325
 
2171
- def get_height(self):
2326
+ def get_height(self) -> float:
2172
2327
  """Returns the height of the mobject."""
2173
2328
  return self.length_over_dim(1)
2174
2329
 
2175
- def get_depth(self):
2330
+ def get_depth(self) -> float:
2176
2331
  """Returns the depth of the mobject."""
2177
2332
  return self.length_over_dim(2)
2178
2333
 
2179
- def get_coord(self, dim: int, direction=ORIGIN):
2334
+ def get_coord(self, dim: int, direction: Vector3D = ORIGIN) -> ManimFloat:
2180
2335
  """Meant to generalize ``get_x``, ``get_y`` and ``get_z``"""
2181
2336
  return self.get_bounding_box_point(direction)[dim]
2182
2337
 
2183
- def get_x(self, direction=ORIGIN) -> np.float64:
2338
+ def get_x(self, direction: Vector3D = ORIGIN) -> ManimFloat:
2184
2339
  """Returns x coordinate of the center of the :class:`~.OpenGLMobject` as ``float``"""
2185
2340
  return self.get_coord(0, direction)
2186
2341
 
2187
- def get_y(self, direction=ORIGIN) -> np.float64:
2342
+ def get_y(self, direction: Vector3D = ORIGIN) -> ManimFloat:
2188
2343
  """Returns y coordinate of the center of the :class:`~.OpenGLMobject` as ``float``"""
2189
2344
  return self.get_coord(1, direction)
2190
2345
 
2191
- def get_z(self, direction=ORIGIN) -> np.float64:
2346
+ def get_z(self, direction: Vector3D = ORIGIN) -> ManimFloat:
2192
2347
  """Returns z coordinate of the center of the :class:`~.OpenGLMobject` as ``float``"""
2193
2348
  return self.get_coord(2, direction)
2194
2349
 
2195
- def get_start(self):
2350
+ def get_start(self) -> Point3D:
2196
2351
  """Returns the point, where the stroke that surrounds the :class:`~.OpenGLMobject` starts."""
2197
2352
  self.throw_error_if_no_points()
2198
2353
  return np.array(self.points[0])
2199
2354
 
2200
- def get_end(self):
2355
+ def get_end(self) -> Point3D:
2201
2356
  """Returns the point, where the stroke that surrounds the :class:`~.OpenGLMobject` ends."""
2202
2357
  self.throw_error_if_no_points()
2203
2358
  return np.array(self.points[-1])
2204
2359
 
2205
- def get_start_and_end(self):
2360
+ def get_start_and_end(self) -> tuple[Point3D, Point3D]:
2206
2361
  """Returns starting and ending point of a stroke as a ``tuple``."""
2207
2362
  return self.get_start(), self.get_end()
2208
2363
 
2209
- def point_from_proportion(self, alpha):
2364
+ def point_from_proportion(self, alpha: float) -> Point3D:
2210
2365
  points = self.points
2211
2366
  i, subalpha = integer_interpolate(0, len(points) - 1, alpha)
2212
2367
  return interpolate(points[i], points[i + 1], subalpha)
2213
2368
 
2214
- def pfp(self, alpha):
2369
+ def pfp(self, alpha: float) -> Point3D:
2215
2370
  """Abbreviation for point_from_proportion"""
2216
2371
  return self.point_from_proportion(alpha)
2217
2372
 
2218
- def get_pieces(self, n_pieces):
2373
+ def get_pieces(self, n_pieces: int) -> OpenGLMobject:
2219
2374
  template = self.copy()
2220
2375
  template.submobjects = []
2221
2376
  alphas = np.linspace(0, 1, n_pieces + 1)
@@ -2226,34 +2381,36 @@ class OpenGLMobject:
2226
2381
  )
2227
2382
  )
2228
2383
 
2229
- def get_z_index_reference_point(self):
2384
+ def get_z_index_reference_point(self) -> Point3D:
2230
2385
  # TODO, better place to define default z_index_group?
2231
2386
  z_index_group = getattr(self, "z_index_group", self)
2232
2387
  return z_index_group.get_center()
2233
2388
 
2234
2389
  # Match other mobject properties
2235
2390
 
2236
- def match_color(self, mobject: OpenGLMobject):
2391
+ def match_color(self, mobject: OpenGLMobject) -> Self:
2237
2392
  """Match the color with the color of another :class:`~.OpenGLMobject`."""
2238
2393
  return self.set_color(mobject.get_color())
2239
2394
 
2240
- def match_dim_size(self, mobject: OpenGLMobject, dim, **kwargs):
2395
+ def match_dim_size(self, mobject: OpenGLMobject, dim: int, **kwargs) -> Self:
2241
2396
  """Match the specified dimension with the dimension of another :class:`~.OpenGLMobject`."""
2242
2397
  return self.rescale_to_fit(mobject.length_over_dim(dim), dim, **kwargs)
2243
2398
 
2244
- def match_width(self, mobject: OpenGLMobject, **kwargs):
2399
+ def match_width(self, mobject: OpenGLMobject, **kwargs) -> Self:
2245
2400
  """Match the width with the width of another :class:`~.OpenGLMobject`."""
2246
2401
  return self.match_dim_size(mobject, 0, **kwargs)
2247
2402
 
2248
- def match_height(self, mobject: OpenGLMobject, **kwargs):
2403
+ def match_height(self, mobject: OpenGLMobject, **kwargs) -> Self:
2249
2404
  """Match the height with the height of another :class:`~.OpenGLMobject`."""
2250
2405
  return self.match_dim_size(mobject, 1, **kwargs)
2251
2406
 
2252
- def match_depth(self, mobject: OpenGLMobject, **kwargs):
2407
+ def match_depth(self, mobject: OpenGLMobject, **kwargs) -> Self:
2253
2408
  """Match the depth with the depth of another :class:`~.OpenGLMobject`."""
2254
2409
  return self.match_dim_size(mobject, 2, **kwargs)
2255
2410
 
2256
- def match_coord(self, mobject: OpenGLMobject, dim, direction=ORIGIN):
2411
+ def match_coord(
2412
+ self, mobject: OpenGLMobject, dim: int, direction: Vector3D = ORIGIN
2413
+ ) -> Self:
2257
2414
  """Match the coordinates with the coordinates of another :class:`~.OpenGLMobject`."""
2258
2415
  return self.set_coord(
2259
2416
  mobject.get_coord(dim, direction),
@@ -2261,23 +2418,23 @@ class OpenGLMobject:
2261
2418
  direction=direction,
2262
2419
  )
2263
2420
 
2264
- def match_x(self, mobject, direction=ORIGIN):
2421
+ def match_x(self, mobject: OpenGLMobject, direction: Vector3D = ORIGIN) -> Self:
2265
2422
  """Match x coord. to the x coord. of another :class:`~.OpenGLMobject`."""
2266
2423
  return self.match_coord(mobject, 0, direction)
2267
2424
 
2268
- def match_y(self, mobject, direction=ORIGIN):
2425
+ def match_y(self, mobject: OpenGLMobject, direction: Vector3D = ORIGIN) -> Self:
2269
2426
  """Match y coord. to the x coord. of another :class:`~.OpenGLMobject`."""
2270
2427
  return self.match_coord(mobject, 1, direction)
2271
2428
 
2272
- def match_z(self, mobject, direction=ORIGIN):
2429
+ def match_z(self, mobject: OpenGLMobject, direction: Vector3D = ORIGIN) -> Self:
2273
2430
  """Match z coord. to the x coord. of another :class:`~.OpenGLMobject`."""
2274
2431
  return self.match_coord(mobject, 2, direction)
2275
2432
 
2276
2433
  def align_to(
2277
2434
  self,
2278
- mobject_or_point: OpenGLMobject | Sequence[float],
2279
- direction=ORIGIN,
2280
- ):
2435
+ mobject_or_point: OpenGLMobject | Point3DLike,
2436
+ direction: Vector3D = ORIGIN,
2437
+ ) -> Self:
2281
2438
  """
2282
2439
  Examples:
2283
2440
  mob1.align_to(mob2, UP) moves mob1 vertically so that its
@@ -2297,21 +2454,22 @@ class OpenGLMobject:
2297
2454
  self.set_coord(point[dim], dim, direction)
2298
2455
  return self
2299
2456
 
2300
- def get_group_class(self):
2457
+ def get_group_class(self) -> type[OpenGLGroup]:
2301
2458
  return OpenGLGroup
2302
2459
 
2303
2460
  @staticmethod
2304
- def get_mobject_type_class():
2461
+ def get_mobject_type_class() -> type[OpenGLMobject]:
2305
2462
  """Return the base class of this mobject type."""
2306
2463
  return OpenGLMobject
2307
2464
 
2308
2465
  # Alignment
2309
2466
 
2310
- def align_data_and_family(self, mobject):
2467
+ def align_data_and_family(self, mobject: OpenGLMobject) -> Self:
2311
2468
  self.align_family(mobject)
2312
2469
  self.align_data(mobject)
2470
+ return self
2313
2471
 
2314
- def align_data(self, mobject):
2472
+ def align_data(self, mobject: OpenGLMobject) -> Self:
2315
2473
  # In case any data arrays get resized when aligned to shader data
2316
2474
  # self.refresh_shader_data()
2317
2475
  for mob1, mob2 in zip(self.get_family(), mobject.get_family()):
@@ -2327,14 +2485,15 @@ class OpenGLMobject:
2327
2485
  mob1.data[key] = resize_preserving_order(arr1, len(arr2))
2328
2486
  elif len(arr1) > len(arr2):
2329
2487
  mob2.data[key] = resize_preserving_order(arr2, len(arr1))
2488
+ return self
2330
2489
 
2331
- def align_points(self, mobject):
2490
+ def align_points(self, mobject: OpenGLMobject) -> Self:
2332
2491
  max_len = max(self.get_num_points(), mobject.get_num_points())
2333
2492
  for mob in (self, mobject):
2334
2493
  mob.resize_points(max_len, resize_func=resize_preserving_order)
2335
2494
  return self
2336
2495
 
2337
- def align_family(self, mobject):
2496
+ def align_family(self, mobject: OpenGLMobject) -> Self:
2338
2497
  mob1 = self
2339
2498
  mob2 = mobject
2340
2499
  n1 = len(mob1)
@@ -2347,14 +2506,14 @@ class OpenGLMobject:
2347
2506
  sm1.align_family(sm2)
2348
2507
  return self
2349
2508
 
2350
- def push_self_into_submobjects(self):
2509
+ def push_self_into_submobjects(self) -> Self:
2351
2510
  copy = self.deepcopy()
2352
2511
  copy.submobjects = []
2353
2512
  self.resize_points(0)
2354
2513
  self.add(copy)
2355
2514
  return self
2356
2515
 
2357
- def add_n_more_submobjects(self, n):
2516
+ def add_n_more_submobjects(self, n: int) -> Self:
2358
2517
  if n == 0:
2359
2518
  return self
2360
2519
 
@@ -2383,7 +2542,13 @@ class OpenGLMobject:
2383
2542
 
2384
2543
  # Interpolate
2385
2544
 
2386
- def interpolate(self, mobject1, mobject2, alpha, path_func=straight_path()):
2545
+ def interpolate(
2546
+ self,
2547
+ mobject1: OpenGLMobject,
2548
+ mobject2: OpenGLMobject,
2549
+ alpha: float,
2550
+ path_func: PathFuncType = straight_path(),
2551
+ ) -> Self:
2387
2552
  """Turns this :class:`~.OpenGLMobject` into an interpolation between ``mobject1``
2388
2553
  and ``mobject2``.
2389
2554
 
@@ -2412,10 +2577,7 @@ class OpenGLMobject:
2412
2577
  if key not in mobject1.data or key not in mobject2.data:
2413
2578
  continue
2414
2579
 
2415
- if key in ("points", "bounding_box"):
2416
- func = path_func
2417
- else:
2418
- func = interpolate
2580
+ func = path_func if key in ("points", "bounding_box") else interpolate
2419
2581
 
2420
2582
  self.data[key][:] = func(mobject1.data[key], mobject2.data[key], alpha)
2421
2583
 
@@ -2436,7 +2598,9 @@ class OpenGLMobject:
2436
2598
  )
2437
2599
  return self
2438
2600
 
2439
- def pointwise_become_partial(self, mobject, a, b):
2601
+ def pointwise_become_partial(
2602
+ self, mobject: OpenGLMobject, a: float, b: float
2603
+ ) -> None:
2440
2604
  """
2441
2605
  Set points in such a way as to become only
2442
2606
  part of mobject.
@@ -2453,7 +2617,7 @@ class OpenGLMobject:
2453
2617
  match_depth: bool = False,
2454
2618
  match_center: bool = False,
2455
2619
  stretch: bool = False,
2456
- ):
2620
+ ) -> Self:
2457
2621
  """Edit all data and submobjects to be identical
2458
2622
  to another :class:`~.OpenGLMobject`
2459
2623
 
@@ -2488,7 +2652,6 @@ class OpenGLMobject:
2488
2652
  circ.become(square)
2489
2653
  self.wait(0.5)
2490
2654
  """
2491
-
2492
2655
  if stretch:
2493
2656
  mobject.stretch_to_fit_height(self.height)
2494
2657
  mobject.stretch_to_fit_width(self.width)
@@ -2513,7 +2676,7 @@ class OpenGLMobject:
2513
2676
 
2514
2677
  # Locking data
2515
2678
 
2516
- def lock_data(self, keys):
2679
+ def lock_data(self, keys: Iterable[str]) -> None:
2517
2680
  """
2518
2681
  To speed up some animations, particularly transformations,
2519
2682
  it can be handy to acknowledge which pieces of data
@@ -2527,7 +2690,9 @@ class OpenGLMobject:
2527
2690
  self.refresh_shader_data()
2528
2691
  self.locked_data_keys = set(keys)
2529
2692
 
2530
- def lock_matching_data(self, mobject1, mobject2):
2693
+ def lock_matching_data(
2694
+ self, mobject1: OpenGLMobject, mobject2: OpenGLMobject
2695
+ ) -> Self:
2531
2696
  for sm, sm1, sm2 in zip(
2532
2697
  self.get_family(),
2533
2698
  mobject1.get_family(),
@@ -2544,57 +2709,57 @@ class OpenGLMobject:
2544
2709
  )
2545
2710
  return self
2546
2711
 
2547
- def unlock_data(self):
2712
+ def unlock_data(self) -> None:
2548
2713
  for mob in self.get_family():
2549
2714
  mob.locked_data_keys = set()
2550
2715
 
2551
2716
  # Operations touching shader uniforms
2552
2717
 
2553
2718
  @affects_shader_info_id
2554
- def fix_in_frame(self):
2719
+ def fix_in_frame(self) -> Self:
2555
2720
  self.is_fixed_in_frame = 1.0
2556
2721
  return self
2557
2722
 
2558
2723
  @affects_shader_info_id
2559
- def fix_orientation(self):
2724
+ def fix_orientation(self) -> Self:
2560
2725
  self.is_fixed_orientation = 1.0
2561
2726
  self.fixed_orientation_center = tuple(self.get_center())
2562
2727
  self.depth_test = True
2563
2728
  return self
2564
2729
 
2565
2730
  @affects_shader_info_id
2566
- def unfix_from_frame(self):
2731
+ def unfix_from_frame(self) -> Self:
2567
2732
  self.is_fixed_in_frame = 0.0
2568
2733
  return self
2569
2734
 
2570
2735
  @affects_shader_info_id
2571
- def unfix_orientation(self):
2736
+ def unfix_orientation(self) -> Self:
2572
2737
  self.is_fixed_orientation = 0.0
2573
2738
  self.fixed_orientation_center = (0, 0, 0)
2574
2739
  self.depth_test = False
2575
2740
  return self
2576
2741
 
2577
2742
  @affects_shader_info_id
2578
- def apply_depth_test(self):
2743
+ def apply_depth_test(self) -> Self:
2579
2744
  self.depth_test = True
2580
2745
  return self
2581
2746
 
2582
2747
  @affects_shader_info_id
2583
- def deactivate_depth_test(self):
2748
+ def deactivate_depth_test(self) -> Self:
2584
2749
  self.depth_test = False
2585
2750
  return self
2586
2751
 
2587
2752
  # Shader code manipulation
2588
2753
 
2589
- def replace_shader_code(self, old, new):
2754
+ def replace_shader_code(self, old_code: str, new_code: str) -> Self:
2590
2755
  # TODO, will this work with VMobject structure, given
2591
2756
  # that it does not simpler return shader_wrappers of
2592
2757
  # family?
2593
2758
  for wrapper in self.get_shader_wrapper_list():
2594
- wrapper.replace_code(old, new)
2759
+ wrapper.replace_code(old_code, new_code)
2595
2760
  return self
2596
2761
 
2597
- def set_color_by_code(self, glsl_code):
2762
+ def set_color_by_code(self, glsl_code: str) -> Self:
2598
2763
  """
2599
2764
  Takes a snippet of code and inserts it into a
2600
2765
  context which has the following variables:
@@ -2606,11 +2771,11 @@ class OpenGLMobject:
2606
2771
 
2607
2772
  def set_color_by_xyz_func(
2608
2773
  self,
2609
- glsl_snippet,
2610
- min_value=-5.0,
2611
- max_value=5.0,
2612
- colormap="viridis",
2613
- ):
2774
+ glsl_snippet: str,
2775
+ min_value: float = -5.0,
2776
+ max_value: float = 5.0,
2777
+ colormap: str = "viridis",
2778
+ ) -> Self:
2614
2779
  """
2615
2780
  Pass in a glsl expression in terms of x, y and z which returns
2616
2781
  a float.
@@ -2621,22 +2786,17 @@ class OpenGLMobject:
2621
2786
  glsl_snippet = glsl_snippet.replace(char, "point." + char)
2622
2787
  rgb_list = get_colormap_list(colormap)
2623
2788
  self.set_color_by_code(
2624
- "color.rgb = float_to_color({}, {}, {}, {});".format(
2625
- glsl_snippet,
2626
- float(min_value),
2627
- float(max_value),
2628
- get_colormap_code(rgb_list),
2629
- ),
2789
+ f"color.rgb = float_to_color({glsl_snippet}, {float(min_value)}, {float(max_value)}, {get_colormap_code(rgb_list)});",
2630
2790
  )
2631
2791
  return self
2632
2792
 
2633
2793
  # For shader data
2634
2794
 
2635
- def refresh_shader_wrapper_id(self):
2795
+ def refresh_shader_wrapper_id(self) -> Self:
2636
2796
  self.get_shader_wrapper().refresh_id()
2637
2797
  return self
2638
2798
 
2639
- def get_shader_wrapper(self):
2799
+ def get_shader_wrapper(self) -> ShaderWrapper:
2640
2800
  from manim.renderer.shader_wrapper import ShaderWrapper
2641
2801
 
2642
2802
  # if hasattr(self, "__shader_wrapper"):
@@ -2653,7 +2813,7 @@ class OpenGLMobject:
2653
2813
  )
2654
2814
  return self.shader_wrapper
2655
2815
 
2656
- def get_shader_wrapper_list(self):
2816
+ def get_shader_wrapper_list(self) -> Sequence[ShaderWrapper]:
2657
2817
  shader_wrappers = it.chain(
2658
2818
  [self.get_shader_wrapper()],
2659
2819
  *(sm.get_shader_wrapper_list() for sm in self.submobjects),
@@ -2670,7 +2830,7 @@ class OpenGLMobject:
2670
2830
  result.append(shader_wrapper)
2671
2831
  return result
2672
2832
 
2673
- def check_data_alignment(self, array, data_key):
2833
+ def check_data_alignment(self, array: npt.NDArray, data_key: str) -> Self:
2674
2834
  # Makes sure that self.data[key] can be broadcast into
2675
2835
  # the given array, meaning its length has to be either 1
2676
2836
  # or the length of the array
@@ -2682,45 +2842,50 @@ class OpenGLMobject:
2682
2842
  )
2683
2843
  return self
2684
2844
 
2685
- def get_resized_shader_data_array(self, length):
2845
+ def get_resized_shader_data_array(self, length: float) -> npt.NDArray:
2686
2846
  # If possible, try to populate an existing array, rather
2687
2847
  # than recreating it each frame
2688
2848
  points = self.points
2689
2849
  shader_data = np.zeros(len(points), dtype=self.shader_dtype)
2690
2850
  return shader_data
2691
2851
 
2692
- def read_data_to_shader(self, shader_data, shader_data_key, data_key):
2852
+ def read_data_to_shader(
2853
+ self,
2854
+ shader_data: npt.NDArray, # has structured data type, ex. ("point", np.float32, (3,))
2855
+ shader_data_key: str,
2856
+ data_key: str,
2857
+ ) -> None:
2693
2858
  if data_key in self.locked_data_keys:
2694
2859
  return
2695
2860
  self.check_data_alignment(shader_data, data_key)
2696
2861
  shader_data[shader_data_key] = self.data[data_key]
2697
2862
 
2698
- def get_shader_data(self):
2863
+ def get_shader_data(self) -> npt.NDArray:
2699
2864
  shader_data = self.get_resized_shader_data_array(self.get_num_points())
2700
2865
  self.read_data_to_shader(shader_data, "point", "points")
2701
2866
  return shader_data
2702
2867
 
2703
- def refresh_shader_data(self):
2868
+ def refresh_shader_data(self) -> None:
2704
2869
  self.get_shader_data()
2705
2870
 
2706
- def get_shader_uniforms(self):
2871
+ def get_shader_uniforms(self) -> dict[str, Any]:
2707
2872
  return self.uniforms
2708
2873
 
2709
- def get_shader_vert_indices(self):
2874
+ def get_shader_vert_indices(self) -> Sequence[int]:
2710
2875
  return self.shader_indices
2711
2876
 
2712
2877
  @property
2713
- def submobjects(self):
2878
+ def submobjects(self) -> Sequence[OpenGLMobject]:
2714
2879
  return self._submobjects if hasattr(self, "_submobjects") else []
2715
2880
 
2716
2881
  @submobjects.setter
2717
- def submobjects(self, submobject_list):
2882
+ def submobjects(self, submobject_list: Iterable[OpenGLMobject]) -> None:
2718
2883
  self.remove(*self.submobjects)
2719
2884
  self.add(*submobject_list)
2720
2885
 
2721
2886
  # Errors
2722
2887
 
2723
- def throw_error_if_no_points(self):
2888
+ def throw_error_if_no_points(self) -> None:
2724
2889
  if not self.has_points():
2725
2890
  message = (
2726
2891
  "Cannot call OpenGLMobject.{} " + "for a OpenGLMobject with no points"
@@ -2730,40 +2895,42 @@ class OpenGLMobject:
2730
2895
 
2731
2896
 
2732
2897
  class OpenGLGroup(OpenGLMobject):
2733
- def __init__(self, *mobjects, **kwargs):
2734
- if not all(isinstance(m, OpenGLMobject) for m in mobjects):
2735
- raise Exception("All submobjects must be of type OpenGLMobject")
2898
+ def __init__(self, *mobjects: OpenGLMobject, **kwargs):
2736
2899
  super().__init__(**kwargs)
2737
2900
  self.add(*mobjects)
2738
2901
 
2739
2902
 
2740
2903
  class OpenGLPoint(OpenGLMobject):
2741
2904
  def __init__(
2742
- self, location=ORIGIN, artificial_width=1e-6, artificial_height=1e-6, **kwargs
2905
+ self,
2906
+ location: Point3DLike = ORIGIN,
2907
+ artificial_width: float = 1e-6,
2908
+ artificial_height: float = 1e-6,
2909
+ **kwargs,
2743
2910
  ):
2744
2911
  self.artificial_width = artificial_width
2745
2912
  self.artificial_height = artificial_height
2746
2913
  super().__init__(**kwargs)
2747
2914
  self.set_location(location)
2748
2915
 
2749
- def get_width(self):
2916
+ def get_width(self) -> float:
2750
2917
  return self.artificial_width
2751
2918
 
2752
- def get_height(self):
2919
+ def get_height(self) -> float:
2753
2920
  return self.artificial_height
2754
2921
 
2755
- def get_location(self):
2922
+ def get_location(self) -> Point3D:
2756
2923
  return self.points[0].copy()
2757
2924
 
2758
- def get_bounding_box_point(self, *args, **kwargs):
2925
+ def get_bounding_box_point(self, *args, **kwargs) -> Point3D:
2759
2926
  return self.get_location()
2760
2927
 
2761
- def set_location(self, new_loc):
2928
+ def set_location(self, new_loc: Point3D) -> None:
2762
2929
  self.set_points(np.array(new_loc, ndmin=2, dtype=float))
2763
2930
 
2764
2931
 
2765
2932
  class _AnimationBuilder:
2766
- def __init__(self, mobject):
2933
+ def __init__(self, mobject: OpenGLMobject):
2767
2934
  self.mobject = mobject
2768
2935
  self.mobject.generate_target()
2769
2936
 
@@ -2775,7 +2942,7 @@ class _AnimationBuilder:
2775
2942
  self.cannot_pass_args = False
2776
2943
  self.anim_args = {}
2777
2944
 
2778
- def __call__(self, **kwargs):
2945
+ def __call__(self, **kwargs) -> Self:
2779
2946
  if self.cannot_pass_args:
2780
2947
  raise ValueError(
2781
2948
  "Animation arguments must be passed before accessing methods and can only be passed once",
@@ -2786,14 +2953,13 @@ class _AnimationBuilder:
2786
2953
 
2787
2954
  return self
2788
2955
 
2789
- def __getattr__(self, method_name):
2956
+ def __getattr__(self, method_name: str) -> Callable[..., Self]:
2790
2957
  method = getattr(self.mobject.target, method_name)
2791
2958
  has_overridden_animation = hasattr(method, "_override_animate")
2792
2959
 
2793
2960
  if (self.is_chaining and has_overridden_animation) or self.overridden_animation:
2794
2961
  raise NotImplementedError(
2795
- "Method chaining is currently not supported for "
2796
- "overridden animations",
2962
+ "Method chaining is currently not supported for overridden animations",
2797
2963
  )
2798
2964
 
2799
2965
  def update_target(*method_args, **method_kwargs):
@@ -2814,7 +2980,7 @@ class _AnimationBuilder:
2814
2980
 
2815
2981
  return update_target
2816
2982
 
2817
- def build(self):
2983
+ def build(self) -> _MethodAnimation:
2818
2984
  from manim.animation.transform import _MethodAnimation
2819
2985
 
2820
2986
  if self.overridden_animation:
@@ -2828,7 +2994,7 @@ class _AnimationBuilder:
2828
2994
  return anim
2829
2995
 
2830
2996
 
2831
- def override_animate(method):
2997
+ def override_animate(method: types.FunctionType) -> types.FunctionType:
2832
2998
  r"""Decorator for overriding method animations.
2833
2999
 
2834
3000
  This allows to specify a method (returning an :class:`~.Animation`)