manim 0.18.1__py3-none-any.whl → 0.19.0__py3-none-any.whl

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

Potentially problematic release.


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

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