Skip to content

Commit d7f68f2

Browse files
committed
Merge remote-tracking branch 'upstream/main' into typing-logger-utils
2 parents f272e78 + 6b66878 commit d7f68f2

24 files changed

+820
-640
lines changed

manim/camera/camera.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@
1414

1515
import cairo
1616
import numpy as np
17+
import numpy.typing as npt
1718
from PIL import Image
1819
from scipy.spatial.distance import pdist
1920
from typing_extensions import Self
2021

21-
from manim.typing import MatrixMN, PixelArray, Point3D, Point3D_Array
22+
from manim.typing import (
23+
FloatRGBA_Array,
24+
FloatRGBALike_Array,
25+
ManimInt,
26+
PixelArray,
27+
Point3D,
28+
Point3D_Array,
29+
)
2230

2331
from .. import config, logger
2432
from ..constants import *
@@ -211,8 +219,8 @@ def type_or_raise(
211219
type[Mobject], Callable[[list[Mobject], PixelArray], Any]
212220
] = {
213221
VMobject: self.display_multiple_vectorized_mobjects, # type: ignore[dict-item]
214-
PMobject: self.display_multiple_point_cloud_mobjects,
215-
AbstractImageMobject: self.display_multiple_image_mobjects,
222+
PMobject: self.display_multiple_point_cloud_mobjects, # type: ignore[dict-item]
223+
AbstractImageMobject: self.display_multiple_image_mobjects, # type: ignore[dict-item]
216224
Mobject: lambda batch, pa: batch, # Do nothing
217225
}
218226
# We have to check each type in turn because we are dealing with
@@ -723,7 +731,7 @@ def set_cairo_context_path(self, ctx: cairo.Context, vmobject: VMobject) -> Self
723731
return self
724732

725733
def set_cairo_context_color(
726-
self, ctx: cairo.Context, rgbas: MatrixMN, vmobject: VMobject
734+
self, ctx: cairo.Context, rgbas: FloatRGBALike_Array, vmobject: VMobject
727735
) -> Self:
728736
"""Sets the color of the cairo context
729737
@@ -818,7 +826,7 @@ def apply_stroke(
818826

819827
def get_stroke_rgbas(
820828
self, vmobject: VMobject, background: bool = False
821-
) -> PixelArray:
829+
) -> FloatRGBA_Array:
822830
"""Gets the RGBA array for the stroke of the passed
823831
VMobject.
824832
@@ -837,7 +845,7 @@ def get_stroke_rgbas(
837845
"""
838846
return vmobject.get_stroke_rgbas(background)
839847

840-
def get_fill_rgbas(self, vmobject: VMobject) -> PixelArray:
848+
def get_fill_rgbas(self, vmobject: VMobject) -> FloatRGBA_Array:
841849
"""Returns the RGBA array of the fill of the passed VMobject
842850
843851
Parameters
@@ -898,7 +906,7 @@ def display_multiple_background_colored_vmobjects(
898906
# As a result, the other methods do not have as detailed docstrings as would be preferred.
899907

900908
def display_multiple_point_cloud_mobjects(
901-
self, pmobjects: list, pixel_array: PixelArray
909+
self, pmobjects: Iterable[PMobject], pixel_array: PixelArray
902910
) -> None:
903911
"""Displays multiple PMobjects by modifying the passed pixel array.
904912
@@ -921,8 +929,8 @@ def display_multiple_point_cloud_mobjects(
921929
def display_point_cloud(
922930
self,
923931
pmobject: PMobject,
924-
points: list,
925-
rgbas: np.ndarray,
932+
points: Point3D_Array,
933+
rgbas: FloatRGBA_Array,
926934
thickness: float,
927935
pixel_array: PixelArray,
928936
) -> None:
@@ -972,7 +980,9 @@ def display_point_cloud(
972980
pixel_array[:, :] = new_pa.reshape((ph, pw, rgba_len))
973981

974982
def display_multiple_image_mobjects(
975-
self, image_mobjects: list, pixel_array: np.ndarray
983+
self,
984+
image_mobjects: Iterable[AbstractImageMobject],
985+
pixel_array: PixelArray,
976986
) -> None:
977987
"""Displays multiple image mobjects by modifying the passed pixel_array.
978988
@@ -1121,8 +1131,8 @@ def transform_points_pre_display(
11211131
def points_to_pixel_coords(
11221132
self,
11231133
mobject: Mobject,
1124-
points: np.ndarray,
1125-
) -> np.ndarray: # TODO: Write more detailed docstrings for this method.
1134+
points: Point3D_Array,
1135+
) -> npt.NDArray[ManimInt]: # TODO: Write more detailed docstrings for this method.
11261136
points = self.transform_points_pre_display(mobject, points)
11271137
shifted_points = points - self.frame_center
11281138

manim/camera/moving_camera.py

Lines changed: 79 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
from collections.abc import Iterable
1313
from typing import Any
1414

15-
import numpy as np
15+
from cairo import Context
16+
17+
from manim.typing import PixelArray, Point3D, Point3DLike
1618

1719
from .. import config
1820
from ..camera.camera import Camera
@@ -34,12 +36,12 @@ class MovingCamera(Camera):
3436

3537
def __init__(
3638
self,
37-
frame=None,
39+
frame: Mobject | None = None,
3840
fixed_dimension: int = 0, # width
3941
default_frame_stroke_color: ManimColor = WHITE,
4042
default_frame_stroke_width: int = 0,
4143
**kwargs: Any,
42-
) -> None:
44+
):
4345
"""Frame is a Mobject, (should almost certainly be a rectangle)
4446
determining which region of space the camera displays
4547
"""
@@ -57,7 +59,7 @@ def __init__(
5759

5860
# TODO, make these work for a rotated frame
5961
@property
60-
def frame_height(self):
62+
def frame_height(self) -> float:
6163
"""Returns the height of the frame.
6264
6365
Returns
@@ -67,30 +69,8 @@ def frame_height(self):
6769
"""
6870
return self.frame.height
6971

70-
@property
71-
def frame_width(self):
72-
"""Returns the width of the frame
73-
74-
Returns
75-
-------
76-
float
77-
The width of the frame.
78-
"""
79-
return self.frame.width
80-
81-
@property
82-
def frame_center(self):
83-
"""Returns the centerpoint of the frame in cartesian coordinates.
84-
85-
Returns
86-
-------
87-
np.array
88-
The cartesian coordinates of the center of the frame.
89-
"""
90-
return self.frame.get_center()
91-
9272
@frame_height.setter
93-
def frame_height(self, frame_height: float):
73+
def frame_height(self, frame_height: float) -> None:
9474
"""Sets the height of the frame in MUnits.
9575
9676
Parameters
@@ -100,8 +80,19 @@ def frame_height(self, frame_height: float):
10080
"""
10181
self.frame.stretch_to_fit_height(frame_height)
10282

83+
@property
84+
def frame_width(self) -> float:
85+
"""Returns the width of the frame
86+
87+
Returns
88+
-------
89+
float
90+
The width of the frame.
91+
"""
92+
return self.frame.width
93+
10394
@frame_width.setter
104-
def frame_width(self, frame_width: float):
95+
def frame_width(self, frame_width: float) -> None:
10596
"""Sets the width of the frame in MUnits.
10697
10798
Parameters
@@ -111,8 +102,19 @@ def frame_width(self, frame_width: float):
111102
"""
112103
self.frame.stretch_to_fit_width(frame_width)
113104

105+
@property
106+
def frame_center(self) -> Point3D:
107+
"""Returns the centerpoint of the frame in cartesian coordinates.
108+
109+
Returns
110+
-------
111+
np.array
112+
The cartesian coordinates of the center of the frame.
113+
"""
114+
return self.frame.get_center()
115+
114116
@frame_center.setter
115-
def frame_center(self, frame_center: np.ndarray | list | tuple | Mobject):
117+
def frame_center(self, frame_center: Point3DLike | Mobject) -> None:
116118
"""Sets the centerpoint of the frame.
117119
118120
Parameters
@@ -129,17 +131,14 @@ def capture_mobjects(self, mobjects: Iterable[Mobject], **kwargs: Any) -> None:
129131
# self.realign_frame_shape()
130132
super().capture_mobjects(mobjects, **kwargs)
131133

132-
# Since the frame can be moving around, the cairo
133-
# context used for updating should be regenerated
134-
# at each frame. So no caching.
135-
def get_cached_cairo_context(self, pixel_array):
134+
def get_cached_cairo_context(self, pixel_array: PixelArray) -> None:
136135
"""Since the frame can be moving around, the cairo
137136
context used for updating should be regenerated
138137
at each frame. So no caching.
139138
"""
140139
return None
141140

142-
def cache_cairo_context(self, pixel_array, ctx):
141+
def cache_cairo_context(self, pixel_array: PixelArray, ctx: Context) -> None:
143142
"""Since the frame can be moving around, the cairo
144143
context used for updating should be regenerated
145144
at each frame. So no caching.
@@ -157,23 +156,23 @@ def cache_cairo_context(self, pixel_array, ctx):
157156
# self.frame_shape = (self.frame.height, width)
158157
# self.resize_frame_shape(fixed_dimension=self.fixed_dimension)
159158

160-
def get_mobjects_indicating_movement(self):
159+
def get_mobjects_indicating_movement(self) -> list[Mobject]:
161160
"""Returns all mobjects whose movement implies that the camera
162161
should think of all other mobjects on the screen as moving
163162
164163
Returns
165164
-------
166-
list
165+
list[Mobject]
167166
"""
168167
return [self.frame]
169168

170169
def auto_zoom(
171170
self,
172-
mobjects: list[Mobject],
171+
mobjects: Iterable[Mobject],
173172
margin: float = 0,
174173
only_mobjects_in_frame: bool = False,
175174
animate: bool = True,
176-
):
175+
) -> Mobject:
177176
"""Zooms on to a given array of mobjects (or a singular mobject)
178177
and automatically resizes to frame all the mobjects.
179178
@@ -203,10 +202,36 @@ def auto_zoom(
203202
or ScreenRectangle with position and size updated to zoomed position.
204203
205204
"""
206-
scene_critical_x_left = None
207-
scene_critical_x_right = None
208-
scene_critical_y_up = None
209-
scene_critical_y_down = None
205+
(
206+
scene_critical_x_left,
207+
scene_critical_x_right,
208+
scene_critical_y_up,
209+
scene_critical_y_down,
210+
) = self._get_bounding_box(mobjects, only_mobjects_in_frame)
211+
212+
# calculate center x and y
213+
x = (scene_critical_x_left + scene_critical_x_right) / 2
214+
y = (scene_critical_y_up + scene_critical_y_down) / 2
215+
216+
# calculate proposed width and height of zoomed scene
217+
new_width = abs(scene_critical_x_left - scene_critical_x_right)
218+
new_height = abs(scene_critical_y_up - scene_critical_y_down)
219+
220+
m_target = self.frame.animate if animate else self.frame
221+
# zoom to fit all mobjects along the side that has the largest size
222+
if new_width / self.frame.width > new_height / self.frame.height:
223+
return m_target.set_x(x).set_y(y).set(width=new_width + margin)
224+
else:
225+
return m_target.set_x(x).set_y(y).set(height=new_height + margin)
226+
227+
def _get_bounding_box(
228+
self, mobjects: Iterable[Mobject], only_mobjects_in_frame: bool
229+
) -> tuple[float, float, float, float]:
230+
bounding_box_located = False
231+
scene_critical_x_left: float = 0
232+
scene_critical_x_right: float = 1
233+
scene_critical_y_up: float = 1
234+
scene_critical_y_down: float = 0
210235

211236
for m in mobjects:
212237
if (m == self.frame) or (
@@ -216,11 +241,12 @@ def auto_zoom(
216241
continue
217242

218243
# initialize scene critical points with first mobjects critical points
219-
if scene_critical_x_left is None:
244+
if not bounding_box_located:
220245
scene_critical_x_left = m.get_critical_point(LEFT)[0]
221246
scene_critical_x_right = m.get_critical_point(RIGHT)[0]
222247
scene_critical_y_up = m.get_critical_point(UP)[1]
223248
scene_critical_y_down = m.get_critical_point(DOWN)[1]
249+
bounding_box_located = True
224250

225251
else:
226252
if m.get_critical_point(LEFT)[0] < scene_critical_x_left:
@@ -235,17 +261,14 @@ def auto_zoom(
235261
if m.get_critical_point(DOWN)[1] < scene_critical_y_down:
236262
scene_critical_y_down = m.get_critical_point(DOWN)[1]
237263

238-
# calculate center x and y
239-
x = (scene_critical_x_left + scene_critical_x_right) / 2
240-
y = (scene_critical_y_up + scene_critical_y_down) / 2
241-
242-
# calculate proposed width and height of zoomed scene
243-
new_width = abs(scene_critical_x_left - scene_critical_x_right)
244-
new_height = abs(scene_critical_y_up - scene_critical_y_down)
264+
if not bounding_box_located:
265+
raise Exception(
266+
"Could not determine bounding box of the mobjects given to 'auto_zoom'."
267+
)
245268

246-
m_target = self.frame.animate if animate else self.frame
247-
# zoom to fit all mobjects along the side that has the largest size
248-
if new_width / self.frame.width > new_height / self.frame.height:
249-
return m_target.set_x(x).set_y(y).set(width=new_width + margin)
250-
else:
251-
return m_target.set_x(x).set_y(y).set(height=new_height + margin)
269+
return (
270+
scene_critical_x_left,
271+
scene_critical_x_right,
272+
scene_critical_y_up,
273+
scene_critical_y_down,
274+
)

manim/camera/three_d_camera.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from manim.mobject.types.vectorized_mobject import VMobject
2121
from manim.mobject.value_tracker import ValueTracker
2222
from manim.typing import (
23+
FloatRGBA_Array,
2324
MatrixMN,
2425
Point3D,
2526
Point3D_Array,
@@ -109,7 +110,9 @@ def get_value_trackers(self) -> list[ValueTracker]:
109110
self.zoom_tracker,
110111
]
111112

112-
def modified_rgbas(self, vmobject: VMobject, rgbas: MatrixMN) -> MatrixMN:
113+
def modified_rgbas(
114+
self, vmobject: VMobject, rgbas: FloatRGBA_Array
115+
) -> FloatRGBA_Array:
113116
if not self.should_apply_shading:
114117
return rgbas
115118
if vmobject.shade_in_3d and (vmobject.get_num_points() > 0):
@@ -137,12 +140,12 @@ def get_stroke_rgbas(
137140
self,
138141
vmobject: VMobject,
139142
background: bool = False,
140-
) -> MatrixMN: # NOTE : DocStrings From parent
143+
) -> FloatRGBA_Array: # NOTE : DocStrings From parent
141144
return self.modified_rgbas(vmobject, vmobject.get_stroke_rgbas(background))
142145

143146
def get_fill_rgbas(
144147
self, vmobject: VMobject
145-
) -> MatrixMN: # NOTE : DocStrings From parent
148+
) -> FloatRGBA_Array: # NOTE : DocStrings From parent
146149
return self.modified_rgbas(vmobject, vmobject.get_fill_rgbas())
147150

148151
def get_mobjects_to_display(

manim/mobject/graphing/probability.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,6 @@ def get_division_along_dimension(
105105
p_list_complete = self.complete_p_list(p_list)
106106
colors_in_gradient = color_gradient(colors, len(p_list_complete))
107107

108-
assert isinstance(colors_in_gradient, list)
109-
110108
last_point = self.get_edge_center(-vect)
111109
parts = VGroup()
112110
for factor, color in zip(p_list_complete, colors_in_gradient):

manim/mobject/mobject.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2357,7 +2357,7 @@ def __getitem__(self, value):
23572357
def __iter__(self):
23582358
return iter(self.split())
23592359

2360-
def __len__(self):
2360+
def __len__(self) -> int:
23612361
return len(self.split())
23622362

23632363
def get_group_class(self) -> type[Group]:

0 commit comments

Comments
 (0)