Skip to content

Fix type errors, enable type checking for mobject/geometry and add typings for the transformation functions #4228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion manim/mobject/geometry/arc.py
Original file line number Diff line number Diff line change
@@ -183,7 +183,7 @@ def position_tip(self, tip: tips.ArrowTip, at_start: bool = False) -> tips.Arrow
else:
handle = self.get_last_handle()
anchor = self.get_end()
angles = cartesian_to_spherical((handle - anchor).tolist())
angles = cartesian_to_spherical(handle - anchor)
tip.rotate(
angles[1] - PI - tip.tip_angle,
) # Rotates the tip along the azimuthal
8 changes: 4 additions & 4 deletions manim/mobject/geometry/boolean_ops.py
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ def _convert_2d_to_3d_array(
list_of_points = list(points)
for i, point in enumerate(list_of_points):
if len(point) == 2:
list_of_points[i] = np.array(list(point) + [z_dim])
list_of_points[i] = np.append(point, z_dim)
return np.asarray(list_of_points)

def _convert_vmobject_to_skia_path(self, vmobject: VMobject) -> SkiaPath:
@@ -78,10 +78,10 @@ def _convert_vmobject_to_skia_path(self, vmobject: VMobject) -> SkiaPath:
"""
path = SkiaPath()

if not np.all(np.isfinite(vmobject.points)):
points = np.zeros((1, 3)) # point invalid?
else:
if np.all(np.isfinite(vmobject.points)):
points = vmobject.points
else:
points = np.zeros((1, 3)) # point invalid?

if len(points) == 0: # what? No points so return empty path
return path
2 changes: 1 addition & 1 deletion manim/mobject/graphing/scale.py
Original file line number Diff line number Diff line change
@@ -163,7 +163,7 @@ def get_custom_labels(
self,
val_range: Iterable[float],
unit_decimal_places: int = 0,
**base_config: dict[str, Any],
**base_config: Any,
) -> list[Mobject]:
"""Produces custom :class:`~.Integer` labels in the form of ``10^2``.

86 changes: 65 additions & 21 deletions manim/mobject/mobject.py
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@
PathFuncType,
PixelArray,
Point3D,
Point3D_Array,
Point3DLike,
Point3DLike_Array,
Vector3D,
@@ -1225,7 +1226,13 @@

return self

def scale(self, scale_factor: float, **kwargs) -> Self:
def scale(
self,
scale_factor: float,
*,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = None,
) -> Self:
Comment on lines +1229 to +1235

Check notice

Code scanning / CodeQL

Mismatch between signature and use of an overridden method Note

Overridden method signature does not match
call
, where it is passed too many arguments. Overriding method
method VMobject.scale
matches the call.
Overridden method signature does not match
call
, where it is passed too many arguments. Overriding method
method VMobject.scale
matches the call.
Overridden method signature does not match
call
, where it is passed too many arguments. Overriding method
method VMobject.scale
matches the call.
Overridden method signature does not match
call
, where it is passed too many arguments. Overriding method
method VMobject.scale
matches the call.
Overridden method signature does not match
call
, where it is passed too many arguments. Overriding method
method VMobject.scale
matches the call.
r"""Scale the size by a factor.

Default behavior is to scale about the center of the mobject.
@@ -1236,9 +1243,10 @@
The scaling factor :math:`\alpha`. If :math:`0 < |\alpha| < 1`, the mobject
will shrink, and for :math:`|\alpha| > 1` it will grow. Furthermore,
if :math:`\alpha < 0`, the mobject is also flipped.
kwargs
Additional keyword arguments passed to
:meth:`apply_points_function_about_point`.
about_point
The point about which to apply the scaling.
about_edge
The edge about which to apply the scaling.

Returns
-------
@@ -1267,7 +1275,7 @@

"""
self.apply_points_function_about_point(
lambda points: scale_factor * points, **kwargs
lambda points: scale_factor * points, about_point, about_edge
)
return self

@@ -1280,16 +1288,23 @@
angle: float,
axis: Vector3D = OUT,
about_point: Point3DLike | None = None,
**kwargs,
*,
about_edge: Vector3D | None = None,
) -> Self:
"""Rotates the :class:`~.Mobject` about a certain point."""
rot_matrix = rotation_matrix(angle, axis)
self.apply_points_function_about_point(
lambda points: np.dot(points, rot_matrix.T), about_point, **kwargs
lambda points: np.dot(points, rot_matrix.T), about_point, about_edge
)
return self

def flip(self, axis: Vector3D = UP, **kwargs) -> Self:
def flip(
self,
axis: Vector3D = UP,
*,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = None,
) -> Self:
"""Flips/Mirrors an mobject about its center.

Examples
@@ -1306,26 +1321,43 @@
self.add(s2)

"""
return self.rotate(TAU / 2, axis, **kwargs)
return self.rotate(
TAU / 2, axis, about_point=about_point, about_edge=about_edge
)

def stretch(self, factor: float, dim: int, **kwargs) -> Self:
def stretch(
self,
factor: float,
dim: int,
*,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = None,
) -> Self:
def func(points: Point3D_Array) -> Point3D_Array:
points[:, dim] *= factor
return points

self.apply_points_function_about_point(func, **kwargs)
self.apply_points_function_about_point(func, about_point, about_edge)
return self

def apply_function(self, function: MappingFunction, **kwargs) -> Self:
def apply_function(
self,
function: MappingFunction,
*,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = None,
) -> Self:
# Default to applying matrix about the origin, not mobjects center
if len(kwargs) == 0:
kwargs["about_point"] = ORIGIN
if about_point is None and about_edge is None:
about_point = ORIGIN

def multi_mapping_function(points: Point3D_Array) -> Point3D_Array:
result: Point3D_Array = np.apply_along_axis(function, 1, points)
return result

self.apply_points_function_about_point(multi_mapping_function, **kwargs)
self.apply_points_function_about_point(
multi_mapping_function, about_point, about_edge
)
return self

def apply_function_to_position(self, function: MappingFunction) -> Self:
@@ -1337,20 +1369,30 @@
submob.apply_function_to_position(function)
return self

def apply_matrix(self, matrix, **kwargs) -> Self:
def apply_matrix(
self,
matrix,
*,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = None,
) -> Self:
# Default to applying matrix about the origin, not mobjects center
if ("about_point" not in kwargs) and ("about_edge" not in kwargs):
kwargs["about_point"] = ORIGIN
if about_point is None and about_edge is None:
about_point = ORIGIN
full_matrix = np.identity(self.dim)
matrix = np.array(matrix)
full_matrix[: matrix.shape[0], : matrix.shape[1]] = matrix
self.apply_points_function_about_point(
lambda points: np.dot(points, full_matrix.T), **kwargs
lambda points: np.dot(points, full_matrix.T), about_point, about_edge
)
return self

def apply_complex_function(
self, function: Callable[[complex], complex], **kwargs
self,
function: Callable[[complex], complex],
*,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = None,
) -> Self:
"""Applies a complex function to a :class:`Mobject`.
The x and y Point3Ds correspond to the real and imaginary parts respectively.
@@ -1383,7 +1425,9 @@
xy_complex = function(complex(x, y))
return [xy_complex.real, xy_complex.imag, z]

return self.apply_function(R3_func)
return self.apply_function(
R3_func, about_point=about_point, about_edge=about_edge
)

def reverse_points(self) -> Self:
for mob in self.family_members_with_points():
24 changes: 19 additions & 5 deletions manim/mobject/types/vectorized_mobject.py
Original file line number Diff line number Diff line change
@@ -472,7 +472,14 @@ def set_opacity(self, opacity: float, family: bool = True) -> Self:
self.set_stroke(opacity=opacity, family=family, background=True)
return self

def scale(self, scale_factor: float, scale_stroke: bool = False, **kwargs) -> Self:
def scale(
self,
scale_factor: float,
scale_stroke: bool = False,
*,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = None,
) -> Self:
r"""Scale the size by a factor.

Default behavior is to scale about the center of the vmobject.
@@ -527,7 +534,7 @@ def construct(self):
width=abs(scale_factor) * self.get_stroke_width(background=True),
background=True,
)
super().scale(scale_factor, **kwargs)
super().scale(scale_factor, about_point=about_point, about_edge=about_edge)
return self

def fade(self, darkness: float = 0.5, family: bool = True) -> Self:
@@ -1178,7 +1185,13 @@ def append_vectorized_mobject(self, vectorized_mobject: VMobject) -> None:
self.points = self.points[:-1]
self.append_points(vectorized_mobject.points)

def apply_function(self, function: MappingFunction) -> Self:
def apply_function(
self,
function: MappingFunction,
*,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = None,
) -> Self:
factor = self.pre_function_handle_to_anchor_scale_factor
self.scale_handle_to_anchor_distances(factor)
super().apply_function(function)
@@ -1192,10 +1205,11 @@ def rotate(
angle: float,
axis: Vector3D = OUT,
about_point: Point3DLike | None = None,
**kwargs,
*,
about_edge: Vector3D | None = None,
) -> Self:
self.rotate_sheen_direction(angle, axis)
super().rotate(angle, axis, about_point, **kwargs)
super().rotate(angle, axis, about_point, about_edge=about_edge)
return self

def scale_handle_to_anchor_distances(self, factor: float) -> Self:
10 changes: 5 additions & 5 deletions manim/utils/bezier.py
Original file line number Diff line number Diff line change
@@ -915,10 +915,10 @@ def subdivide_bezier(points: BezierPointsLike, n_divisions: int) -> Spline:
:class:`~.Spline`
An array containing the points defining the new :math:`n` subcurves.
"""
points = np.asarray(points)
if n_divisions == 1:
return points

points = np.asarray(points)
N, dim = points.shape

if N <= 4:
@@ -1754,10 +1754,10 @@ def get_quadratic_approximation_of_cubic(


def get_quadratic_approximation_of_cubic(
a0: Point3D | Point3D_Array,
h0: Point3D | Point3D_Array,
h1: Point3D | Point3D_Array,
a1: Point3D | Point3D_Array,
a0: Point3DLike | Point3DLike_Array,
h0: Point3DLike | Point3DLike_Array,
h1: Point3DLike | Point3DLike_Array,
a1: Point3DLike | Point3DLike_Array,
) -> QuadraticSpline | QuadraticBezierPath:
r"""If ``a0``, ``h0``, ``h1`` and ``a1`` are the control points of a cubic
Bézier curve, approximate the curve with two quadratic Bézier curves and
6 changes: 3 additions & 3 deletions manim/utils/images.py
Original file line number Diff line number Diff line change
@@ -40,16 +40,16 @@ def get_full_vector_image_path(image_file_name: str | PurePath) -> Path:
)


def drag_pixels(frames: list[np.array]) -> list[np.array]:
def drag_pixels(frames: list[np.ndarray]) -> list[np.ndarray]:
curr = frames[0]
new_frames = []
new_frames: list[np.ndarray] = []
for frame in frames:
curr += (curr == 0) * np.array(frame)
new_frames.append(np.array(curr))
return new_frames


def invert_image(image: np.array) -> Image:
def invert_image(image: np.ndarray) -> Image.Image:
arr = np.array(image)
arr = (255 * np.ones(arr.shape)).astype(arr.dtype) - arr
return Image.fromarray(arr)
6 changes: 4 additions & 2 deletions manim/utils/space_ops.py
Original file line number Diff line number Diff line change
@@ -802,14 +802,16 @@ def earclip_triangulation(verts: np.ndarray, ring_ends: list) -> list:
return [indices[mi] for mi in meta_indices]


def cartesian_to_spherical(vec: Sequence[float]) -> np.ndarray:
def cartesian_to_spherical(
vec: np.ndarray | Sequence[float],
) -> np.ndarray | Sequence[float]:
"""Returns an array of numbers corresponding to each
polar coordinate value (distance, phi, theta).

Parameters
----------
vec
A numpy array ``[x, y, z]``.
A numpy array or a sequence of floats ``[x, y, z]``.
"""
norm = np.linalg.norm(vec)
if norm == 0:
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ ignore_errors = True
ignore_errors = False

[mypy-manim.mobject.geometry.*]
ignore_errors = True
ignore_errors = False

[mypy-manim.renderer.*]
ignore_errors = True