manim 0.17.0__py3-none-any.whl → 0.19.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. manim/__init__.py +11 -6
  2. manim/__main__.py +62 -19
  3. manim/_config/__init__.py +10 -9
  4. manim/_config/cli_colors.py +26 -9
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +23 -13
  7. manim/_config/utils.py +662 -468
  8. manim/animation/animation.py +164 -18
  9. manim/animation/changing.py +34 -23
  10. manim/animation/composition.py +265 -67
  11. manim/animation/creation.py +208 -26
  12. manim/animation/fading.py +16 -18
  13. manim/animation/growing.py +35 -15
  14. manim/animation/indication.py +150 -76
  15. manim/animation/movement.py +56 -22
  16. manim/animation/numbers.py +64 -6
  17. manim/animation/rotation.py +78 -7
  18. manim/animation/specialized.py +6 -7
  19. manim/animation/speedmodifier.py +13 -10
  20. manim/animation/transform.py +14 -11
  21. manim/animation/transform_matching_parts.py +3 -4
  22. manim/animation/updaters/mobject_update_utils.py +152 -30
  23. manim/animation/updaters/update.py +10 -7
  24. manim/camera/camera.py +182 -118
  25. manim/camera/mapping_camera.py +34 -3
  26. manim/camera/moving_camera.py +95 -74
  27. manim/camera/multi_camera.py +23 -15
  28. manim/camera/three_d_camera.py +70 -52
  29. manim/cli/__init__.py +17 -0
  30. manim/cli/cfg/group.py +76 -44
  31. manim/cli/checkhealth/checks.py +192 -0
  32. manim/cli/checkhealth/commands.py +90 -0
  33. manim/cli/default_group.py +158 -25
  34. manim/cli/init/commands.py +33 -25
  35. manim/cli/plugins/commands.py +16 -3
  36. manim/cli/render/commands.py +72 -60
  37. manim/cli/render/ease_of_access_options.py +4 -3
  38. manim/cli/render/global_options.py +59 -17
  39. manim/cli/render/output_options.py +6 -5
  40. manim/cli/render/render_options.py +98 -33
  41. manim/constants.py +109 -59
  42. manim/data_structures.py +31 -0
  43. manim/mobject/frame.py +8 -5
  44. manim/mobject/geometry/__init__.py +1 -0
  45. manim/mobject/geometry/arc.py +277 -135
  46. manim/mobject/geometry/boolean_ops.py +32 -31
  47. manim/mobject/geometry/labeled.py +376 -0
  48. manim/mobject/geometry/line.py +192 -87
  49. manim/mobject/geometry/polygram.py +224 -58
  50. manim/mobject/geometry/shape_matchers.py +61 -25
  51. manim/mobject/geometry/tips.py +122 -48
  52. manim/mobject/graph.py +1027 -419
  53. manim/mobject/graphing/coordinate_systems.py +533 -278
  54. manim/mobject/graphing/functions.py +53 -32
  55. manim/mobject/graphing/number_line.py +123 -65
  56. manim/mobject/graphing/probability.py +88 -62
  57. manim/mobject/graphing/scale.py +33 -19
  58. manim/mobject/logo.py +118 -28
  59. manim/mobject/matrix.py +87 -83
  60. manim/mobject/mobject.py +912 -442
  61. manim/mobject/opengl/dot_cloud.py +16 -5
  62. manim/mobject/opengl/opengl_compatibility.py +4 -2
  63. manim/mobject/opengl/opengl_geometry.py +254 -153
  64. manim/mobject/opengl/opengl_image_mobject.py +3 -1
  65. manim/mobject/opengl/opengl_mobject.py +779 -482
  66. manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
  67. manim/mobject/opengl/opengl_surface.py +14 -92
  68. manim/mobject/opengl/opengl_three_dimensions.py +12 -8
  69. manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
  70. manim/mobject/svg/brace.py +173 -41
  71. manim/mobject/svg/svg_mobject.py +139 -53
  72. manim/mobject/table.py +61 -68
  73. manim/mobject/text/code_mobject.py +193 -539
  74. manim/mobject/text/numbers.py +81 -34
  75. manim/mobject/text/tex_mobject.py +130 -78
  76. manim/mobject/text/text_mobject.py +288 -164
  77. manim/mobject/three_d/polyhedra.py +111 -13
  78. manim/mobject/three_d/three_d_utils.py +17 -8
  79. manim/mobject/three_d/three_dimensions.py +239 -106
  80. manim/mobject/types/image_mobject.py +50 -30
  81. manim/mobject/types/point_cloud_mobject.py +120 -75
  82. manim/mobject/types/vectorized_mobject.py +841 -408
  83. manim/mobject/value_tracker.py +105 -38
  84. manim/mobject/vector_field.py +50 -31
  85. manim/opengl/__init__.py +3 -3
  86. manim/plugins/__init__.py +14 -1
  87. manim/plugins/plugins_flags.py +10 -14
  88. manim/renderer/cairo_renderer.py +65 -50
  89. manim/renderer/opengl_renderer.py +89 -69
  90. manim/renderer/opengl_renderer_window.py +39 -18
  91. manim/renderer/shader.py +123 -87
  92. manim/renderer/shader_wrapper.py +44 -28
  93. manim/renderer/vectorized_mobject_rendering.py +38 -10
  94. manim/scene/moving_camera_scene.py +32 -3
  95. manim/scene/scene.py +507 -242
  96. manim/scene/scene_file_writer.py +371 -220
  97. manim/scene/section.py +20 -16
  98. manim/scene/three_d_scene.py +14 -22
  99. manim/scene/vector_space_scene.py +223 -129
  100. manim/scene/zoomed_scene.py +46 -41
  101. manim/typing.py +990 -0
  102. manim/utils/bezier.py +1823 -371
  103. manim/utils/caching.py +12 -5
  104. manim/utils/color/AS2700.py +236 -0
  105. manim/utils/color/BS381.py +318 -0
  106. manim/utils/color/DVIPSNAMES.py +96 -0
  107. manim/utils/color/SVGNAMES.py +179 -0
  108. manim/utils/color/X11.py +533 -0
  109. manim/utils/color/XKCD.py +952 -0
  110. manim/utils/color/__init__.py +61 -0
  111. manim/utils/color/core.py +1667 -0
  112. manim/utils/color/manim_colors.py +218 -0
  113. manim/utils/commands.py +48 -20
  114. manim/utils/config_ops.py +39 -19
  115. manim/utils/debug.py +8 -7
  116. manim/utils/deprecation.py +86 -39
  117. manim/utils/docbuild/__init__.py +17 -0
  118. manim/utils/docbuild/autoaliasattr_directive.py +236 -0
  119. manim/utils/docbuild/autocolor_directive.py +99 -0
  120. manim/utils/docbuild/manim_directive.py +94 -41
  121. manim/utils/docbuild/module_parsing.py +245 -0
  122. manim/utils/exceptions.py +6 -0
  123. manim/utils/family.py +5 -3
  124. manim/utils/family_ops.py +17 -4
  125. manim/utils/file_ops.py +27 -17
  126. manim/utils/hashing.py +55 -45
  127. manim/utils/images.py +13 -7
  128. manim/utils/ipython_magic.py +13 -7
  129. manim/utils/iterables.py +163 -120
  130. manim/utils/module_ops.py +66 -24
  131. manim/utils/opengl.py +77 -24
  132. manim/utils/parameter_parsing.py +32 -0
  133. manim/utils/paths.py +30 -33
  134. manim/utils/polylabel.py +235 -0
  135. manim/utils/qhull.py +218 -0
  136. manim/utils/rate_functions.py +98 -32
  137. manim/utils/simple_functions.py +25 -33
  138. manim/utils/sounds.py +7 -1
  139. manim/utils/space_ops.py +188 -115
  140. manim/utils/testing/__init__.py +17 -0
  141. manim/utils/testing/_frames_testers.py +13 -8
  142. manim/utils/testing/_show_diff.py +5 -3
  143. manim/utils/testing/_test_class_makers.py +34 -18
  144. manim/utils/testing/frames_comparison.py +37 -19
  145. manim/utils/tex.py +130 -198
  146. manim/utils/tex_file_writing.py +77 -47
  147. manim/utils/tex_templates.py +2 -1
  148. manim/utils/unit.py +6 -5
  149. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
  150. manim-0.19.1.dist-info/RECORD +220 -0
  151. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
  152. manim-0.19.1.dist-info/entry_points.txt +3 -0
  153. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
  154. manim/cli/new/group.py +0 -189
  155. manim/communitycolors.py +0 -9
  156. manim/gui/__init__.py +0 -0
  157. manim/gui/gui.py +0 -82
  158. manim/plugins/import_plugins.py +0 -43
  159. manim/utils/color.py +0 -552
  160. manim-0.17.0.dist-info/RECORD +0 -206
  161. manim-0.17.0.dist-info/entry_points.txt +0 -4
  162. /manim/cli/{new → checkhealth}/__init__.py +0 -0
  163. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
@@ -5,8 +5,8 @@ from __future__ import annotations
5
5
  __all__ = ["AbstractImageMobject", "ImageMobject", "ImageMobjectFromCamera"]
6
6
 
7
7
  import pathlib
8
+ from typing import TYPE_CHECKING, Any
8
9
 
9
- import colour
10
10
  import numpy as np
11
11
  from PIL import Image
12
12
  from PIL.Image import Resampling
@@ -14,12 +14,23 @@ from PIL.Image import Resampling
14
14
  from manim.mobject.geometry.shape_matchers import SurroundingRectangle
15
15
 
16
16
  from ... import config
17
+ from ...camera.moving_camera import MovingCamera
17
18
  from ...constants import *
18
19
  from ...mobject.mobject import Mobject
19
20
  from ...utils.bezier import interpolate
20
- from ...utils.color import WHITE, color_to_int_rgb
21
+ from ...utils.color import WHITE, ManimColor, color_to_int_rgb
21
22
  from ...utils.images import change_to_rgba_array, get_full_raster_image_path
22
23
 
24
+ __all__ = ["ImageMobject", "ImageMobjectFromCamera"]
25
+
26
+ if TYPE_CHECKING:
27
+ import numpy.typing as npt
28
+ from typing_extensions import Self
29
+
30
+ from manim.typing import PixelArray, StrPath
31
+
32
+ from ...camera.moving_camera import MovingCamera
33
+
23
34
 
24
35
  class AbstractImageMobject(Mobject):
25
36
  """
@@ -38,23 +49,23 @@ class AbstractImageMobject(Mobject):
38
49
  def __init__(
39
50
  self,
40
51
  scale_to_resolution: int,
41
- pixel_array_dtype="uint8",
42
- resampling_algorithm=Resampling.BICUBIC,
43
- **kwargs,
44
- ):
52
+ pixel_array_dtype: str = "uint8",
53
+ resampling_algorithm: Resampling = Resampling.BICUBIC,
54
+ **kwargs: Any,
55
+ ) -> None:
45
56
  self.pixel_array_dtype = pixel_array_dtype
46
57
  self.scale_to_resolution = scale_to_resolution
47
58
  self.set_resampling_algorithm(resampling_algorithm)
48
59
  super().__init__(**kwargs)
49
60
 
50
- def get_pixel_array(self):
61
+ def get_pixel_array(self) -> PixelArray:
51
62
  raise NotImplementedError()
52
63
 
53
64
  def set_color(self, color, alpha=None, family=True):
54
65
  # Likely to be implemented in subclasses, but no obligation
55
66
  pass
56
67
 
57
- def set_resampling_algorithm(self, resampling_algorithm: int):
68
+ def set_resampling_algorithm(self, resampling_algorithm: int) -> Self:
58
69
  """
59
70
  Sets the interpolation method for upscaling the image. By default the image is
60
71
  interpolated using bicubic algorithm. This method lets you change it.
@@ -84,14 +95,16 @@ class AbstractImageMobject(Mobject):
84
95
  "Available algorithms: 'bicubic', 'nearest', 'box', 'bilinear', "
85
96
  "'hamming', 'lanczos'.",
86
97
  )
98
+ return self
87
99
 
88
- def reset_points(self):
89
- # Corresponding corners of image are fixed to these 3 points
100
+ def reset_points(self) -> None:
101
+ """Sets :attr:`points` to be the four image corners."""
90
102
  self.points = np.array(
91
103
  [
92
104
  UP + LEFT,
93
105
  UP + RIGHT,
94
106
  DOWN + LEFT,
107
+ DOWN + RIGHT,
95
108
  ],
96
109
  )
97
110
  self.center()
@@ -168,15 +181,15 @@ class ImageMobject(AbstractImageMobject):
168
181
 
169
182
  def __init__(
170
183
  self,
171
- filename_or_array,
184
+ filename_or_array: StrPath | npt.NDArray,
172
185
  scale_to_resolution: int = QUALITIES[DEFAULT_QUALITY]["pixel_height"],
173
- invert=False,
174
- image_mode="RGBA",
175
- **kwargs,
176
- ):
177
- self.fill_opacity = 1
178
- self.stroke_opacity = 1
179
- self.invert = invert
186
+ invert: bool = False,
187
+ image_mode: str = "RGBA",
188
+ **kwargs: Any,
189
+ ) -> None:
190
+ self.fill_opacity: float = 1
191
+ self.stroke_opacity: float = 1
192
+ self.invert_image = invert
180
193
  self.image_mode = image_mode
181
194
  if isinstance(filename_or_array, (str, pathlib.PurePath)):
182
195
  path = get_full_raster_image_path(filename_or_array)
@@ -189,8 +202,11 @@ class ImageMobject(AbstractImageMobject):
189
202
  self.pixel_array = change_to_rgba_array(
190
203
  self.pixel_array, self.pixel_array_dtype
191
204
  )
192
- if self.invert:
193
- self.pixel_array[:, :, :3] = 255 - self.pixel_array[:, :, :3]
205
+ if self.invert_image:
206
+ self.pixel_array[:, :, :3] = (
207
+ np.iinfo(self.pixel_array_dtype).max - self.pixel_array[:, :, :3]
208
+ )
209
+ self.orig_alpha_pixel_array = self.pixel_array[:, :, 3].copy()
194
210
  super().__init__(scale_to_resolution, **kwargs)
195
211
 
196
212
  def get_pixel_array(self):
@@ -207,7 +223,7 @@ class ImageMobject(AbstractImageMobject):
207
223
  self.color = color
208
224
  return self
209
225
 
210
- def set_opacity(self, alpha: float):
226
+ def set_opacity(self, alpha: float) -> Self:
211
227
  """Sets the image's opacity.
212
228
 
213
229
  Parameters
@@ -216,12 +232,11 @@ class ImageMobject(AbstractImageMobject):
216
232
  The alpha value of the object, 1 being opaque and 0 being
217
233
  transparent.
218
234
  """
219
- self.pixel_array[:, :, 3] = int(255 * alpha)
220
- self.fill_opacity = alpha
235
+ self.pixel_array[:, :, 3] = self.orig_alpha_pixel_array * alpha
221
236
  self.stroke_opacity = alpha
222
237
  return self
223
238
 
224
- def fade(self, darkness: float = 0.5, family: bool = True):
239
+ def fade(self, darkness: float = 0.5, family: bool = True) -> Self:
225
240
  """Sets the image's opacity using a 1 - alpha relationship.
226
241
 
227
242
  Parameters
@@ -238,7 +253,7 @@ class ImageMobject(AbstractImageMobject):
238
253
 
239
254
  def interpolate_color(
240
255
  self, mobject1: ImageMobject, mobject2: ImageMobject, alpha: float
241
- ):
256
+ ) -> None:
242
257
  """Interpolates the array of pixel color values from one ImageMobject
243
258
  into an array of equal size in the target ImageMobject.
244
259
 
@@ -274,9 +289,9 @@ class ImageMobject(AbstractImageMobject):
274
289
  alpha,
275
290
  ).astype(self.pixel_array_dtype)
276
291
 
277
- def get_style(self):
292
+ def get_style(self) -> dict[str, Any]:
278
293
  return {
279
- "fill_color": colour.rgb2hex(self.color.get_rgb()),
294
+ "fill_color": ManimColor(self.color.get_rgb()).to_hex(),
280
295
  "fill_opacity": self.fill_opacity,
281
296
  }
282
297
 
@@ -287,7 +302,12 @@ class ImageMobject(AbstractImageMobject):
287
302
 
288
303
 
289
304
  class ImageMobjectFromCamera(AbstractImageMobject):
290
- def __init__(self, camera, default_display_frame_config=None, **kwargs):
305
+ def __init__(
306
+ self,
307
+ camera: MovingCamera,
308
+ default_display_frame_config: dict[str, Any] | None = None,
309
+ **kwargs: Any,
310
+ ) -> None:
291
311
  self.camera = camera
292
312
  if default_display_frame_config is None:
293
313
  default_display_frame_config = {
@@ -304,14 +324,14 @@ class ImageMobjectFromCamera(AbstractImageMobject):
304
324
  self.pixel_array = self.camera.pixel_array
305
325
  return self.pixel_array
306
326
 
307
- def add_display_frame(self, **kwargs):
327
+ def add_display_frame(self, **kwargs: Any) -> Self:
308
328
  config = dict(self.default_display_frame_config)
309
329
  config.update(kwargs)
310
330
  self.display_frame = SurroundingRectangle(self, **config)
311
331
  self.add(self.display_frame)
312
332
  return self
313
333
 
314
- def interpolate_color(self, mobject1, mobject2, alpha):
334
+ def interpolate_color(self, mobject1, mobject2, alpha) -> None:
315
335
  assert mobject1.pixel_array.shape == mobject2.pixel_array.shape, (
316
336
  f"Mobject pixel array shapes incompatible for interpolation.\n"
317
337
  f"Mobject 1 ({mobject1}) : {mobject1.pixel_array.shape}\n"
@@ -4,8 +4,10 @@ from __future__ import annotations
4
4
 
5
5
  __all__ = ["PMobject", "Mobject1D", "Mobject2D", "PGroup", "PointCloudDot", "Point"]
6
6
 
7
+ from collections.abc import Callable
8
+ from typing import TYPE_CHECKING, Any
9
+
7
10
  import numpy as np
8
- from colour import Color
9
11
 
10
12
  from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
11
13
  from manim.mobject.opengl.opengl_point_cloud_mobject import OpenGLPMobject
@@ -17,12 +19,29 @@ from ...utils.color import (
17
19
  BLACK,
18
20
  WHITE,
19
21
  YELLOW,
22
+ ManimColor,
23
+ ParsableManimColor,
20
24
  color_gradient,
21
25
  color_to_rgba,
22
26
  rgba_to_color,
23
27
  )
24
28
  from ...utils.iterables import stretch_array_to_length
25
29
 
30
+ __all__ = ["PMobject", "Mobject1D", "Mobject2D", "PGroup", "PointCloudDot", "Point"]
31
+
32
+ if TYPE_CHECKING:
33
+ import numpy.typing as npt
34
+ from typing_extensions import Self
35
+
36
+ from manim.typing import (
37
+ FloatRGBA_Array,
38
+ FloatRGBALike_Array,
39
+ ManimFloat,
40
+ Point3D_Array,
41
+ Point3DLike,
42
+ Point3DLike_Array,
43
+ )
44
+
26
45
 
27
46
  class PMobject(Mobject, metaclass=ConvertToOpenGL):
28
47
  """A disc made of a cloud of Dots
@@ -53,19 +72,25 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
53
72
 
54
73
  """
55
74
 
56
- def __init__(self, stroke_width=DEFAULT_STROKE_WIDTH, **kwargs):
75
+ def __init__(self, stroke_width: int = DEFAULT_STROKE_WIDTH, **kwargs: Any) -> None:
57
76
  self.stroke_width = stroke_width
58
77
  super().__init__(**kwargs)
59
78
 
60
- def reset_points(self):
61
- self.rgbas = np.zeros((0, 4))
62
- self.points = np.zeros((0, 3))
79
+ def reset_points(self) -> Self:
80
+ self.rgbas: FloatRGBA_Array = np.zeros((0, 4))
81
+ self.points: Point3D_Array = np.zeros((0, 3))
63
82
  return self
64
83
 
65
- def get_array_attrs(self):
84
+ def get_array_attrs(self) -> list[str]:
66
85
  return super().get_array_attrs() + ["rgbas"]
67
86
 
68
- def add_points(self, points, rgbas=None, color=None, alpha=1):
87
+ def add_points(
88
+ self,
89
+ points: Point3DLike_Array,
90
+ rgbas: FloatRGBALike_Array | None = None,
91
+ color: ParsableManimColor | None = None,
92
+ alpha: float = 1.0,
93
+ ) -> Self:
69
94
  """Add points.
70
95
 
71
96
  Points must be a Nx3 numpy array.
@@ -76,31 +101,33 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
76
101
  num_new_points = len(points)
77
102
  self.points = np.append(self.points, points, axis=0)
78
103
  if rgbas is None:
79
- color = Color(color) if color else self.color
104
+ color = ManimColor(color) if color else self.color
80
105
  rgbas = np.repeat([color_to_rgba(color, alpha)], num_new_points, axis=0)
81
106
  elif len(rgbas) != len(points):
82
107
  raise ValueError("points and rgbas must have same length")
83
108
  self.rgbas = np.append(self.rgbas, rgbas, axis=0)
84
109
  return self
85
110
 
86
- def set_color(self, color=YELLOW, family=True):
111
+ def set_color(
112
+ self, color: ParsableManimColor = YELLOW, family: bool = True
113
+ ) -> Self:
87
114
  rgba = color_to_rgba(color)
88
115
  mobs = self.family_members_with_points() if family else [self]
89
116
  for mob in mobs:
90
117
  mob.rgbas[:, :] = rgba
91
- self.color = color
118
+ self.color = ManimColor.parse(color)
92
119
  return self
93
120
 
94
- def get_stroke_width(self):
121
+ def get_stroke_width(self) -> int:
95
122
  return self.stroke_width
96
123
 
97
- def set_stroke_width(self, width, family=True):
124
+ def set_stroke_width(self, width: int, family: bool = True) -> Self:
98
125
  mobs = self.family_members_with_points() if family else [self]
99
126
  for mob in mobs:
100
127
  mob.stroke_width = width
101
128
  return self
102
129
 
103
- def set_color_by_gradient(self, *colors):
130
+ def set_color_by_gradient(self, *colors: ParsableManimColor) -> Self:
104
131
  self.rgbas = np.array(
105
132
  list(map(color_to_rgba, color_gradient(*colors, len(self.points)))),
106
133
  )
@@ -108,11 +135,11 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
108
135
 
109
136
  def set_colors_by_radial_gradient(
110
137
  self,
111
- center=None,
112
- radius=1,
113
- inner_color=WHITE,
114
- outer_color=BLACK,
115
- ):
138
+ center: Point3DLike | None = None,
139
+ radius: float = 1,
140
+ inner_color: ParsableManimColor = WHITE,
141
+ outer_color: ParsableManimColor = BLACK,
142
+ ) -> Self:
116
143
  start_rgba, end_rgba = list(map(color_to_rgba, [inner_color, outer_color]))
117
144
  if center is None:
118
145
  center = self.get_center()
@@ -127,48 +154,48 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
127
154
  )
128
155
  return self
129
156
 
130
- def match_colors(self, mobject):
157
+ def match_colors(self, mobject: Mobject) -> Self:
131
158
  Mobject.align_data(self, mobject)
132
159
  self.rgbas = np.array(mobject.rgbas)
133
160
  return self
134
161
 
135
- def filter_out(self, condition):
162
+ def filter_out(self, condition: npt.NDArray) -> Self:
136
163
  for mob in self.family_members_with_points():
137
164
  to_eliminate = ~np.apply_along_axis(condition, 1, mob.points)
138
165
  mob.points = mob.points[to_eliminate]
139
166
  mob.rgbas = mob.rgbas[to_eliminate]
140
167
  return self
141
168
 
142
- def thin_out(self, factor=5):
143
- """
144
- Removes all but every nth point for n = factor
145
- """
169
+ def thin_out(self, factor: int = 5) -> Self:
170
+ """Removes all but every nth point for n = factor"""
146
171
  for mob in self.family_members_with_points():
147
172
  num_points = self.get_num_points()
148
173
  mob.apply_over_attr_arrays(
149
- lambda arr: arr[np.arange(0, num_points, factor)],
174
+ lambda arr, n=num_points: arr[np.arange(0, n, factor)],
150
175
  )
151
176
  return self
152
177
 
153
- def sort_points(self, function=lambda p: p[0]):
154
- """
155
- Function is any map from R^3 to R
156
- """
178
+ def sort_points(
179
+ self, function: Callable[[npt.NDArray[ManimFloat]], float] = lambda p: p[0]
180
+ ) -> Self:
181
+ """Function is any map from R^3 to R"""
157
182
  for mob in self.family_members_with_points():
158
183
  indices = np.argsort(np.apply_along_axis(function, 1, mob.points))
159
- mob.apply_over_attr_arrays(lambda arr: arr[indices])
184
+ mob.apply_over_attr_arrays(lambda arr, idx=indices: arr[idx])
160
185
  return self
161
186
 
162
- def fade_to(self, color, alpha, family=True):
187
+ def fade_to(
188
+ self, color: ParsableManimColor, alpha: float, family: bool = True
189
+ ) -> Self:
163
190
  self.rgbas = interpolate(self.rgbas, color_to_rgba(color), alpha)
164
191
  for mob in self.submobjects:
165
192
  mob.fade_to(color, alpha, family)
166
193
  return self
167
194
 
168
- def get_all_rgbas(self):
195
+ def get_all_rgbas(self) -> npt.NDArray:
169
196
  return self.get_merged_array("rgbas")
170
197
 
171
- def ingest_submobjects(self):
198
+ def ingest_submobjects(self) -> Self:
172
199
  attrs = self.get_array_attrs()
173
200
  arrays = list(map(self.get_merged_array, attrs))
174
201
  for attr, array in zip(attrs, arrays):
@@ -176,30 +203,32 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
176
203
  self.submobjects = []
177
204
  return self
178
205
 
179
- def get_color(self):
206
+ def get_color(self) -> ManimColor:
180
207
  return rgba_to_color(self.rgbas[0, :])
181
208
 
182
- def point_from_proportion(self, alpha):
209
+ def point_from_proportion(self, alpha: float) -> Any:
183
210
  index = alpha * (self.get_num_points() - 1)
184
- return self.points[index]
211
+ return self.points[np.floor(index)]
185
212
 
186
213
  @staticmethod
187
- def get_mobject_type_class():
214
+ def get_mobject_type_class() -> type[PMobject]:
188
215
  return PMobject
189
216
 
190
217
  # Alignment
191
- def align_points_with_larger(self, larger_mobject):
218
+ def align_points_with_larger(self, larger_mobject: Mobject) -> None:
192
219
  assert isinstance(larger_mobject, PMobject)
193
220
  self.apply_over_attr_arrays(
194
221
  lambda a: stretch_array_to_length(a, larger_mobject.get_num_points()),
195
222
  )
196
223
 
197
- def get_point_mobject(self, center=None):
224
+ def get_point_mobject(self, center: Point3DLike | None = None) -> Point:
198
225
  if center is None:
199
226
  center = self.get_center()
200
227
  return Point(center)
201
228
 
202
- def interpolate_color(self, mobject1, mobject2, alpha):
229
+ def interpolate_color(
230
+ self, mobject1: Mobject, mobject2: Mobject, alpha: float
231
+ ) -> Self:
203
232
  self.rgbas = interpolate(mobject1.rgbas, mobject2.rgbas, alpha)
204
233
  self.set_stroke_width(
205
234
  interpolate(
@@ -210,7 +239,7 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
210
239
  )
211
240
  return self
212
241
 
213
- def pointwise_become_partial(self, mobject, a, b):
242
+ def pointwise_become_partial(self, mobject: Mobject, a: float, b: float) -> None:
214
243
  lower_index, upper_index = (int(x * mobject.get_num_points()) for x in (a, b))
215
244
  for attr in self.get_array_attrs():
216
245
  full_array = getattr(mobject, attr)
@@ -220,31 +249,39 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
220
249
 
221
250
  # TODO, Make the two implementations below non-redundant
222
251
  class Mobject1D(PMobject, metaclass=ConvertToOpenGL):
223
- def __init__(self, density=DEFAULT_POINT_DENSITY_1D, **kwargs):
252
+ def __init__(self, density: int = DEFAULT_POINT_DENSITY_1D, **kwargs: Any) -> None:
224
253
  self.density = density
225
254
  self.epsilon = 1.0 / self.density
226
255
  super().__init__(**kwargs)
227
256
 
228
- def add_line(self, start, end, color=None):
257
+ def add_line(
258
+ self,
259
+ start: npt.NDArray,
260
+ end: npt.NDArray,
261
+ color: ParsableManimColor | None = None,
262
+ ) -> None:
229
263
  start, end = list(map(np.array, [start, end]))
230
264
  length = np.linalg.norm(end - start)
231
265
  if length == 0:
232
- points = [start]
266
+ points = np.array([start])
233
267
  else:
234
268
  epsilon = self.epsilon / length
235
- points = [interpolate(start, end, t) for t in np.arange(0, 1, epsilon)]
269
+ points = np.array(
270
+ [interpolate(start, end, t) for t in np.arange(0, 1, epsilon)]
271
+ )
236
272
  self.add_points(points, color=color)
237
273
 
238
274
 
239
275
  class Mobject2D(PMobject, metaclass=ConvertToOpenGL):
240
- def __init__(self, density=DEFAULT_POINT_DENSITY_2D, **kwargs):
276
+ def __init__(self, density: int = DEFAULT_POINT_DENSITY_2D, **kwargs: Any) -> None:
241
277
  self.density = density
242
278
  self.epsilon = 1.0 / self.density
243
279
  super().__init__(**kwargs)
244
280
 
245
281
 
246
282
  class PGroup(PMobject):
247
- """
283
+ """A group for several point mobjects.
284
+
248
285
  Examples
249
286
  --------
250
287
 
@@ -265,8 +302,8 @@ class PGroup(PMobject):
265
302
 
266
303
  """
267
304
 
268
- def __init__(self, *pmobs, **kwargs):
269
- if not all([isinstance(m, (PMobject, OpenGLPMobject)) for m in pmobs]):
305
+ def __init__(self, *pmobs: Any, **kwargs: Any) -> None:
306
+ if not all(isinstance(m, (PMobject, OpenGLPMobject)) for m in pmobs):
270
307
  raise ValueError(
271
308
  "All submobjects must be of type PMobject or OpenGLPMObject"
272
309
  " if using the opengl renderer",
@@ -274,14 +311,18 @@ class PGroup(PMobject):
274
311
  super().__init__(**kwargs)
275
312
  self.add(*pmobs)
276
313
 
277
- def fade_to(self, color, alpha, family=True):
314
+ def fade_to(
315
+ self, color: ParsableManimColor, alpha: float, family: bool = True
316
+ ) -> Self:
278
317
  if family:
279
318
  for mob in self.submobjects:
280
319
  mob.fade_to(color, alpha, family)
320
+ return self
281
321
 
282
322
 
283
323
  class PointCloudDot(Mobject1D):
284
- """A disc made of a cloud of Dots
324
+ """A disc made of a cloud of dots.
325
+
285
326
  Examples
286
327
  --------
287
328
  .. manim:: PointCloudDotExample
@@ -313,13 +354,13 @@ class PointCloudDot(Mobject1D):
313
354
 
314
355
  def __init__(
315
356
  self,
316
- center=ORIGIN,
317
- radius=2.0,
318
- stroke_width=2,
319
- density=DEFAULT_POINT_DENSITY_1D,
320
- color=YELLOW,
321
- **kwargs,
322
- ):
357
+ center: Point3DLike = ORIGIN,
358
+ radius: float = 2.0,
359
+ stroke_width: int = 2,
360
+ density: int = DEFAULT_POINT_DENSITY_1D,
361
+ color: ManimColor = YELLOW,
362
+ **kwargs: Any,
363
+ ) -> None:
323
364
  self.radius = radius
324
365
  self.epsilon = 1.0 / density
325
366
  super().__init__(
@@ -327,27 +368,29 @@ class PointCloudDot(Mobject1D):
327
368
  )
328
369
  self.shift(center)
329
370
 
330
- def init_points(self):
371
+ def init_points(self) -> None:
331
372
  self.reset_points()
332
373
  self.generate_points()
333
374
 
334
- def generate_points(self):
375
+ def generate_points(self) -> None:
335
376
  self.add_points(
336
- [
337
- r * (np.cos(theta) * RIGHT + np.sin(theta) * UP)
338
- for r in np.arange(self.epsilon, self.radius, self.epsilon)
339
- # Num is equal to int(stop - start)/ (step + 1) reformulated.
340
- for theta in np.linspace(
341
- 0,
342
- 2 * np.pi,
343
- num=int(2 * np.pi * (r + self.epsilon) / self.epsilon),
344
- )
345
- ],
377
+ np.array(
378
+ [
379
+ r * (np.cos(theta) * RIGHT + np.sin(theta) * UP)
380
+ for r in np.arange(self.epsilon, self.radius, self.epsilon)
381
+ # Num is equal to int(stop - start)/ (step + 1) reformulated.
382
+ for theta in np.linspace(
383
+ 0,
384
+ 2 * np.pi,
385
+ num=int(2 * np.pi * (r + self.epsilon) / self.epsilon),
386
+ )
387
+ ]
388
+ ),
346
389
  )
347
390
 
348
391
 
349
392
  class Point(PMobject):
350
- """
393
+ """A mobject representing a point.
351
394
 
352
395
  Examples
353
396
  --------
@@ -367,14 +410,16 @@ class Point(PMobject):
367
410
  self.add(point)
368
411
  """
369
412
 
370
- def __init__(self, location=ORIGIN, color=BLACK, **kwargs):
413
+ def __init__(
414
+ self, location: Point3DLike = ORIGIN, color: ManimColor = BLACK, **kwargs: Any
415
+ ) -> None:
371
416
  self.location = location
372
417
  super().__init__(color=color, **kwargs)
373
418
 
374
- def init_points(self):
419
+ def init_points(self) -> None:
375
420
  self.reset_points()
376
421
  self.generate_points()
377
422
  self.set_points([self.location])
378
423
 
379
- def generate_points(self):
380
- self.add_points([self.location])
424
+ def generate_points(self) -> None:
425
+ self.add_points(np.array([self.location]))