From b61ab94bb118c7f52e1e99731aadfd6a15d4baba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Thu, 28 Nov 2024 17:28:16 -0300 Subject: [PATCH 01/15] Create Point3DLike and similar type aliases --- manim/mobject/geometry/arc.py | 65 +++-- manim/mobject/geometry/boolean_ops.py | 11 +- manim/mobject/geometry/labeled.py | 10 +- manim/mobject/geometry/line.py | 52 ++-- manim/mobject/geometry/polygram.py | 18 +- manim/mobject/geometry/tips.py | 7 +- manim/mobject/graph.py | 37 +-- manim/mobject/graphing/coordinate_systems.py | 16 +- manim/mobject/graphing/functions.py | 8 +- manim/mobject/graphing/number_line.py | 4 +- manim/mobject/mobject.py | 45 +-- manim/mobject/opengl/opengl_mobject.py | 30 +- manim/mobject/svg/brace.py | 6 +- manim/mobject/three_d/three_dimensions.py | 6 +- manim/mobject/types/point_cloud_mobject.py | 6 +- manim/mobject/types/vectorized_mobject.py | 99 +++---- manim/typing.py | 284 ++++++++++++++++--- manim/utils/bezier.py | 81 +++--- manim/utils/space_ops.py | 12 +- 19 files changed, 516 insertions(+), 281 deletions(-) diff --git a/manim/mobject/geometry/arc.py b/manim/mobject/geometry/arc.py index c211deae01..0009b723d1 100644 --- a/manim/mobject/geometry/arc.py +++ b/manim/mobject/geometry/arc.py @@ -71,10 +71,9 @@ def construct(self): from manim.mobject.text.tex_mobject import SingleStringMathTex, Tex from manim.mobject.text.text_mobject import Text from manim.typing import ( - CubicBezierPoints, - InternalPoint3D, Point3D, - QuadraticBezierPoints, + Point3DLike, + QuadraticSpline, Vector3D, ) @@ -269,22 +268,27 @@ def get_tip(self) -> VMobject: def get_default_tip_length(self) -> float: return self.tip_length - def get_first_handle(self) -> InternalPoint3D: + def get_first_handle(self) -> Point3D: # Type inference of extracting an element from a list, is not # supported by numpy, see this numpy issue # https://github.com/numpy/numpy/issues/16544 - return self.points[1] + first_handle: Point3D = self.points[1] + return first_handle - def get_last_handle(self) -> InternalPoint3D: - return self.points[-2] + def get_last_handle(self) -> Point3D: + # Type inference of extracting an element from a list, is not + # supported by numpy, see this numpy issue + # https://github.com/numpy/numpy/issues/16544 + last_handle: Point3D = self.points[-2] + return last_handle - def get_end(self) -> InternalPoint3D: + def get_end(self) -> Point3D: if self.has_tip(): return self.tip.get_start() else: return super().get_end() - def get_start(self) -> InternalPoint3D: + def get_start(self) -> Point3D: if self.has_start_tip(): return self.start_tip.get_start() else: @@ -316,7 +320,7 @@ def __init__( start_angle: float = 0, angle: float = TAU / 4, num_components: int = 9, - arc_center: InternalPoint3D = ORIGIN, + arc_center: Point3D = ORIGIN, **kwargs: Any, ): if radius is None: # apparently None is passed by ArcBetweenPoints @@ -351,7 +355,7 @@ def init_points(self) -> None: @staticmethod def _create_quadratic_bezier_points( angle: float, start_angle: float = 0, n_components: int = 8 - ) -> QuadraticBezierPoints: + ) -> QuadraticSpline: samples = np.array( [ [np.cos(a), np.sin(a), 0] @@ -394,7 +398,7 @@ def _set_pre_positioned_points(self) -> None: handles2 = anchors[1:] - (d_theta / 3) * tangent_vectors[1:] self.set_anchors_and_handles(anchors[:-1], handles1, handles2, anchors[1:]) - def get_arc_center(self, warning: bool = True) -> InternalPoint3D: + def get_arc_center(self, warning: bool = True) -> Point3D: """Looks at the normals to the first two anchors, and finds their intersection points """ @@ -422,7 +426,7 @@ def get_arc_center(self, warning: bool = True) -> InternalPoint3D: self._failed_to_get_center = True return np.array(ORIGIN) - def move_arc_center_to(self, point: InternalPoint3D) -> Self: + def move_arc_center_to(self, point: Point3D) -> Self: self.shift(point - self.get_arc_center()) return self @@ -454,8 +458,8 @@ def construct(self): def __init__( self, - start: Point3D, - end: Point3D, + start: Point3DLike, + end: Point3DLike, angle: float = TAU / 4, radius: float | None = None, **kwargs: Any, @@ -484,14 +488,18 @@ def __init__( if radius is None: center = self.get_arc_center(warning=False) if not self._failed_to_get_center: - temp_radius: float = np.linalg.norm(np.array(start) - np.array(center)) - self.radius = temp_radius + # np.linalg.norm returns floating[Any] which is not compatible with float + self.radius = cast( + float, np.linalg.norm(np.array(start) - np.array(center)) + ) else: self.radius = np.inf class CurvedArrow(ArcBetweenPoints): - def __init__(self, start_point: Point3D, end_point: Point3D, **kwargs: Any) -> None: + def __init__( + self, start_point: Point3DLike, end_point: Point3DLike, **kwargs: Any + ) -> None: from manim.mobject.geometry.tips import ArrowTriangleFilledTip tip_shape = kwargs.pop("tip_shape", ArrowTriangleFilledTip) @@ -500,7 +508,9 @@ def __init__(self, start_point: Point3D, end_point: Point3D, **kwargs: Any) -> N class CurvedDoubleArrow(CurvedArrow): - def __init__(self, start_point: Point3D, end_point: Point3D, **kwargs: Any) -> None: + def __init__( + self, start_point: Point3DLike, end_point: Point3DLike, **kwargs: Any + ) -> None: if "tip_shape_end" in kwargs: kwargs["tip_shape"] = kwargs.pop("tip_shape_end") from manim.mobject.geometry.tips import ArrowTriangleFilledTip @@ -637,7 +647,7 @@ def construct(self): @staticmethod def from_three_points( - p1: Point3D, p2: Point3D, p3: Point3D, **kwargs: Any + p1: Point3DLike, p2: Point3DLike, p3: Point3DLike, **kwargs: Any ) -> Circle: """Returns a circle passing through the specified three points. @@ -661,7 +671,8 @@ def construct(self): perpendicular_bisector([np.asarray(p1), np.asarray(p2)]), perpendicular_bisector([np.asarray(p2), np.asarray(p3)]), ) - radius: float = np.linalg.norm(p1 - center) + # np.linalg.norm returns floating[Any] which is not compatible with float + radius = cast(float, np.linalg.norm(p1 - center)) return Circle(radius=radius, **kwargs).shift(center) @@ -698,7 +709,7 @@ def construct(self): def __init__( self, - point: Point3D = ORIGIN, + point: Point3DLike = ORIGIN, radius: float = DEFAULT_DOT_RADIUS, stroke_width: float = 0, fill_opacity: float = 1.0, @@ -1006,10 +1017,10 @@ def construct(self): def __init__( self, - start_anchor: CubicBezierPoints, - start_handle: CubicBezierPoints, - end_handle: CubicBezierPoints, - end_anchor: CubicBezierPoints, + start_anchor: Point3DLike, + start_handle: Point3DLike, + end_handle: Point3DLike, + end_anchor: Point3DLike, **kwargs: Any, ) -> None: super().__init__(**kwargs) @@ -1097,7 +1108,7 @@ def construct(self): def __init__( self, - *vertices: Point3D, + *vertices: Point3DLike, angle: float = PI / 4, radius: float | None = None, arc_config: list[dict] | None = None, diff --git a/manim/mobject/geometry/boolean_ops.py b/manim/mobject/geometry/boolean_ops.py index a34d6fc7c4..6b206bfc57 100644 --- a/manim/mobject/geometry/boolean_ops.py +++ b/manim/mobject/geometry/boolean_ops.py @@ -13,9 +13,10 @@ from manim.mobject.types.vectorized_mobject import VMobject if TYPE_CHECKING: + from collections.abc import Sequence from typing import Any - from manim.typing import InternalPoint3D_Array, Point2D_Array + from manim.typing import Point2DLike, Point3D_Array, Point3DLike from ...constants import RendererType @@ -30,17 +31,17 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL): def _convert_2d_to_3d_array( self, - points: Point2D_Array, + points: Sequence[Point2DLike | Point3DLike], z_dim: float = 0.0, - ) -> InternalPoint3D_Array: + ) -> Point3D_Array: """Converts an iterable with coordinates in 2D to 3D by adding :attr:`z_dim` as the Z coordinate. Parameters ---------- - points: + points An iterable of points. - z_dim: + z_dim Default value for the Z coordinate. Returns diff --git a/manim/mobject/geometry/labeled.py b/manim/mobject/geometry/labeled.py index 3c6ef335e2..5c9734ed8c 100644 --- a/manim/mobject/geometry/labeled.py +++ b/manim/mobject/geometry/labeled.py @@ -24,7 +24,7 @@ if TYPE_CHECKING: from typing import Any - from manim.typing import Point3D_Array + from manim.typing import Point3DLike_Array class Label(VGroup): @@ -344,7 +344,7 @@ def construct(self): def __init__( self, - *vertex_groups: Point3D_Array, + *vertex_groups: Point3DLike_Array, label: str | Tex | MathTex | Text, precision: float = 0.01, label_config: dict[str, Any] | None = None, @@ -365,7 +365,11 @@ def __init__( # Close Vertex Groups rings = [ - group if np.array_equal(group[0], group[-1]) else list(group) + [group[0]] + np.asarray( + group + if np.array_equal(group[0], group[-1]) + else list(group) + [group[0]] + ) for group in vertex_groups ] diff --git a/manim/mobject/geometry/line.py b/manim/mobject/geometry/line.py index ef0e4aa1ff..d3f5576ebd 100644 --- a/manim/mobject/geometry/line.py +++ b/manim/mobject/geometry/line.py @@ -32,9 +32,9 @@ if TYPE_CHECKING: from typing import Any - from typing_extensions import Self + from typing_extensions import Literal, Self - from manim.typing import InternalPoint3D, Point2D, Point3D, Vector3D + from manim.typing import Point3D, Point3DLike, Vector2D, Vector3D from manim.utils.color import ParsableManimColor from ..matrix import Matrix # Avoid circular import @@ -43,8 +43,8 @@ class Line(TipableVMobject): def __init__( self, - start: Point3D | Mobject = LEFT, - end: Point3D | Mobject = RIGHT, + start: Point3DLike | Mobject = LEFT, + end: Point3DLike | Mobject = RIGHT, buff: float = 0, path_arc: float | None = None, **kwargs: Any, @@ -66,8 +66,8 @@ def generate_points(self) -> None: def set_points_by_ends( self, - start: Point3D | Mobject, - end: Point3D | Mobject, + start: Point3DLike | Mobject, + end: Point3DLike | Mobject, buff: float = 0, path_arc: float = 0, ) -> None: @@ -113,7 +113,7 @@ def _account_for_buff(self, buff: float) -> None: return def _set_start_and_end_attrs( - self, start: Point3D | Mobject, end: Point3D | Mobject + self, start: Point3DLike | Mobject, end: Point3DLike | Mobject ) -> None: # If either start or end are Mobjects, this # gives their centers @@ -128,9 +128,9 @@ def _set_start_and_end_attrs( def _pointify( self, - mob_or_point: Mobject | Point3D, + mob_or_point: Mobject | Point3DLike, direction: Vector3D | None = None, - ) -> InternalPoint3D: + ) -> Point3D: """Transforms a mobject into its corresponding point. Does nothing if a point is passed. ``direction`` determines the location of the point along its bounding box in that direction. @@ -156,8 +156,8 @@ def set_path_arc(self, new_value: float) -> None: def put_start_and_end_on( self, - start: InternalPoint3D, - end: InternalPoint3D, + start: Point3DLike, + end: Point3DLike, ) -> Self: """Sets starts and end coordinates of a line. @@ -184,8 +184,8 @@ def construct(self): if np.all(curr_start == curr_end): # TODO, any problems with resetting # these attrs? - self.start = start - self.end = end + self.start = np.asarray(start) + self.end = np.asarray(end) self.generate_points() return super().put_start_and_end_on(start, end) @@ -198,7 +198,7 @@ def get_unit_vector(self) -> Vector3D: def get_angle(self) -> float: return angle_of_vector(self.get_vector()) - def get_projection(self, point: InternalPoint3D) -> Vector3D: + def get_projection(self, point: Point3D) -> Vector3D: """Returns the projection of a point onto a line. Parameters @@ -214,7 +214,7 @@ def get_projection(self, point: InternalPoint3D) -> Vector3D: def get_slope(self) -> float: return float(np.tan(self.get_angle())) - def set_angle(self, angle: float, about_point: Point3D | None = None) -> Self: + def set_angle(self, angle: float, about_point: Point3DLike | None = None) -> Self: if about_point is None: about_point = self.get_start() @@ -298,7 +298,7 @@ def _calculate_num_dashes(self) -> int: int(np.ceil((self.get_length() / self.dash_length) * self.dashed_ratio)), ) - def get_start(self) -> InternalPoint3D: + def get_start(self) -> Point3D: """Returns the start point of the line. Examples @@ -313,7 +313,7 @@ def get_start(self) -> InternalPoint3D: else: return super().get_start() - def get_end(self) -> InternalPoint3D: + def get_end(self) -> Point3D: """Returns the end point of the line. Examples @@ -328,7 +328,7 @@ def get_end(self) -> InternalPoint3D: else: return super().get_end() - def get_first_handle(self) -> InternalPoint3D: + def get_first_handle(self) -> Point3D: """Returns the point of the first handle. Examples @@ -341,9 +341,10 @@ def get_first_handle(self) -> InternalPoint3D: # Type inference of extracting an element from a list, is not # supported by numpy, see this numpy issue # https://github.com/numpy/numpy/issues/16544 - return self.submobjects[0].points[1] + first_handle: Point3D = self.submobjects[0].points[1] + return first_handle - def get_last_handle(self) -> InternalPoint3D: + def get_last_handle(self) -> Point3D: """Returns the point of the last handle. Examples @@ -356,7 +357,8 @@ def get_last_handle(self) -> InternalPoint3D: # Type inference of extracting an element from a list, is not # supported by numpy, see this numpy issue # https://github.com/numpy/numpy/issues/16544 - return self.submobjects[-1].points[-2] + last_handle: Point3D = self.submobjects[-1].points[2] + return last_handle class TangentLine(Line): @@ -690,7 +692,7 @@ def construct(self): def __init__( self, - direction: Point2D | Point3D = RIGHT, + direction: Vector2D | Vector3D = RIGHT, buff: float = 0, **kwargs: Any, ) -> None: @@ -930,7 +932,7 @@ def __init__( line1: Line, line2: Line, radius: float | None = None, - quadrant: Point2D = (1, 1), + quadrant: tuple[Literal[-1, 1], Literal[-1, 1]] = (1, 1), other_angle: bool = False, dot: bool = False, dot_radius: float | None = None, @@ -1076,7 +1078,9 @@ def construct(self): return self.angle_value / DEGREES if degrees else self.angle_value @staticmethod - def from_three_points(A: Point3D, B: Point3D, C: Point3D, **kwargs: Any) -> Angle: + def from_three_points( + A: Point3DLike, B: Point3DLike, C: Point3DLike, **kwargs: Any + ) -> Angle: r"""The angle between the lines AB and BC. This constructs the angle :math:`\\angle ABC`. diff --git a/manim/mobject/geometry/polygram.py b/manim/mobject/geometry/polygram.py index 482581df12..9242055e38 100644 --- a/manim/mobject/geometry/polygram.py +++ b/manim/mobject/geometry/polygram.py @@ -38,10 +38,10 @@ from typing_extensions import Self from manim.typing import ( - InternalPoint3D, - InternalPoint3D_Array, Point3D, Point3D_Array, + Point3DLike, + Point3DLike_Array, ) from manim.utils.color import ParsableManimColor @@ -83,7 +83,7 @@ def construct(self): def __init__( self, - *vertex_groups: Point3D_Array, + *vertex_groups: Point3DLike_Array, color: ParsableManimColor = BLUE, **kwargs: Any, ): @@ -91,7 +91,7 @@ def __init__( for vertices in vertex_groups: # The inferred type for *vertices is Any, but it should be - # InternalPoint3D_Array + # Point3D_Array first_vertex, *vertices = vertices first_vertex = np.array(first_vertex) @@ -100,7 +100,7 @@ def __init__( [*(np.array(vertex) for vertex in vertices), first_vertex], ) - def get_vertices(self) -> InternalPoint3D_Array: + def get_vertices(self) -> Point3D_Array: """Gets the vertices of the :class:`Polygram`. Returns @@ -121,7 +121,7 @@ def get_vertices(self) -> InternalPoint3D_Array: """ return self.get_start_anchors() - def get_vertex_groups(self) -> InternalPoint3D_Array: + def get_vertex_groups(self) -> Point3D_Array: """Gets the vertex groups of the :class:`Polygram`. Returns @@ -220,7 +220,7 @@ def construct(self): if radius == 0: return self - new_points: list[InternalPoint3D] = [] + new_points: list[Point3D] = [] for vertices in self.get_vertex_groups(): arcs = [] @@ -324,7 +324,7 @@ def construct(self): self.add(isosceles, square_and_triangles) """ - def __init__(self, *vertices: InternalPoint3D, **kwargs: Any) -> None: + def __init__(self, *vertices: Point3DLike, **kwargs: Any) -> None: super().__init__(vertices, **kwargs) @@ -824,7 +824,7 @@ def construct(self): """ def __init__( - self, *points: Point3D, tolerance: float = 1e-5, **kwargs: Any + self, *points: Point3DLike, tolerance: float = 1e-5, **kwargs: Any ) -> None: # Build Convex Hull array = np.array(points)[:, :2] diff --git a/manim/mobject/geometry/tips.py b/manim/mobject/geometry/tips.py index e137016a88..2f57c3ca38 100644 --- a/manim/mobject/geometry/tips.py +++ b/manim/mobject/geometry/tips.py @@ -27,7 +27,7 @@ if TYPE_CHECKING: from typing import Any - from manim.typing import InternalPoint3D, Point3D, Vector3D + from manim.typing import Point3D, Vector3D class ArrowTip(VMobject, metaclass=ConvertToOpenGL): @@ -136,7 +136,7 @@ def base(self) -> Point3D: return self.point_from_proportion(0.5) @property - def tip_point(self) -> InternalPoint3D: + def tip_point(self) -> Point3D: r"""The tip point of the arrow tip. Examples @@ -152,7 +152,8 @@ def tip_point(self) -> InternalPoint3D: # Type inference of extracting an element from a list, is not # supported by numpy, see this numpy issue # https://github.com/numpy/numpy/issues/16544 - return self.points[0] + tip_point: Point3D = self.points[0] + return tip_point @property def vector(self) -> Vector3D: diff --git a/manim/mobject/graph.py b/manim/mobject/graph.py index 9cca91f03e..1218b22c0d 100644 --- a/manim/mobject/graph.py +++ b/manim/mobject/graph.py @@ -8,7 +8,7 @@ ] import itertools as it -from collections.abc import Hashable, Iterable +from collections.abc import Hashable, Iterable, Sequence from copy import copy from typing import TYPE_CHECKING, Any, Literal, Protocol, cast @@ -19,7 +19,7 @@ from typing_extensions import TypeAlias from manim.scene.scene import Scene - from manim.typing import Point3D + from manim.typing import Point3D, Point3DLike NxGraph: TypeAlias = nx.classes.graph.Graph | nx.classes.digraph.DiGraph @@ -268,9 +268,9 @@ def __call__( Parameters ---------- - graph : NxGraph + graph The underlying NetworkX graph to be laid out. DO NOT MODIFY. - scale : float | tuple[float, float, float], optional + scale Either a single float value, or a tuple of three float values specifying the scale along each axis. Returns @@ -284,7 +284,7 @@ def __call__( def _partite_layout( nx_graph: NxGraph, scale: float = 2, - partitions: list[list[Hashable]] | None = None, + partitions: Sequence[Sequence[Hashable]] | None = None, **kwargs: Any, ) -> dict[Hashable, Point3D]: if partitions is None or len(partitions) == 0: @@ -443,7 +443,7 @@ def slide(v, dx): def _determine_graph_layout( nx_graph: nx.classes.graph.Graph | nx.classes.digraph.DiGraph, - layout: LayoutName | dict[Hashable, Point3D] | LayoutFunction = "spring", + layout: LayoutName | dict[Hashable, Point3DLike] | LayoutFunction = "spring", layout_scale: float | tuple[float, float, float] = 2, layout_config: dict[str, Any] | None = None, ) -> dict[Hashable, Point3D]: @@ -562,18 +562,18 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL): def __init__( self, - vertices: list[Hashable], - edges: list[tuple[Hashable, Hashable]], + vertices: Sequence[Hashable], + edges: Sequence[tuple[Hashable, Hashable]], labels: bool | dict = False, label_fill_color: str = BLACK, - layout: LayoutName | dict[Hashable, Point3D] | LayoutFunction = "spring", + layout: LayoutName | dict[Hashable, Point3DLike] | LayoutFunction = "spring", layout_scale: float | tuple[float, float, float] = 2, layout_config: dict | None = None, vertex_type: type[Mobject] = Dot, vertex_config: dict | None = None, vertex_mobjects: dict | None = None, edge_type: type[Mobject] = Line, - partitions: list[list[Hashable]] | None = None, + partitions: Sequence[Sequence[Hashable]] | None = None, root_vertex: Hashable | None = None, edge_config: dict | None = None, ) -> None: @@ -677,15 +677,16 @@ def __getitem__(self: Graph, v: Hashable) -> Mobject: def _create_vertex( self, vertex: Hashable, - position: Point3D | None = None, + position: Point3DLike | None = None, label: bool = False, label_fill_color: str = BLACK, vertex_type: type[Mobject] = Dot, vertex_config: dict | None = None, vertex_mobject: dict | None = None, ) -> tuple[Hashable, Point3D, dict, Mobject]: - if position is None: - position = self.get_center() + np_position: Point3D = ( + self.get_center() if position is None else np.asarray(position) + ) if vertex_config is None: vertex_config = {} @@ -714,14 +715,14 @@ def _create_vertex( if vertex_mobject is None: vertex_mobject = vertex_type(**vertex_config) - vertex_mobject.move_to(position) + vertex_mobject.move_to(np_position) - return (vertex, position, vertex_config, vertex_mobject) + return (vertex, np_position, vertex_config, vertex_mobject) def _add_created_vertex( self, vertex: Hashable, - position: Point3D, + position: Point3DLike, vertex_config: dict, vertex_mobject: Mobject, ) -> Mobject: @@ -747,7 +748,7 @@ def _add_created_vertex( def _add_vertex( self, vertex: Hashable, - position: Point3D | None = None, + position: Point3DLike | None = None, label: bool = False, label_fill_color: str = BLACK, vertex_type: type[Mobject] = Dot, @@ -1206,7 +1207,7 @@ def construct(self): def change_layout( self, - layout: LayoutName | dict[Hashable, Point3D] | LayoutFunction = "spring", + layout: LayoutName | dict[Hashable, Point3DLike] | LayoutFunction = "spring", layout_scale: float | tuple[float, float, float] = 2, layout_config: dict[str, Any] | None = None, partitions: list[list[Hashable]] | None = None, diff --git a/manim/mobject/graphing/coordinate_systems.py b/manim/mobject/graphing/coordinate_systems.py index fa07c7fd53..106f679873 100644 --- a/manim/mobject/graphing/coordinate_systems.py +++ b/manim/mobject/graphing/coordinate_systems.py @@ -57,7 +57,7 @@ if TYPE_CHECKING: from manim.mobject.mobject import Mobject - from manim.typing import ManimFloat, Point2D, Point3D, Vector3D + from manim.typing import ManimFloat, Point2D, Point2DLike, Point3DLike, Vector3D LineType = TypeVar("LineType", bound=Line) @@ -150,7 +150,7 @@ def __init__( def coords_to_point(self, *coords: ManimFloat): raise NotImplementedError() - def point_to_coords(self, point: Point3D): + def point_to_coords(self, point: Point3DLike): raise NotImplementedError() def polar_to_point(self, radius: float, azimuth: float) -> Point2D: @@ -184,7 +184,7 @@ def construct(self): """ return self.coords_to_point(radius * np.cos(azimuth), radius * np.sin(azimuth)) - def point_to_polar(self, point: np.ndarray) -> Point2D: + def point_to_polar(self, point: Point2DLike) -> Point2D: r"""Gets polar coordinates from a point. Parameters @@ -206,7 +206,7 @@ def c2p( """Abbreviation for :meth:`coords_to_point`""" return self.coords_to_point(*coords) - def p2c(self, point: Point3D): + def p2c(self, point: Point3DLike): """Abbreviation for :meth:`point_to_coords`""" return self.point_to_coords(point) @@ -1836,12 +1836,12 @@ def construct(self): return T_label_group - def __matmul__(self, coord: Point3D | Mobject): + def __matmul__(self, coord: Point3DLike | Mobject): if isinstance(coord, Mobject): coord = coord.get_center() return self.coords_to_point(*coord) - def __rmatmul__(self, point: Point3D): + def __rmatmul__(self, point: Point3DLike): return self.point_to_coords(point) @@ -3379,7 +3379,7 @@ def n2p(self, number: float | complex) -> np.ndarray: """Abbreviation for :meth:`number_to_point`.""" return self.number_to_point(number) - def point_to_number(self, point: Point3D) -> complex: + def point_to_number(self, point: Point3DLike) -> complex: """Accepts a point and returns a complex number equivalent to that point on the plane. Parameters @@ -3395,7 +3395,7 @@ def point_to_number(self, point: Point3D) -> complex: x, y = self.point_to_coords(point) return complex(x, y) - def p2n(self, point: Point3D) -> complex: + def p2n(self, point: Point3DLike) -> complex: """Abbreviation for :meth:`point_to_number`.""" return self.point_to_number(point) diff --git a/manim/mobject/graphing/functions.py b/manim/mobject/graphing/functions.py index 1cd660b894..3a8913dd60 100644 --- a/manim/mobject/graphing/functions.py +++ b/manim/mobject/graphing/functions.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: from typing_extensions import Self - from manim.typing import Point3D + from manim.typing import Point3DLike from manim.utils.color import YELLOW @@ -104,7 +104,7 @@ def construct(self): def __init__( self, - function: Callable[[float], Point3D], + function: Callable[[float], Point3DLike], t_range: tuple[float, float] | tuple[float, float, float] = (0, 1), scaling: _ScaleBase = LinearBase(), dt: float = 1e-8, @@ -127,10 +127,10 @@ def __init__( super().__init__(**kwargs) - def get_function(self) -> Callable[[float], Point3D]: + def get_function(self) -> Callable[[float], Point3DLike]: return self.function - def get_point_from_function(self, t: float) -> Point3D: + def get_point_from_function(self, t: float) -> Point3DLike: return self.function(t) def generate_points(self) -> Self: diff --git a/manim/mobject/graphing/number_line.py b/manim/mobject/graphing/number_line.py index efb95ae399..0830bf68ec 100644 --- a/manim/mobject/graphing/number_line.py +++ b/manim/mobject/graphing/number_line.py @@ -13,7 +13,7 @@ if TYPE_CHECKING: from manim.mobject.geometry.tips import ArrowTip - from manim.typing import Point3D + from manim.typing import Point3DLike import numpy as np @@ -650,7 +650,7 @@ def _decimal_places_from_step(step) -> int: def __matmul__(self, other: float): return self.n2p(other) - def __rmatmul__(self, other: Point3D | Mobject): + def __rmatmul__(self, other: Point3DLike | Mobject): if isinstance(other, Mobject): other = other.get_center() return self.p2n(other) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 0359f66045..3b9856c274 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -44,14 +44,14 @@ from manim.typing import ( FunctionOverride, - InternalPoint3D, ManimFloat, ManimInt, MappingFunction, PathFuncType, PixelArray, Point3D, - Point3D_Array, + Point3DLike, + Point3DLike_Array, Vector3D, ) @@ -1276,7 +1276,7 @@ def rotate( self, angle: float, axis: Vector3D = OUT, - about_point: Point3D | None = None, + about_point: Point3DLike | None = None, **kwargs, ) -> Self: """Rotates the :class:`~.Mobject` about a certain point.""" @@ -1401,7 +1401,7 @@ def repeat_array(array): def apply_points_function_about_point( self, func: MappingFunction, - about_point: Point3D = None, + about_point: Point3DLike = None, about_edge=None, ) -> Self: if about_point is None: @@ -1508,7 +1508,7 @@ def construct(self): def next_to( self, - mobject_or_point: Mobject | Point3D, + mobject_or_point: Mobject | Point3DLike, direction: Vector3D = RIGHT, buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, aligned_edge: Vector3D = ORIGIN, @@ -1575,7 +1575,7 @@ def is_off_screen(self): return True return self.get_top()[1] < -config["frame_y_radius"] - def stretch_about_point(self, factor: float, dim: int, point: Point3D) -> Self: + def stretch_about_point(self, factor: float, dim: int, point: Point3DLike) -> Self: return self.stretch(factor, dim, about_point=point) def rescale_to_fit( @@ -1725,7 +1725,7 @@ def space_out_submobjects(self, factor: float = 1.5, **kwargs) -> Self: def move_to( self, - point_or_mobject: Point3D | Mobject, + point_or_mobject: Point3DLike | Mobject, aligned_edge: Vector3D = ORIGIN, coor_mask: Vector3D = np.array([1, 1, 1]), ) -> Self: @@ -1767,7 +1767,7 @@ def surround( self.scale((length + buff) / length) return self - def put_start_and_end_on(self, start: Point3D, end: Point3D) -> Self: + def put_start_and_end_on(self, start: Point3DLike, end: Point3DLike) -> Self: curr_start, curr_end = self.get_start_and_end() curr_vect = curr_end - curr_start if np.all(curr_vect == 0): @@ -1874,7 +1874,7 @@ def set_color_by_gradient(self, *colors: ParsableManimColor) -> Self: def set_colors_by_radial_gradient( self, - center: Point3D | None = None, + center: Point3DLike | None = None, radius: float = 1, inner_color: ParsableManimColor = WHITE, outer_color: ParsableManimColor = BLACK, @@ -1902,7 +1902,7 @@ def set_submobject_colors_by_gradient(self, *colors: Iterable[ParsableManimColor def set_submobject_colors_by_radial_gradient( self, - center: Point3D | None = None, + center: Point3DLike | None = None, radius: float = 1, inner_color: ParsableManimColor = WHITE, outer_color: ParsableManimColor = BLACK, @@ -2028,11 +2028,14 @@ def get_num_points(self) -> int: return len(self.points) def get_extremum_along_dim( - self, points: Point3D_Array | None = None, dim: int = 0, key: int = 0 + self, points: Point3DLike_Array | None = None, dim: int = 0, key: int = 0 ) -> np.ndarray | float: - if points is None: - points = self.get_points_defining_boundary() - values = points[:, dim] + np_points: Point3D_Array = ( + self.get_points_defining_boundary() + if points is None + else np.asarray(points) + ) + values = np_points[:, dim] if key < 0: return np.min(values) elif key == 0: @@ -2159,24 +2162,24 @@ def get_z(self, direction: Vector3D = ORIGIN) -> ManimFloat: """Returns z Point3D of the center of the :class:`~.Mobject` as ``float``""" return self.get_coord(2, direction) - def get_start(self) -> InternalPoint3D: + def get_start(self) -> Point3D: """Returns the point, where the stroke that surrounds the :class:`~.Mobject` starts.""" self.throw_error_if_no_points() return np.array(self.points[0]) - def get_end(self) -> InternalPoint3D: + def get_end(self) -> Point3D: """Returns the point, where the stroke that surrounds the :class:`~.Mobject` ends.""" self.throw_error_if_no_points() return np.array(self.points[-1]) - def get_start_and_end(self) -> tuple[InternalPoint3D, InternalPoint3D]: + def get_start_and_end(self) -> tuple[Point3D, Point3D]: """Returns starting and ending point of a stroke as a ``tuple``.""" return self.get_start(), self.get_end() def point_from_proportion(self, alpha: float) -> Point3D: raise NotImplementedError("Please override in a child class.") - def proportion_from_point(self, point: Point3D) -> float: + def proportion_from_point(self, point: Point3DLike) -> float: raise NotImplementedError("Please override in a child class.") def get_pieces(self, n_pieces: float) -> Group: @@ -2249,7 +2252,7 @@ def match_z(self, mobject: Mobject, direction=ORIGIN) -> Self: def align_to( self, - mobject_or_point: Mobject | Point3D, + mobject_or_point: Mobject | Point3DLike, direction: Vector3D = ORIGIN, ) -> Self: """Aligns mobject to another :class:`~.Mobject` in a certain direction. @@ -2576,13 +2579,13 @@ def init_sizes(sizes, num, measures, name): def sort( self, - point_to_num_func: Callable[[Point3D], ManimInt] = lambda p: p[0], + point_to_num_func: Callable[[Point3DLike], ManimInt] = lambda p: p[0], submob_func: Callable[[Mobject], ManimInt] | None = None, ) -> Self: """Sorts the list of :attr:`submobjects` by a function defined by ``submob_func``.""" if submob_func is None: - def submob_func(m: Mobject): + def submob_func(m: Mobject) -> ManimInt: return point_to_num_func(m.get_center()) self.submobjects.sort(key=submob_func) diff --git a/manim/mobject/opengl/opengl_mobject.py b/manim/mobject/opengl/opengl_mobject.py index 1f7d44d6a3..577bae8afc 100644 --- a/manim/mobject/opengl/opengl_mobject.py +++ b/manim/mobject/opengl/opengl_mobject.py @@ -58,6 +58,8 @@ PathFuncType, Point3D, Point3D_Array, + Point3DLike, + Point3DLike_Array, Vector3D, ) @@ -573,7 +575,7 @@ def resize_points(self, new_length, resize_func=resize_array): self.refresh_bounding_box() return self - def set_points(self, points: Point3D_Array) -> Self: + def set_points(self, points: Point3DLike_Array) -> Self: if len(points) == len(self.points): self.points[:] = points elif isinstance(points, np.ndarray): @@ -591,7 +593,7 @@ def apply_over_attr_arrays( setattr(self, attr, func(getattr(self, attr))) return self - def append_points(self, new_points: Point3D_Array) -> Self: + def append_points(self, new_points: Point3DLike_Array) -> Self: self.points = np.vstack([self.points, new_points]) self.refresh_bounding_box() return self @@ -628,7 +630,7 @@ def construct(self): def apply_points_function( self, func: MappingFunction, - about_point: Point3D | None = None, + about_point: Point3DLike | None = None, about_edge: Vector3D | None = ORIGIN, works_on_bounding_box: bool = False, ) -> Self: @@ -729,7 +731,9 @@ def refresh_bounding_box( parent.refresh_bounding_box() return self - def is_point_touching(self, point: Point3D, buff: float = MED_SMALL_BUFF) -> bool: + def is_point_touching( + self, point: Point3DLike, buff: float = MED_SMALL_BUFF + ) -> bool: bb = self.get_bounding_box() mins = bb[0] - buff maxs = bb[2] + buff @@ -1291,7 +1295,7 @@ def duplicate(self, n: int) -> OpenGLGroup: def sort( self, - point_to_num_func: Callable[[Point3D], float] = lambda p: p[0], + point_to_num_func: Callable[[Point3DLike], float] = lambda p: p[0], submob_func: Callable[[OpenGLMobject], Any] | None = None, ) -> Self: """Sorts the list of :attr:`submobjects` by a function defined by ``submob_func``.""" @@ -1800,13 +1804,13 @@ def to_edge( def next_to( self, - mobject_or_point: OpenGLMobject | Point3D, + mobject_or_point: OpenGLMobject | Point3DLike, direction: Vector3D = RIGHT, buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, aligned_edge: Vector3D = ORIGIN, submobject_to_align: OpenGLMobject | None = None, index_of_submobject_to_align: int | None = None, - coor_mask: Point3D = np.array([1, 1, 1]), + coor_mask: Point3DLike = np.array([1, 1, 1]), ) -> Self: """Move this :class:`~.OpenGLMobject` next to another's :class:`~.OpenGLMobject` or coordinate. @@ -1869,7 +1873,7 @@ def is_off_screen(self) -> bool: return True return self.get_top()[1] < -config.frame_y_radius - def stretch_about_point(self, factor: float, dim: int, point: Point3D) -> Self: + def stretch_about_point(self, factor: float, dim: int, point: Point3DLike) -> Self: return self.stretch(factor, dim, about_point=point) def rescale_to_fit( @@ -1983,9 +1987,9 @@ def space_out_submobjects(self, factor: float = 1.5, **kwargs) -> Self: def move_to( self, - point_or_mobject: Point3D | OpenGLMobject, + point_or_mobject: Point3DLike | OpenGLMobject, aligned_edge: Vector3D = ORIGIN, - coor_mask: Point3D = np.array([1, 1, 1]), + coor_mask: Point3DLike = np.array([1, 1, 1]), ) -> Self: """Move center of the :class:`~.OpenGLMobject` to certain coordinate.""" if isinstance(point_or_mobject, OpenGLMobject): @@ -2029,7 +2033,7 @@ def surround( self.scale((length + buff) / length) return self - def put_start_and_end_on(self, start: Point3D, end: Point3D) -> Self: + def put_start_and_end_on(self, start: Point3DLike, end: Point3DLike) -> Self: curr_start, curr_end = self.get_start_and_end() curr_vect = curr_end - curr_start if np.all(curr_vect == 0): @@ -2421,7 +2425,7 @@ def match_z(self, mobject: OpenGLMobject, direction: Vector3D = ORIGIN) -> Self: def align_to( self, - mobject_or_point: OpenGLMobject | Point3D, + mobject_or_point: OpenGLMobject | Point3DLike, direction: Vector3D = ORIGIN, ) -> Self: """ @@ -2892,7 +2896,7 @@ def __init__(self, *mobjects: OpenGLMobject, **kwargs): class OpenGLPoint(OpenGLMobject): def __init__( self, - location: Point3D = ORIGIN, + location: Point3DLike = ORIGIN, artificial_width: float = 1e-6, artificial_height: float = 1e-6, **kwargs, diff --git a/manim/mobject/svg/brace.py b/manim/mobject/svg/brace.py index ae7ee6a2ac..3d826f4f01 100644 --- a/manim/mobject/svg/brace.py +++ b/manim/mobject/svg/brace.py @@ -26,7 +26,7 @@ from ..svg.svg_mobject import VMobjectFromSVGPath if TYPE_CHECKING: - from manim.typing import Point3D, Vector3D + from manim.typing import Point3DLike, Vector3D from manim.utils.color.core import ParsableManimColor __all__ = ["Brace", "BraceBetweenPoints", "BraceLabel", "ArcBrace"] @@ -317,8 +317,8 @@ def construct(self): def __init__( self, - point_1: Point3D | None, - point_2: Point3D | None, + point_1: Point3DLike | None, + point_2: Point3DLike | None, direction: Vector3D | None = ORIGIN, **kwargs, ): diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 7b30f9a7ad..5732ebb98c 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -2,7 +2,7 @@ from __future__ import annotations -from manim.typing import Point3D, Vector3D +from manim.typing import Point3DLike, Vector3D from manim.utils.color import BLUE, BLUE_D, BLUE_E, LIGHT_GREY, WHITE, interpolate_color __all__ = [ @@ -373,7 +373,7 @@ def construct(self): def __init__( self, - center: Point3D = ORIGIN, + center: Point3DLike = ORIGIN, radius: float = 1, resolution: Sequence[int] | None = None, u_range: Sequence[float] = (0, TAU), @@ -966,7 +966,7 @@ def set_start_and_end_attrs( def pointify( self, - mob_or_point: Mobject | Point3D, + mob_or_point: Mobject | Point3DLike, direction: Vector3D = None, ) -> np.ndarray: """Gets a point representing the center of the :class:`Mobjects <.Mobject>`. diff --git a/manim/mobject/types/point_cloud_mobject.py b/manim/mobject/types/point_cloud_mobject.py index c9f54e6ed2..f5953aab8c 100644 --- a/manim/mobject/types/point_cloud_mobject.py +++ b/manim/mobject/types/point_cloud_mobject.py @@ -35,7 +35,7 @@ import numpy.typing as npt from typing_extensions import Self - from manim.typing import ManimFloat, Point3D, Vector3D + from manim.typing import ManimFloat, Point3DLike, Vector3D class PMobject(Mobject, metaclass=ConvertToOpenGL): @@ -130,7 +130,7 @@ def set_color_by_gradient(self, *colors: ParsableManimColor) -> Self: def set_colors_by_radial_gradient( self, - center: Point3D | None = None, + center: Point3DLike | None = None, radius: float = 1, inner_color: ParsableManimColor = WHITE, outer_color: ParsableManimColor = BLACK, @@ -216,7 +216,7 @@ def align_points_with_larger(self, larger_mobject: Mobject) -> None: lambda a: stretch_array_to_length(a, larger_mobject.get_num_points()), ) - def get_point_mobject(self, center: Point3D | None = None) -> Point: + def get_point_mobject(self, center: Point3DLike | None = None) -> Point: if center is None: center = self.get_center() return Point(center) diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index a8d32682fd..66b4270571 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -54,15 +54,14 @@ from typing_extensions import Self from manim.typing import ( - BezierPoints, - CubicBezierPoints, - InternalPoint3D_Array, + CubicBezierPointsLike, ManimFloat, MappingFunction, - Point2D, + Point2DLike, Point3D, Point3D_Array, - QuadraticBezierPoints, + Point3DLike, + Point3DLike_Array, RGBA_Array_Float, Vector3D, Zeros, @@ -769,13 +768,13 @@ def set_shade_in_3d( return self def set_points(self, points: Point3D_Array) -> Self: - self.points: InternalPoint3D_Array = np.array(points) + self.points: Point3D_Array = np.array(points) return self def resize_points( self, new_length: int, - resize_func: Callable[[Point3D, int], Point3D] = resize_array, + resize_func: Callable[[Point3DLike, int], Point3DLike] = resize_array, ) -> Self: """Resize the array of anchor points and handles to have the specified size. @@ -795,10 +794,10 @@ def resize_points( def set_anchors_and_handles( self, - anchors1: CubicBezierPoints, - handles1: CubicBezierPoints, - handles2: CubicBezierPoints, - anchors2: CubicBezierPoints, + anchors1: Point3DLike_Array, + handles1: Point3DLike_Array, + handles2: Point3DLike_Array, + anchors2: Point3DLike_Array, ) -> Self: """Given two sets of anchors and handles, process them to set them as anchors and handles of the VMobject. @@ -855,7 +854,7 @@ def append_points(self, new_points: Point3D_Array) -> Self: self.points = points return self - def start_new_path(self, point: Point3D) -> Self: + def start_new_path(self, point: Point3DLike) -> Self: """Append a ``point`` to the :attr:`VMobject.points`, which will be the beginning of a new Bézier curve in the path given by the points. If there's an unfinished curve at the end of :attr:`VMobject.points`, @@ -887,10 +886,10 @@ def start_new_path(self, point: Point3D) -> Self: def add_cubic_bezier_curve( self, - anchor1: CubicBezierPoints, - handle1: CubicBezierPoints, - handle2: CubicBezierPoints, - anchor2: CubicBezierPoints, + anchor1: Point3DLike, + handle1: Point3DLike, + handle2: Point3DLike, + anchor2: Point3DLike, ) -> None: # TODO, check the len(self.points) % 4 == 0? self.append_points([anchor1, handle1, handle2, anchor2]) @@ -901,9 +900,9 @@ def add_cubic_bezier_curves(self, curves) -> None: def add_cubic_bezier_curve_to( self, - handle1: CubicBezierPoints, - handle2: CubicBezierPoints, - anchor: CubicBezierPoints, + handle1: Point3DLike, + handle2: Point3DLike, + anchor: Point3DLike, ) -> Self: """Add cubic bezier curve to the path. @@ -933,8 +932,8 @@ def add_cubic_bezier_curve_to( def add_quadratic_bezier_curve_to( self, - handle: QuadraticBezierPoints, - anchor: QuadraticBezierPoints, + handle: Point3DLike, + anchor: Point3DLike, ) -> Self: """Add Quadratic bezier curve to the path. @@ -957,7 +956,7 @@ def add_quadratic_bezier_curve_to( ) return self - def add_line_to(self, point: Point3D) -> Self: + def add_line_to(self, point: Point3DLike) -> Self: """Add a straight line from the last point of VMobject to the given point. Parameters @@ -979,7 +978,7 @@ def add_line_to(self, point: Point3D) -> Self: ) return self - def add_smooth_curve_to(self, *points: Point3D) -> Self: + def add_smooth_curve_to(self, *points: Point3DLike) -> Self: """Creates a smooth curve from given points and add it to the VMobject. If two points are passed in, the first is interpreted as a handle, the second as an anchor. @@ -1038,7 +1037,7 @@ def close_path(self) -> None: if not self.is_closed(): self.add_line_to(self.get_subpaths()[-1][0]) - def add_points_as_corners(self, points: Iterable[Point3D]) -> Iterable[Point3D]: + def add_points_as_corners(self, points: Point3DLike_Array) -> Point3D_Array: """Append multiple straight lines at the end of :attr:`VMobject.points`, which connect the given ``points`` in order starting from the end of the current path. These ``points`` would be @@ -1080,7 +1079,7 @@ def add_points_as_corners(self, points: Iterable[Point3D]) -> Iterable[Point3D]: # TODO: shouldn't this method return self instead of points? return points - def set_points_as_corners(self, points: Point3D_Array) -> Self: + def set_points_as_corners(self, points: Point3DLike_Array) -> Self: """Given an array of points, set them as corners of the :class:`VMobject`. @@ -1127,7 +1126,7 @@ def construct(self): ) return self - def set_points_smoothly(self, points: Point3D_Array) -> Self: + def set_points_smoothly(self, points: Point3DLike_Array) -> Self: self.set_points_as_corners(points) self.make_smooth() return self @@ -1172,7 +1171,7 @@ def make_smooth(self) -> Self: def make_jagged(self) -> Self: return self.change_anchor_mode("jagged") - def add_subpath(self, points: Point3D_Array) -> Self: + def add_subpath(self, points: CubicBezierPathLike) -> Self: assert len(points) % 4 == 0 self.append_points(points) return self @@ -1197,7 +1196,7 @@ def rotate( self, angle: float, axis: Vector3D = OUT, - about_point: Point3D | None = None, + about_point: Point3DLike | None = None, **kwargs, ) -> Self: self.rotate_sheen_direction(angle, axis) @@ -1236,10 +1235,10 @@ def scale_handle_to_anchor_distances(self, factor: float) -> Self: return self # - def consider_points_equals(self, p0: Point3D, p1: Point3D) -> bool: + def consider_points_equals(self, p0: Point3DLike, p1: Point3DLike) -> bool: return np.allclose(p0, p1, atol=self.tolerance_for_point_equality) - def consider_points_equals_2d(self, p0: Point2D, p1: Point2D) -> bool: + def consider_points_equals_2d(self, p0: Point2DLike, p1: Point2DLike) -> bool: """Determine if two points are close enough to be considered equal. This uses the algorithm from np.isclose(), but expanded here for the @@ -1264,13 +1263,13 @@ def consider_points_equals_2d(self, p0: Point2D, p1: Point2D) -> bool: # Information about line def get_cubic_bezier_tuples_from_points( - self, points: Point3D_Array - ) -> npt.NDArray[Point3D_Array]: + self, points: CubicBezierPathLike + ) -> CubicBezierPoints_Array: return np.array(self.gen_cubic_bezier_tuples_from_points(points)) def gen_cubic_bezier_tuples_from_points( - self, points: Point3D_Array - ) -> tuple[Point3D_Array]: + self, points: CubicBezierPathLike + ) -> tuple[CubicBezierPointsLike, ...]: """Returns the bezier tuples from an array of points. self.points is a list of the anchors and handles of the bezier curves of the mobject (ie [anchor1, handle1, handle2, anchor2, anchor3 ..]) @@ -1294,14 +1293,14 @@ def gen_cubic_bezier_tuples_from_points( # Basically take every nppcc element. return tuple(points[i : i + nppcc] for i in range(0, len(points), nppcc)) - def get_cubic_bezier_tuples(self) -> npt.NDArray[Point3D_Array]: + def get_cubic_bezier_tuples(self) -> CubicBezierPoints_Array: return self.get_cubic_bezier_tuples_from_points(self.points) def _gen_subpaths_from_points( self, - points: Point3D_Array, + points: Point3DLike_Array, filter_func: Callable[[int], bool], - ) -> Generator[Point3D_Array]: + ) -> Generator[CubicSplineLike]: """Given an array of points defining the bezier curves of the vmobject, return subpaths formed by these points. Here, Two bezier curves form a path if at least two of their anchors are evaluated True by the relation defined by filter_func. @@ -1331,7 +1330,9 @@ def _gen_subpaths_from_points( if (i2 - i1) >= nppcc ) - def get_subpaths_from_points(self, points: Point3D_Array) -> list[Point3D_Array]: + def get_subpaths_from_points( + self, points: CubicBezierPath + ) -> list[CubicSplineLike]: return list( self._gen_subpaths_from_points( points, @@ -1340,14 +1341,14 @@ def get_subpaths_from_points(self, points: Point3D_Array) -> list[Point3D_Array] ) def gen_subpaths_from_points_2d( - self, points: Point3D_Array - ) -> Generator[Point3D_Array]: + self, points: CubicBezierPathLike + ) -> Generator[CubicSplineLike]: return self._gen_subpaths_from_points( points, lambda n: not self.consider_points_equals_2d(points[n - 1], points[n]), ) - def get_subpaths(self) -> list[Point3D_Array]: + def get_subpaths(self) -> list[CubicSplineLike]: """Returns subpaths formed by the curves of the VMobject. Subpaths are ranges of curves with each pair of consecutive curves having their end/start points coincident. @@ -1359,7 +1360,7 @@ def get_subpaths(self) -> list[Point3D_Array]: """ return self.get_subpaths_from_points(self.points) - def get_nth_curve_points(self, n: int) -> Point3D_Array: + def get_nth_curve_points(self, n: int) -> CubicBezierPoints: """Returns the points defining the nth curve of the vmobject. Parameters @@ -1579,7 +1580,7 @@ def construct(self): def proportion_from_point( self, - point: Iterable[float | int], + point: Point3DLike, ) -> float: """Returns the proportion along the path of the :class:`VMobject` a particular given point is at. @@ -1647,7 +1648,7 @@ def get_anchors_and_handles(self) -> list[Point3D_Array]: nppcc = self.n_points_per_cubic_curve return [self.points[i::nppcc] for i in range(nppcc)] - def get_start_anchors(self) -> InternalPoint3D_Array: + def get_start_anchors(self) -> Point3D_Array: """Returns the start anchors of the bezier curves. Returns @@ -1668,7 +1669,7 @@ def get_end_anchors(self) -> Point3D_Array: nppcc = self.n_points_per_cubic_curve return self.points[nppcc - 1 :: nppcc] - def get_anchors(self) -> Point3D_Array: + def get_anchors(self) -> list[Point3D]: """Returns the anchors of the curves forming the VMobject. Returns @@ -1806,8 +1807,8 @@ def insert_n_curves(self, n: int) -> Self: return self def insert_n_curves_to_point_list( - self, n: int, points: Point3D_Array - ) -> npt.NDArray[BezierPoints]: + self, n: int, points: BezierPathLike + ) -> BezierPath: """Given an array of k points defining a bezier curves (anchors and handles), returns points defining exactly k + n bezier curves. Parameters @@ -1844,7 +1845,7 @@ def align_rgbas(self, vmobject: VMobject) -> Self: setattr(self, attr, new_a1) return self - def get_point_mobject(self, center: Point3D | None = None) -> VectorizedPoint: + def get_point_mobject(self, center: Point3DLike | None = None) -> VectorizedPoint: if center is None: center = self.get_center() point = VectorizedPoint(center) @@ -2625,7 +2626,7 @@ def add_key_value_pair(self, key: Hashable, value: VMobject) -> None: class VectorizedPoint(VMobject, metaclass=ConvertToOpenGL): def __init__( self, - location: Point3D = ORIGIN, + location: Point3DLike = ORIGIN, color: ManimColor = BLACK, fill_opacity: float = 0, stroke_width: float = 0, diff --git a/manim/typing.py b/manim/typing.py index 92815e95e9..99678f9928 100644 --- a/manim/typing.py +++ b/manim/typing.py @@ -20,6 +20,7 @@ from __future__ import annotations +from collections.abc import Sequence from os import PathLike from typing import Callable, Union @@ -47,18 +48,18 @@ "HSVA_Tuple_Float", "ManimColorInternal", "PointDType", - "InternalPoint2D", "Point2D", - "InternalPoint2D_Array", + "Point2DLike", "Point2D_Array", - "InternalPoint3D", + "Point2DLike_Array", "Point3D", - "InternalPoint3D_Array", + "Point3DLike", "Point3D_Array", - "InternalPointND", + "Point3DLike_Array", "PointND", - "InternalPointND_Array", + "PointNDLike", "PointND_Array", + "PointNDLike_Array", "Vector2D", "Vector2D_Array", "Vector3D", @@ -70,17 +71,29 @@ "MatrixMN", "Zeros", "QuadraticBezierPoints", + "QuadraticBezierPointsLike", "QuadraticBezierPoints_Array", + "QuadraticBezierPointsLike_Array", "QuadraticBezierPath", + "QuadraticBezierPathLike", "QuadraticSpline", + "QuadraticSplineLike", "CubicBezierPoints", + "CubicBezierPointsLike", "CubicBezierPoints_Array", + "CubicBezierPointsLike_Array", "CubicBezierPath", + "CubicBezierPathLike", "CubicSpline", + "CubicSplineLike", "BezierPoints", + "BezierPointsLike", "BezierPoints_Array", + "BezierPointsLike_Array", "BezierPath", + "BezierPathLike", "Spline", + "SplineLike", "FlatBezierPoints", "FunctionOverride", "PathFuncType", @@ -285,39 +298,42 @@ floating point value. """ -InternalPoint2D: TypeAlias = npt.NDArray[PointDType] +Point2D: TypeAlias = npt.NDArray[PointDType] """``shape: (2,)`` -A 2-dimensional point: ``[float, float]``. - -.. note:: - This type alias is mostly made available for internal use, and - only includes the NumPy type. +A NumPy array representing a 2-dimensional point: ``[float, float]``. """ -Point2D: TypeAlias = Union[InternalPoint2D, tuple[float, float]] +Point2DLike: TypeAlias = Union[Point2D, tuple[float, float]] """``shape: (2,)`` A 2-dimensional point: ``[float, float]``. +This represents anything which can be converted to a :class:`Point2D` NumPy +array. + Normally, a function or method which expects a `Point2D` as a parameter can handle being passed a `Point3D` instead. """ -InternalPoint2D_Array: TypeAlias = npt.NDArray[PointDType] +Point2D_Array: TypeAlias = npt.NDArray[PointDType] """``shape: (M, 2)`` -An array of `InternalPoint2D` objects: ``[[float, float], ...]``. +A NumPy array representing a sequence of `Point2D` objects: +``[[float, float], ...]``. .. note:: This type alias is mostly made available for internal use, and only includes the NumPy type. """ -Point2D_Array: TypeAlias = Union[InternalPoint2D_Array, tuple[Point2D, ...]] +Point2DLike_Array: TypeAlias = Union[Point2D_Array, Sequence[Point2DLike]] """``shape: (M, 2)`` -An array of `Point2D` objects: ``[[float, float], ...]``. +An array of `Point2DLike` objects: ``[[float, float], ...]``. + +This represents anything which can be converted to a :class:`Point2D_Array` +NumPy array. Normally, a function or method which expects a `Point2D_Array` as a parameter can handle being passed a `Point3D_Array` instead. @@ -326,72 +342,86 @@ further type information. """ -InternalPoint3D: TypeAlias = npt.NDArray[PointDType] +Point3D: TypeAlias = npt.NDArray[PointDType] """``shape: (3,)`` -A 3-dimensional point: ``[float, float, float]``. +A NumPy array representing a 3-dimensional point: ``[float, float, float]``. .. note:: This type alias is mostly made available for internal use, and only includes the NumPy type. """ -Point3D: TypeAlias = Union[InternalPoint3D, tuple[float, float, float]] +Point3DLike: TypeAlias = Union[Point3D, tuple[float, float, float]] """``shape: (3,)`` A 3-dimensional point: ``[float, float, float]``. + +This represents anything which can be converted to a :class:`Point3D` NumPy +array. """ -InternalPoint3D_Array: TypeAlias = npt.NDArray[PointDType] +Point3D_Array: TypeAlias = npt.NDArray[PointDType] """``shape: (M, 3)`` -An array of `Point3D` objects: ``[[float, float, float], ...]``. +A NumPy array representing a sequence of `Point3D` objects: +``[[float, float, float], ...]``. .. note:: This type alias is mostly made available for internal use, and only includes the NumPy type. """ -Point3D_Array: TypeAlias = Union[InternalPoint3D_Array, tuple[Point3D, ...]] +Point3DLike_Array: TypeAlias = Union[Point3D_Array, Sequence[Point3DLike]] """``shape: (M, 3)`` An array of `Point3D` objects: ``[[float, float, float], ...]``. +This represents anything which can be converted to a :class:`Point3D_Array` +NumPy array. + Please refer to the documentation of the function you are using for further type information. """ -InternalPointND: TypeAlias = npt.NDArray[PointDType] +PointND: TypeAlias = npt.NDArray[PointDType] """``shape: (N,)`` -An N-dimensional point: ``[float, ...]``. +A NumPy array representing an N-dimensional point: ``[float, ...]``. .. note:: This type alias is mostly made available for internal use, and only includes the NumPy type. """ -PointND: TypeAlias = Union[InternalPointND, tuple[float, ...]] +PointNDLike: TypeAlias = Union[PointND, Sequence[float]] """``shape: (N,)`` An N-dimensional point: ``[float, ...]``. + +This represents anything which can be converted to a :class:`PointND` NumPy +array. """ -InternalPointND_Array: TypeAlias = npt.NDArray[PointDType] +PointND_Array: TypeAlias = npt.NDArray[PointDType] """``shape: (M, N)`` -An array of `PointND` objects: ``[[float, ...], ...]``. +A NumPy array representing a sequence of `PointND` objects: +``[[float, ...], ...]``. .. note:: This type alias is mostly made available for internal use, and only includes the NumPy type. """ -PointND_Array: TypeAlias = Union[InternalPointND_Array, tuple[PointND, ...]] +PointNDLike_Array: TypeAlias = Union[PointND_Array, Sequence[PointNDLike]] """``shape: (M, N)`` An array of `PointND` objects: ``[[float, ...], ...]``. +This represents anything which can be converted to a :class:`PointND_Array` +NumPy array. + Please refer to the documentation of the function you are using for further type information. """ @@ -494,23 +524,44 @@ Bézier types """ -QuadraticBezierPoints: TypeAlias = Union[ - npt.NDArray[PointDType], tuple[Point3D, Point3D, Point3D] +QuadraticBezierPoints: TypeAlias = Point3D_Array +"""``shape: (3, 3)`` + +A `Point3D_Array` of three 3D control points for a single quadratic Bézier +curve: +``[[float, float, float], [float, float, float], [float, float, float]]``. +""" + +QuadraticBezierPointsLike: TypeAlias = Union[ + QuadraticBezierPoints, tuple[Point3DLike, Point3DLike, Point3DLike] ] """``shape: (3, 3)`` -A `Point3D_Array` of 3 control points for a single quadratic Bézier +A `Point3DLike_Array` of three 3D control points for a single quadratic Bézier curve: ``[[float, float, float], [float, float, float], [float, float, float]]``. + +This represents anything which can be converted to a +:class:`QuadraticBezierPoints` NumPy array. +""" + +QuadraticBezierPoints_Array: TypeAlias = npt.NDArray[PointDType] +"""``shape: (N, 3, 3)`` + +A NumPy array containing :math:`N` `QuadraticBezierPoints` objects: +``[[[float, float, float], [float, float, float], [float, float, float]], ...]``. """ -QuadraticBezierPoints_Array: TypeAlias = Union[ - npt.NDArray[PointDType], tuple[QuadraticBezierPoints, ...] +QuadraticBezierPointsLike_Array: TypeAlias = Union[ + QuadraticBezierPoints_Array, Sequence[QuadraticBezierPointsLike] ] """``shape: (N, 3, 3)`` -An array of :math:`N` `QuadraticBezierPoints` objects: +A sequence of :math:`N` `QuadraticBezierPointsLike` objects: ``[[[float, float, float], [float, float, float], [float, float, float]], ...]``. + +This represents anything which can be converted to a +:class:`QuadraticBezierPoints_Array` NumPy array. """ QuadraticBezierPath: TypeAlias = Point3D_Array @@ -525,6 +576,21 @@ further type information. """ +QuadraticBezierPathLike: TypeAlias = Point3DLike_Array +"""``shape: (3*N, 3)`` + +A `Point3DLike_Array` of :math:`3N` points, where each one of the +:math:`N` consecutive blocks of 3 points represents a quadratic +Bézier curve: +``[[float, float, float], ...], ...]``. + +This represents anything which can be converted to a +:class:`QuadraticBezierPath` NumPy array. + +Please refer to the documentation of the function you are using for +further type information. +""" + QuadraticSpline: TypeAlias = QuadraticBezierPath """``shape: (3*N, 3)`` @@ -536,23 +602,56 @@ further type information. """ -CubicBezierPoints: TypeAlias = Union[ - npt.NDArray[PointDType], tuple[Point3D, Point3D, Point3D, Point3D] +QuadraticSplineLike: TypeAlias = QuadraticBezierPathLike +"""``shape: (3*N, 3)`` + +A special case of `QuadraticBezierPathLike` where all the :math:`N` +quadratic Bézier curves are connected, forming a quadratic spline: +``[[float, float, float], ...], ...]``. + +This represents anything which can be converted to a :class:`QuadraticSpline` +NumPy array. + +Please refer to the documentation of the function you are using for +further type information. +""" + +CubicBezierPoints: TypeAlias = Point3D_Array +"""``shape: (4, 3)`` + +A `Point3D_Array` of four 3D control points for a single cubic Bézier curve: +``[[float, float, float], [float, float, float], [float, float, float], [float, float, float]]``. +""" + +CubicBezierPointsLike: TypeAlias = Union[ + CubicBezierPoints, tuple[Point3DLike, Point3DLike, Point3DLike, Point3DLike] ] """``shape: (4, 3)`` -A `Point3D_Array` of 4 control points for a single cubic Bézier -curve: +A `Point3DLike_Array` of 4 control points for a single cubic Bézier curve: ``[[float, float, float], [float, float, float], [float, float, float], [float, float, float]]``. + +This represents anything which can be converted to a :class:`CubicBezierPoints` +NumPy array. +""" + +CubicBezierPoints_Array: TypeAlias = npt.NDArray[PointDType] +"""``shape: (N, 4, 3)`` + +A NumPy array containing :math:`N` `CubicBezierPoints` objects: +``[[[float, float, float], [float, float, float], [float, float, float], [float, float, float]], ...]``. """ -CubicBezierPoints_Array: TypeAlias = Union[ - npt.NDArray[PointDType], tuple[CubicBezierPoints, ...] +CubicBezierPointsLike_Array: TypeAlias = Union[ + CubicBezierPoints_Array, Sequence[CubicBezierPointsLike] ] """``shape: (N, 4, 3)`` -An array of :math:`N` `CubicBezierPoints` objects: +A sequence of :math:`N` `CubicBezierPointsLike` objects: ``[[[float, float, float], [float, float, float], [float, float, float], [float, float, float]], ...]``. + +This represents anything which can be converted to a +:class:`CubicBezierPoints_Array` NumPy array. """ CubicBezierPath: TypeAlias = Point3D_Array @@ -567,6 +666,21 @@ further type information. """ +CubicBezierPathLike: TypeAlias = Point3DLike_Array +"""``shape: (4*N, 3)`` + +A `Point3DLike_Array` of :math:`4N` points, where each one of the +:math:`N` consecutive blocks of 4 points represents a cubic Bézier +curve: +``[[float, float, float], ...], ...]``. + +This represents anything which can be converted to a +:class:`CubicBezierPath` NumPy array. + +Please refer to the documentation of the function you are using for +further type information. +""" + CubicSpline: TypeAlias = CubicBezierPath """``shape: (4*N, 3)`` @@ -578,6 +692,20 @@ further type information. """ +CubicSplineLike: TypeAlias = CubicBezierPathLike +"""``shape: (4*N, 3)`` + +A special case of `CubicBezierPath` where all the :math:`N` cubic +Bézier curves are connected, forming a quadratic spline: +``[[float, float, float], ...], ...]``. + +This represents anything which can be converted to a +:class:`CubicSpline` NumPy array. + +Please refer to the documentation of the function you are using for +further type information. +""" + BezierPoints: TypeAlias = Point3D_Array r"""``shape: (PPC, 3)`` @@ -590,10 +718,25 @@ further type information. """ -BezierPoints_Array: TypeAlias = Union[npt.NDArray[PointDType], tuple[BezierPoints, ...]] +BezierPointsLike: TypeAlias = Point3DLike_Array +r"""``shape: (PPC, 3)`` + +A `Point3DLike_Array` of :math:`\text{PPC}` control points +(:math:`\text{PPC: Points Per Curve} = n + 1`) for a single +:math:`n`-th degree Bézier curve: +``[[float, float, float], ...]``. + +This represents anything which can be converted to a +:class:`BezierPoints` NumPy array. + +Please refer to the documentation of the function you are using for +further type information. +""" + +BezierPoints_Array: TypeAlias = npt.NDArray[PointDType] r"""``shape: (N, PPC, 3)`` -An array of :math:`N` `BezierPoints` objects containing +A NumPy array of :math:`N` `BezierPoints` objects containing :math:`\text{PPC}` `Point3D` objects each (:math:`\text{PPC: Points Per Curve} = n + 1`): ``[[[float, float, float], ...], ...]``. @@ -602,6 +745,23 @@ further type information. """ +BezierPointsLike_Array: TypeAlias = Union[ + BezierPoints_Array, Sequence[BezierPointsLike] +] +r"""``shape: (N, PPC, 3)`` + +A sequence of :math:`N` `BezierPointsLike` objects containing +:math:`\text{PPC}` `Point3DLike` objects each +(:math:`\text{PPC: Points Per Curve} = n + 1`): +``[[[float, float, float], ...], ...]``. + +This represents anything which can be converted to a +:class:`BezierPoints_Array` NumPy array. + +Please refer to the documentation of the function you are using for +further type information. +""" + BezierPath: TypeAlias = Point3D_Array r"""``shape: (PPC*N, 3)`` @@ -615,6 +775,22 @@ further type information. """ +BezierPathLike: TypeAlias = Point3DLike_Array +r"""``shape: (PPC*N, 3)`` + +A `Point3DLike_Array` of :math:`\text{PPC} \cdot N` points, where each +one of the :math:`N` consecutive blocks of :math:`\text{PPC}` control +points (:math:`\text{PPC: Points Per Curve} = n + 1`) represents a +Bézier curve of :math:`n`-th degree: +``[[float, float, float], ...], ...]``. + +This represents anything which can be converted to a +:class:`BezierPath` NumPy array. + +Please refer to the documentation of the function you are using for +further type information. +""" + Spline: TypeAlias = BezierPath r"""``shape: (PPC*N, 3)`` @@ -628,6 +804,22 @@ further type information. """ +SplineLike: TypeAlias = BezierPathLike +r"""``shape: (PPC*N, 3)`` + +A special case of `BezierPathLike` where all the :math:`N` Bézier curves +consisting of :math:`\text{PPC}` `Point3D` objects +(:math:`\text{PPC: Points Per Curve} = n + 1`) are connected, forming +an :math:`n`-th degree spline: +``[[float, float, float], ...], ...]``. + +This represents anything which can be converted to a +:class:`Spline` NumPy array. + +Please refer to the documentation of the function you are using for +further type information. +""" + FlatBezierPoints: TypeAlias = Union[npt.NDArray[PointDType], tuple[float, ...]] """``shape: (3*PPC*N,)`` @@ -651,12 +843,12 @@ :class:`~.Mobject`. """ -PathFuncType: TypeAlias = Callable[[Point3D, Point3D, float], Point3D] +PathFuncType: TypeAlias = Callable[[Point3DLike, Point3DLike, float], Point3DLike] """Function mapping two `Point3D` objects and an alpha value to a new `Point3D`. """ -MappingFunction: TypeAlias = Callable[[Point3D], Point3D] +MappingFunction: TypeAlias = Callable[[Point3DLike], Point3DLike] """A function mapping a `Point3D` to another `Point3D`.""" diff --git a/manim/utils/bezier.py b/manim/utils/bezier.py index a424b617e1..61278c987f 100644 --- a/manim/utils/bezier.py +++ b/manim/utils/bezier.py @@ -22,7 +22,7 @@ from collections.abc import Sequence from functools import reduce -from typing import TYPE_CHECKING, Any, Callable, overload +from typing import TYPE_CHECKING, Callable, overload import numpy as np @@ -32,13 +32,20 @@ import numpy.typing as npt from manim.typing import ( + BezierPath, BezierPoints, BezierPoints_Array, + BezierPointsLike, + BezierPointsLike_Array, ColVector, + ManimFloat, MatrixMN, Point3D, Point3D_Array, - QuadraticBezierPoints_Array, + Point3DLike, + Point3DLike_Array, + QuadraticBezierPath, + QuadraticSpline, ) # l is a commonly used name in linear algebra @@ -47,13 +54,13 @@ @overload def bezier( - points: BezierPoints, + points: BezierPointsLike, ) -> Callable[[float | ColVector], Point3D | Point3D_Array]: ... @overload def bezier( - points: Sequence[Point3D_Array], + points: Sequence[Point3DLike_Array], ) -> Callable[[float | ColVector], Point3D_Array]: ... @@ -168,7 +175,7 @@ def nth_grade_bezier(t): return nth_grade_bezier -def partial_bezier_points(points: BezierPoints, a: float, b: float) -> BezierPoints: +def partial_bezier_points(points: BezierPointsLike, a: float, b: float) -> BezierPoints: r"""Given an array of ``points`` which define a Bézier curve, and two numbers :math:`a, b` such that :math:`0 \le a < b \le 1`, return an array of the same size, which describes the portion of the original Bézier curve on the interval :math:`[a, b]`. @@ -385,7 +392,7 @@ def partial_bezier_points(points: BezierPoints, a: float, b: float) -> BezierPoi return arr -def split_bezier(points: BezierPoints, t: float) -> Point3D_Array: +def split_bezier(points: BezierPointsLike, t: float) -> BezierPath: r"""Split a Bézier curve at argument ``t`` into two curves. .. note:: @@ -700,7 +707,7 @@ def split_bezier(points: BezierPoints, t: float) -> Point3D_Array: # Memos explained in subdivide_bezier docstring -SUBDIVISION_MATRICES = [{} for i in range(4)] +SUBDIVISION_MATRICES: list[dict[int, MatrixMN]] = [{} for i in range(4)] def _get_subdivision_matrix(n_points: int, n_divisions: int) -> MatrixMN: @@ -812,7 +819,7 @@ def _get_subdivision_matrix(n_points: int, n_divisions: int) -> MatrixMN: return subdivision_matrix -def subdivide_bezier(points: BezierPoints, n_divisions: int) -> Point3D_Array: +def subdivide_bezier(points: BezierPointsLike, n_divisions: int) -> BezierPath: r"""Subdivide a Bézier curve into :math:`n` subcurves which have the same shape. The points at which the curve is split are located at the @@ -904,7 +911,7 @@ def subdivide_bezier(points: BezierPoints, n_divisions: int) -> Point3D_Array: Returns ------- - :class:`~.Point3D_Array` + :class:`~.BezierPath` An array containing the points defining the new :math:`n` subcurves. """ if n_divisions == 1: @@ -942,7 +949,7 @@ def subdivide_bezier(points: BezierPoints, n_divisions: int) -> Point3D_Array: def bezier_remap( - bezier_tuples: BezierPoints_Array, + bezier_tuples: BezierPointsLike_Array, new_number_of_curves: int, ) -> BezierPoints_Array: """Subdivides each curve in ``bezier_tuples`` into as many parts as necessary, until the final number of @@ -1012,11 +1019,13 @@ def interpolate(start: float, end: float, alpha: ColVector) -> ColVector: ... @overload -def interpolate(start: Point3D, end: Point3D, alpha: float) -> Point3D: ... +def interpolate(start: Point3DLike, end: Point3DLike, alpha: float) -> Point3D: ... @overload -def interpolate(start: Point3D, end: Point3D, alpha: ColVector) -> Point3D_Array: ... +def interpolate( + start: Point3DLike, end: Point3DLike, alpha: ColVector +) -> Point3D_Array: ... def interpolate(start, end, alpha): @@ -1099,10 +1108,10 @@ def mid(start: float, end: float) -> float: ... @overload -def mid(start: Point3D, end: Point3D) -> Point3D: ... +def mid(start: Point3DLike, end: Point3DLike) -> Point3D: ... -def mid(start: float | Point3D, end: float | Point3D) -> float | Point3D: +def mid(start: float | Point3DLike, end: float | Point3DLike) -> float | Point3D: """Returns the midpoint between two values. Parameters @@ -1124,15 +1133,17 @@ def inverse_interpolate(start: float, end: float, value: float) -> float: ... @overload -def inverse_interpolate(start: float, end: float, value: Point3D) -> Point3D: ... +def inverse_interpolate(start: float, end: float, value: Point3DLike) -> Point3D: ... @overload -def inverse_interpolate(start: Point3D, end: Point3D, value: Point3D) -> Point3D: ... +def inverse_interpolate( + start: Point3DLike, end: Point3DLike, value: Point3DLike +) -> Point3D: ... def inverse_interpolate( - start: float | Point3D, end: float | Point3D, value: float | Point3D + start: float | Point3DLike, end: float | Point3DLike, value: float | Point3DLike ) -> float | Point3D: """Perform inverse interpolation to determine the alpha values that would produce the specified ``value`` @@ -1186,7 +1197,7 @@ def match_interpolate( new_end: float, old_start: float, old_end: float, - old_value: Point3D, + old_value: Point3DLike, ) -> Point3D: ... @@ -1195,7 +1206,7 @@ def match_interpolate( new_end: float, old_start: float, old_end: float, - old_value: float | Point3D, + old_value: float | Point3DLike, ) -> float | Point3D: """Interpolate a value from an old range to a new range. @@ -1233,7 +1244,7 @@ def match_interpolate( # Figuring out which Bézier curves most smoothly connect a sequence of points def get_smooth_cubic_bezier_handle_points( - anchors: Point3D_Array, + anchors: Point3DLike_Array, ) -> tuple[Point3D_Array, Point3D_Array]: """Given an array of anchors for a cubic spline (array of connected cubic Bézier curves), compute the 1st and 2nd handle for every curve, so that @@ -1279,7 +1290,7 @@ def get_smooth_cubic_bezier_handle_points( def get_smooth_closed_cubic_bezier_handle_points( - anchors: Point3D_Array, + anchors: Point3DLike_Array, ) -> tuple[Point3D_Array, Point3D_Array]: r"""Special case of :func:`get_smooth_cubic_bezier_handle_points`, when the ``anchors`` form a closed loop. @@ -1571,7 +1582,7 @@ def get_smooth_closed_cubic_bezier_handle_points( def get_smooth_open_cubic_bezier_handle_points( - anchors: Point3D_Array, + anchors: Point3DLike_Array, ) -> tuple[Point3D_Array, Point3D_Array]: r"""Special case of :func:`get_smooth_cubic_bezier_handle_points`, when the ``anchors`` do not form a closed loop. @@ -1725,17 +1736,17 @@ def get_smooth_open_cubic_bezier_handle_points( @overload def get_quadratic_approximation_of_cubic( - a0: Point3D, h0: Point3D, h1: Point3D, a1: Point3D -) -> QuadraticBezierPoints_Array: ... + a0: Point3DLike, h0: Point3DLike, h1: Point3DLike, a1: Point3DLike +) -> QuadraticSpline: ... @overload def get_quadratic_approximation_of_cubic( - a0: Point3D_Array, - h0: Point3D_Array, - h1: Point3D_Array, - a1: Point3D_Array, -) -> QuadraticBezierPoints_Array: ... + a0: Point3DLike_Array, + h0: Point3DLike_Array, + h1: Point3DLike_Array, + a1: Point3DLike_Array, +) -> QuadraticBezierPath: ... def get_quadratic_approximation_of_cubic(a0, h0, h1, a1): @@ -1868,7 +1879,7 @@ def get_quadratic_approximation_of_cubic(a0, h0, h1, a1): return result -def is_closed(points: Point3D_Array) -> bool: +def is_closed(points: Point3DLike_Array) -> bool: """Returns ``True`` if the spline given by ``points`` is closed, by checking if its first and last points are close to each other, or``False`` otherwise. @@ -1942,10 +1953,10 @@ def is_closed(points: Point3D_Array) -> bool: def proportions_along_bezier_curve_for_point( - point: Point3D, - control_points: BezierPoints, + point: Point3DLike, + control_points: BezierPointsLike, round_to: float = 1e-6, -) -> npt.NDArray[Any]: +) -> npt.NDArray[ManimFloat]: """Obtains the proportion along the bezier curve corresponding to a given point given the bezier curve's control points. @@ -2028,8 +2039,8 @@ def proportions_along_bezier_curve_for_point( def point_lies_on_bezier( - point: Point3D, - control_points: BezierPoints, + point: Point3DLike, + control_points: BezierPointsLike, round_to: float = 1e-6, ) -> bool: """Checks if a given point lies on the bezier curves with the given control points. diff --git a/manim/utils/space_ops.py b/manim/utils/space_ops.py index 0017a53ef7..21cd4ae735 100644 --- a/manim/utils/space_ops.py +++ b/manim/utils/space_ops.py @@ -18,10 +18,12 @@ from manim.typing import ( ManimFloat, + Point3D, Point3D_Array, Vector2D, Vector2D_Array, Vector3D, + Vector3D_Array, ) __all__ = [ @@ -573,12 +575,12 @@ def line_intersection( def find_intersection( - p0s: Sequence[np.ndarray] | Point3D_Array, - v0s: Sequence[np.ndarray] | Point3D_Array, - p1s: Sequence[np.ndarray] | Point3D_Array, - v1s: Sequence[np.ndarray] | Point3D_Array, + p0s: Point3D_Array, + v0s: Vector3D_Array, + p1s: Point3D_Array, + v1s: Vector3D_Array, threshold: float = 1e-5, -) -> Sequence[np.ndarray]: +) -> list[Point3D]: """ Return the intersection of a line passing through p0 in direction v0 with one passing through p1 in direction v1 (or array of intersections From ebb077d67fbcc6eb2003b7bf7687388dbdd32ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Thu, 28 Nov 2024 17:35:05 -0300 Subject: [PATCH 02/15] Modify some type alias docstrings --- manim/typing.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/manim/typing.py b/manim/typing.py index 99678f9928..448d1d7fea 100644 --- a/manim/typing.py +++ b/manim/typing.py @@ -321,10 +321,6 @@ A NumPy array representing a sequence of `Point2D` objects: ``[[float, float], ...]``. - -.. note:: - This type alias is mostly made available for internal use, and - only includes the NumPy type. """ Point2DLike_Array: TypeAlias = Union[Point2D_Array, Sequence[Point2DLike]] @@ -346,10 +342,6 @@ """``shape: (3,)`` A NumPy array representing a 3-dimensional point: ``[float, float, float]``. - -.. note:: - This type alias is mostly made available for internal use, and - only includes the NumPy type. """ Point3DLike: TypeAlias = Union[Point3D, tuple[float, float, float]] @@ -366,10 +358,6 @@ A NumPy array representing a sequence of `Point3D` objects: ``[[float, float, float], ...]``. - -.. note:: - This type alias is mostly made available for internal use, and - only includes the NumPy type. """ Point3DLike_Array: TypeAlias = Union[Point3D_Array, Sequence[Point3DLike]] @@ -388,10 +376,6 @@ """``shape: (N,)`` A NumPy array representing an N-dimensional point: ``[float, ...]``. - -.. note:: - This type alias is mostly made available for internal use, and - only includes the NumPy type. """ PointNDLike: TypeAlias = Union[PointND, Sequence[float]] @@ -408,10 +392,6 @@ A NumPy array representing a sequence of `PointND` objects: ``[[float, ...], ...]``. - -.. note:: - This type alias is mostly made available for internal use, and - only includes the NumPy type. """ PointNDLike_Array: TypeAlias = Union[PointND_Array, Sequence[PointNDLike]] From 3dc649790a97288a9fb1d7020d81f77c07aa40c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Thu, 28 Nov 2024 18:27:08 -0300 Subject: [PATCH 03/15] Details I missed --- manim/mobject/geometry/arc.py | 2 +- manim/mobject/geometry/line.py | 2 +- manim/mobject/geometry/polygram.py | 3 ++- manim/mobject/graphing/coordinate_systems.py | 11 +++++++++-- manim/mobject/graphing/functions.py | 8 ++++---- manim/mobject/mobject.py | 20 ++++++++++---------- manim/mobject/types/vectorized_mobject.py | 18 +++++++++--------- manim/utils/bezier.py | 8 ++++---- manim/utils/space_ops.py | 6 +++--- 9 files changed, 43 insertions(+), 35 deletions(-) diff --git a/manim/mobject/geometry/arc.py b/manim/mobject/geometry/arc.py index 0009b723d1..00c6885fb3 100644 --- a/manim/mobject/geometry/arc.py +++ b/manim/mobject/geometry/arc.py @@ -426,7 +426,7 @@ def get_arc_center(self, warning: bool = True) -> Point3D: self._failed_to_get_center = True return np.array(ORIGIN) - def move_arc_center_to(self, point: Point3D) -> Self: + def move_arc_center_to(self, point: Point3DLike) -> Self: self.shift(point - self.get_arc_center()) return self diff --git a/manim/mobject/geometry/line.py b/manim/mobject/geometry/line.py index d3f5576ebd..55a27e5da7 100644 --- a/manim/mobject/geometry/line.py +++ b/manim/mobject/geometry/line.py @@ -198,7 +198,7 @@ def get_unit_vector(self) -> Vector3D: def get_angle(self) -> float: return angle_of_vector(self.get_vector()) - def get_projection(self, point: Point3D) -> Vector3D: + def get_projection(self, point: Point3DLike) -> Point3D: """Returns the projection of a point onto a line. Parameters diff --git a/manim/mobject/geometry/polygram.py b/manim/mobject/geometry/polygram.py index 9242055e38..7baf6f183c 100644 --- a/manim/mobject/geometry/polygram.py +++ b/manim/mobject/geometry/polygram.py @@ -38,6 +38,7 @@ from typing_extensions import Self from manim.typing import ( + ManimFloat, Point3D, Point3D_Array, Point3DLike, @@ -121,7 +122,7 @@ def get_vertices(self) -> Point3D_Array: """ return self.get_start_anchors() - def get_vertex_groups(self) -> Point3D_Array: + def get_vertex_groups(self) -> npt.NDArray[ManimFloat]: """Gets the vertex groups of the :class:`Polygram`. Returns diff --git a/manim/mobject/graphing/coordinate_systems.py b/manim/mobject/graphing/coordinate_systems.py index 106f679873..caf2dc805c 100644 --- a/manim/mobject/graphing/coordinate_systems.py +++ b/manim/mobject/graphing/coordinate_systems.py @@ -57,7 +57,14 @@ if TYPE_CHECKING: from manim.mobject.mobject import Mobject - from manim.typing import ManimFloat, Point2D, Point2DLike, Point3DLike, Vector3D + from manim.typing import ( + ManimFloat, + Point2D, + Point2DLike, + Point3D, + Point3DLike, + Vector3D, + ) LineType = TypeVar("LineType", bound=Line) @@ -1003,7 +1010,7 @@ def input_to_graph_point( self, x: float, graph: ParametricFunction | VMobject, - ) -> np.ndarray: + ) -> Point3D: """Returns the coordinates of the point on a ``graph`` corresponding to an ``x`` value. Parameters diff --git a/manim/mobject/graphing/functions.py b/manim/mobject/graphing/functions.py index 3a8913dd60..b74129d067 100644 --- a/manim/mobject/graphing/functions.py +++ b/manim/mobject/graphing/functions.py @@ -19,7 +19,7 @@ if TYPE_CHECKING: from typing_extensions import Self - from manim.typing import Point3DLike + from manim.typing import Point3D, Point3DLike from manim.utils.color import YELLOW @@ -113,7 +113,7 @@ def __init__( use_vectorized: bool = False, **kwargs, ): - self.function = function + self.function: Callable[[float], Point3D] = lambda t: np.asarray(function(t)) if len(t_range) == 2: t_range = (*t_range, 0.01) @@ -127,10 +127,10 @@ def __init__( super().__init__(**kwargs) - def get_function(self) -> Callable[[float], Point3DLike]: + def get_function(self) -> Callable[[float], Point3D]: return self.function - def get_point_from_function(self, t: float) -> Point3DLike: + def get_point_from_function(self, t: float) -> Point3D: return self.function(t) def generate_points(self) -> Self: diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 3b9856c274..050bddf9bb 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -17,7 +17,7 @@ from collections.abc import Iterable from functools import partialmethod, reduce from pathlib import Path -from typing import TYPE_CHECKING, Callable, Literal +from typing import TYPE_CHECKING import numpy as np @@ -40,12 +40,12 @@ from ..utils.space_ops import angle_between_vectors, normalize, rotation_matrix if TYPE_CHECKING: + from typing import Any, Callable, Literal + from typing_extensions import Self, TypeAlias from manim.typing import ( FunctionOverride, - ManimFloat, - ManimInt, MappingFunction, PathFuncType, PixelArray, @@ -2029,7 +2029,7 @@ def get_num_points(self) -> int: def get_extremum_along_dim( self, points: Point3DLike_Array | None = None, dim: int = 0, key: int = 0 - ) -> np.ndarray | float: + ) -> float: np_points: Point3D_Array = ( self.get_points_defining_boundary() if points is None @@ -2150,15 +2150,15 @@ def get_coord(self, dim: int, direction: Vector3D = ORIGIN): """Meant to generalize ``get_x``, ``get_y`` and ``get_z``""" return self.get_extremum_along_dim(dim=dim, key=direction[dim]) - def get_x(self, direction: Vector3D = ORIGIN) -> ManimFloat: + def get_x(self, direction: Vector3D = ORIGIN) -> float: """Returns x Point3D of the center of the :class:`~.Mobject` as ``float``""" return self.get_coord(0, direction) - def get_y(self, direction: Vector3D = ORIGIN) -> ManimFloat: + def get_y(self, direction: Vector3D = ORIGIN) -> float: """Returns y Point3D of the center of the :class:`~.Mobject` as ``float``""" return self.get_coord(1, direction) - def get_z(self, direction: Vector3D = ORIGIN) -> ManimFloat: + def get_z(self, direction: Vector3D = ORIGIN) -> float: """Returns z Point3D of the center of the :class:`~.Mobject` as ``float``""" return self.get_coord(2, direction) @@ -2579,13 +2579,13 @@ def init_sizes(sizes, num, measures, name): def sort( self, - point_to_num_func: Callable[[Point3DLike], ManimInt] = lambda p: p[0], - submob_func: Callable[[Mobject], ManimInt] | None = None, + point_to_num_func: Callable[[Point3DLike], float] = lambda p: p[0], + submob_func: Callable[[Mobject], Any] | None = None, ) -> Self: """Sorts the list of :attr:`submobjects` by a function defined by ``submob_func``.""" if submob_func is None: - def submob_func(m: Mobject) -> ManimInt: + def submob_func(m: Mobject) -> float: return point_to_num_func(m.get_center()) self.submobjects.sort(key=submob_func) diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 66b4270571..b7d2386937 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -54,7 +54,9 @@ from typing_extensions import Self from manim.typing import ( + CubicBezierPath, CubicBezierPointsLike, + CubicSpline, ManimFloat, MappingFunction, Point2DLike, @@ -830,7 +832,7 @@ def clear_points(self) -> None: # TODO: shouldn't this return self instead of None? self.points = np.zeros((0, self.dim)) - def append_points(self, new_points: Point3D_Array) -> Self: + def append_points(self, new_points: Point3DLike_Array) -> Self: """Append the given ``new_points`` to the end of :attr:`VMobject.points`. @@ -1298,9 +1300,9 @@ def get_cubic_bezier_tuples(self) -> CubicBezierPoints_Array: def _gen_subpaths_from_points( self, - points: Point3DLike_Array, + points: CubicBezierPath, filter_func: Callable[[int], bool], - ) -> Generator[CubicSplineLike]: + ) -> Generator[CubicSpline]: """Given an array of points defining the bezier curves of the vmobject, return subpaths formed by these points. Here, Two bezier curves form a path if at least two of their anchors are evaluated True by the relation defined by filter_func. @@ -1330,9 +1332,7 @@ def _gen_subpaths_from_points( if (i2 - i1) >= nppcc ) - def get_subpaths_from_points( - self, points: CubicBezierPath - ) -> list[CubicSplineLike]: + def get_subpaths_from_points(self, points: CubicBezierPath) -> list[CubicSpline]: return list( self._gen_subpaths_from_points( points, @@ -1341,14 +1341,14 @@ def get_subpaths_from_points( ) def gen_subpaths_from_points_2d( - self, points: CubicBezierPathLike - ) -> Generator[CubicSplineLike]: + self, points: CubicBezierPath + ) -> Generator[CubicSpline]: return self._gen_subpaths_from_points( points, lambda n: not self.consider_points_equals_2d(points[n - 1], points[n]), ) - def get_subpaths(self) -> list[CubicSplineLike]: + def get_subpaths(self) -> list[CubicSpline]: """Returns subpaths formed by the curves of the VMobject. Subpaths are ranges of curves with each pair of consecutive curves having their end/start points coincident. diff --git a/manim/utils/bezier.py b/manim/utils/bezier.py index 61278c987f..81bf973147 100644 --- a/manim/utils/bezier.py +++ b/manim/utils/bezier.py @@ -32,7 +32,6 @@ import numpy.typing as npt from manim.typing import ( - BezierPath, BezierPoints, BezierPoints_Array, BezierPointsLike, @@ -46,6 +45,7 @@ Point3DLike_Array, QuadraticBezierPath, QuadraticSpline, + Spline, ) # l is a commonly used name in linear algebra @@ -392,7 +392,7 @@ def partial_bezier_points(points: BezierPointsLike, a: float, b: float) -> Bezie return arr -def split_bezier(points: BezierPointsLike, t: float) -> BezierPath: +def split_bezier(points: BezierPointsLike, t: float) -> Spline: r"""Split a Bézier curve at argument ``t`` into two curves. .. note:: @@ -819,7 +819,7 @@ def _get_subdivision_matrix(n_points: int, n_divisions: int) -> MatrixMN: return subdivision_matrix -def subdivide_bezier(points: BezierPointsLike, n_divisions: int) -> BezierPath: +def subdivide_bezier(points: BezierPointsLike, n_divisions: int) -> Spline: r"""Subdivide a Bézier curve into :math:`n` subcurves which have the same shape. The points at which the curve is split are located at the @@ -911,7 +911,7 @@ def subdivide_bezier(points: BezierPointsLike, n_divisions: int) -> BezierPath: Returns ------- - :class:`~.BezierPath` + :class:`~.Spline` An array containing the points defining the new :math:`n` subcurves. """ if n_divisions == 1: diff --git a/manim/utils/space_ops.py b/manim/utils/space_ops.py index 21cd4ae735..7a340c8091 100644 --- a/manim/utils/space_ops.py +++ b/manim/utils/space_ops.py @@ -19,7 +19,7 @@ from manim.typing import ( ManimFloat, Point3D, - Point3D_Array, + Point3DLike_Array, Vector2D, Vector2D_Array, Vector3D, @@ -575,9 +575,9 @@ def line_intersection( def find_intersection( - p0s: Point3D_Array, + p0s: Point3DLike_Array, v0s: Vector3D_Array, - p1s: Point3D_Array, + p1s: Point3DLike_Array, v1s: Vector3D_Array, threshold: float = 1e-5, ) -> list[Point3D]: From b8b46eda98746e4e04c351f5d205f6536545e633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Thu, 28 Nov 2024 18:35:16 -0300 Subject: [PATCH 04/15] Allow passing Point3DLike as arc_center to Arc --- manim/mobject/geometry/arc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manim/mobject/geometry/arc.py b/manim/mobject/geometry/arc.py index 00c6885fb3..709bbcf89a 100644 --- a/manim/mobject/geometry/arc.py +++ b/manim/mobject/geometry/arc.py @@ -320,14 +320,14 @@ def __init__( start_angle: float = 0, angle: float = TAU / 4, num_components: int = 9, - arc_center: Point3D = ORIGIN, + arc_center: Point3DLike = ORIGIN, **kwargs: Any, ): if radius is None: # apparently None is passed by ArcBetweenPoints radius = 1.0 self.radius = radius self.num_components = num_components - self.arc_center = arc_center + self.arc_center: Point3D = np.asarray(arc_center) self.start_angle = start_angle self.angle = angle self._failed_to_get_center: bool = False From 211c9e6e96822e23e3fa9ba10eeaafbaf7bcf05b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Fri, 29 Nov 2024 21:43:41 -0300 Subject: [PATCH 05/15] Other details I missed --- manim/mobject/geometry/boolean_ops.py | 5 ++-- manim/mobject/geometry/labeled.py | 6 +---- manim/mobject/types/vectorized_mobject.py | 10 ++++---- manim/utils/polylabel.py | 29 ++++++++++++++--------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/manim/mobject/geometry/boolean_ops.py b/manim/mobject/geometry/boolean_ops.py index 6b206bfc57..f02b4f7be6 100644 --- a/manim/mobject/geometry/boolean_ops.py +++ b/manim/mobject/geometry/boolean_ops.py @@ -13,10 +13,9 @@ from manim.mobject.types.vectorized_mobject import VMobject if TYPE_CHECKING: - from collections.abc import Sequence from typing import Any - from manim.typing import Point2DLike, Point3D_Array, Point3DLike + from manim.typing import Point2DLike_Array, Point3D_Array, Point3DLike_Array from ...constants import RendererType @@ -31,7 +30,7 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL): def _convert_2d_to_3d_array( self, - points: Sequence[Point2DLike | Point3DLike], + points: Point2DLike_Array | Point3DLike_Array, z_dim: float = 0.0, ) -> Point3D_Array: """Converts an iterable with coordinates in 2D to 3D by adding diff --git a/manim/mobject/geometry/labeled.py b/manim/mobject/geometry/labeled.py index 5c9734ed8c..51b74ccb44 100644 --- a/manim/mobject/geometry/labeled.py +++ b/manim/mobject/geometry/labeled.py @@ -365,11 +365,7 @@ def __init__( # Close Vertex Groups rings = [ - np.asarray( - group - if np.array_equal(group[0], group[-1]) - else list(group) + [group[0]] - ) + group if np.array_equal(group[0], group[-1]) else list(group) + [group[0]] for group in vertex_groups ] diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index b7d2386937..0b3b912c59 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -769,14 +769,14 @@ def set_shade_in_3d( submob.z_index_group = self return self - def set_points(self, points: Point3D_Array) -> Self: + def set_points(self, points: Point3DLike_Array) -> Self: self.points: Point3D_Array = np.array(points) return self def resize_points( self, new_length: int, - resize_func: Callable[[Point3DLike, int], Point3DLike] = resize_array, + resize_func: Callable[[Point3D_Array, int], Point3D_Array] = resize_array, ) -> Self: """Resize the array of anchor points and handles to have the specified size. @@ -1320,7 +1320,7 @@ def _gen_subpaths_from_points( Returns ------- - Generator[Point3D_Array] + Generator[CubicSpline] subpaths formed by the points. """ nppcc = self.n_points_per_cubic_curve @@ -1355,7 +1355,7 @@ def get_subpaths(self) -> list[CubicSpline]: Returns ------- - list[Point3D_Array] + list[CubicSpline] subpaths. """ return self.get_subpaths_from_points(self.points) @@ -1370,7 +1370,7 @@ def get_nth_curve_points(self, n: int) -> CubicBezierPoints: Returns ------- - Point3D_Array + CubicBezierPoints points defining the nth bezier curve (anchors, handles) """ assert n < self.get_num_curves() diff --git a/manim/utils/polylabel.py b/manim/utils/polylabel.py index e7739b8dc1..376645f7ee 100644 --- a/manim/utils/polylabel.py +++ b/manim/utils/polylabel.py @@ -9,7 +9,13 @@ if TYPE_CHECKING: from collections.abc import Sequence - from manim.typing import Point3D, Point3D_Array + from manim.typing import ( + Point2D, + Point2D_Array, + Point2DLike, + Point2DLike_Array, + Point3DLike_Array, + ) class Polygon: @@ -22,10 +28,11 @@ class Polygon: A collection of closed polygonal ring. """ - def __init__(self, rings: Sequence[Point3D_Array]) -> None: + def __init__(self, rings: Sequence[Point2DLike_Array]) -> None: + np_rings: list[Point2D_Array] = [np.asarray(ring) for ring in rings] # Flatten Array - csum = np.cumsum([ring.shape[0] for ring in rings]) - self.array = np.concatenate(rings, axis=0) + csum = np.cumsum([ring.shape[0] for ring in np_rings]) + self.array: Point2D_Array = np.concatenate(np_rings, axis=0) # Compute Boundary self.start = np.delete(self.array, csum - 1, axis=0) @@ -45,14 +52,14 @@ def __init__(self, rings: Sequence[Point3D_Array]) -> None: cy = np.sum((y + yr) * factor) / (6.0 * self.area) self.centroid = np.array([cx, cy]) - def compute_distance(self, point: Point3D) -> float: + def compute_distance(self, point: Point2DLike) -> float: """Compute the minimum distance from a point to the polygon.""" scalars = np.einsum("ij,ij->i", self.norm, point - self.start) clips = np.clip(scalars, 0, 1).reshape(-1, 1) d = np.min(np.linalg.norm(self.start + self.diff * clips - point, axis=1)) return d if self.inside(point) else -d - def inside(self, point: Point3D) -> bool: + def inside(self, point: Point2DLike) -> bool: """Check if a point is inside the polygon.""" # Views px, py = point @@ -79,8 +86,8 @@ class Cell: :class:`~.Polygon` object for which the distance is computed. """ - def __init__(self, c: Point3D, h: float, polygon: Polygon) -> None: - self.c = c + def __init__(self, c: Point2DLike, h: float, polygon: Polygon) -> None: + self.c: Point2D = np.asarray(c) self.h = h self.d = polygon.compute_distance(self.c) self.p = self.d + self.h * np.sqrt(2) @@ -98,7 +105,7 @@ def __ge__(self, other: Cell) -> bool: return self.d >= other.d -def polylabel(rings: Sequence[Point3D_Array], precision: float = 0.01) -> Cell: +def polylabel(rings: Sequence[Point3DLike_Array], precision: float = 0.01) -> Cell: """ Finds the pole of inaccessibility (the point that is farthest from the edges of the polygon) using an iterative grid-based approach. @@ -117,8 +124,8 @@ def polylabel(rings: Sequence[Point3D_Array], precision: float = 0.01) -> Cell: A Cell containing the pole of inaccessibility to a given precision. """ # Precompute Polygon Data - array = [np.array(ring)[:, :2] for ring in rings] - polygon = Polygon(array) + np_rings: list[Point2D_Array] = [np.asarray(ring)[:, :2] for ring in rings] + polygon = Polygon(np_rings) # Bounding Box mins = np.min(polygon.array, axis=0) From e4266dc010f55f73afb05cb744b94471b50ebc70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Sat, 30 Nov 2024 19:21:27 -0300 Subject: [PATCH 06/15] Update docs/source/contributing/docs/types.rst --- docs/source/contributing/docs/types.rst | 44 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/docs/source/contributing/docs/types.rst b/docs/source/contributing/docs/types.rst index 63e5083579..3e40124d80 100644 --- a/docs/source/contributing/docs/types.rst +++ b/docs/source/contributing/docs/types.rst @@ -22,32 +22,46 @@ in space. For example: .. code-block:: python - def status2D(coord: Point2D) -> None: + def print_point2D(coord: Point2DLike) -> None: x, y = coord print(f"Point at {x=},{y=}") - def status3D(coord: Point3D) -> None: + def print_point3D(coord: Point3DLike) -> None: x, y, z = coord print(f"Point at {x=},{y=},{z=}") - def get_statuses(coords: Point2D_Array | Point3D_Array) -> None: + def print_point_array(coords: Point2DLike_Array | Point3DLike_Array) -> None: for coord in coords: if len(coord) == 2: - # it's a Point2D - status2D(coord) + # it's a Point2DLike + print_point2D(coord) else: - # it's a point3D - status3D(coord) - -It's important to realize that the status functions accepted both -tuples/lists of the correct length, and ``NDArray``'s of the correct shape. -If they only accepted ``NDArray``'s, we would use their ``Internal`` counterparts: -:class:`~.typing.InternalPoint2D`, :class:`~.typing.InternalPoint3D`, :class:`~.typing.InternalPoint2D_Array` and :class:`~.typing.InternalPoint3D_Array`. - -In general, the type aliases prefixed with ``Internal`` should never be used on -user-facing classes and functions, but should be reserved for internal behavior. + # it's a Point3DLike + print_point3D(coord) + + def shift_point_up(coord: Point3DLike) -> Point3D: + result = np.asarray(coord) + result += UP + return result + +Notice that the last function, ``shift_point_up()``, accepts a +:class:`~.Point3DLike` as a parameter and returns a :class:`~.Point3D`. A +:class:`~.Point3D` always represents a NumPy array consisting of 3 floats, +whereas a :class:`~.Point3DLike` can represent anything resembling a 3D point: +either a NumPy array or a tuple/list of 3 floats, hence the ``Like`` word. The +same happens with :class:`~.Point2D`, :class:`~.Point2D_Array` and +:class:`~.Point3D_Array`, and their ``Like`` counterparts +:class:`~.Point2DLike`, :class:`~.Point2DLike_Array` and +:class:`~.Point3DLike_Array`. + +The rule for typing functions is: **make parameter types as broad as possible, +and return types as specific as possible.** Therefore, for functions which are +intended to be called by users, we should always, if possible, accept ``Like`` +types as parameters and return NumPy, non-``Like`` types. The last function, +``shift_point_up()``, is an example of it. Internal functions which are *not* +meant to be called by users may accept non-``Like`` parameters if necessary. Vectors ~~~~~~~ From 4673f73cb7235419b3fd4f73be7a8679686bf972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Sat, 30 Nov 2024 19:57:37 -0300 Subject: [PATCH 07/15] Update types.rst again --- docs/source/contributing/docs/types.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/source/contributing/docs/types.rst b/docs/source/contributing/docs/types.rst index 3e40124d80..079950b70d 100644 --- a/docs/source/contributing/docs/types.rst +++ b/docs/source/contributing/docs/types.rst @@ -58,10 +58,14 @@ same happens with :class:`~.Point2D`, :class:`~.Point2D_Array` and The rule for typing functions is: **make parameter types as broad as possible, and return types as specific as possible.** Therefore, for functions which are -intended to be called by users, we should always, if possible, accept ``Like`` -types as parameters and return NumPy, non-``Like`` types. The last function, -``shift_point_up()``, is an example of it. Internal functions which are *not* -meant to be called by users may accept non-``Like`` parameters if necessary. +intended to be called by users, **we should always, if possible, accept ``Like`` +types as parameters and return NumPy, non-``Like`` types.** The main reason is to +be more flexible with users who might want to pass tuples or lists as arguments +rather than NumPy arrays, because it's more convenient. The last function, +``shift_point_up()``, is an example of it. + +Internal functions which are *not* meant to be called by users may accept +non-``Like`` parameters if necessary. Vectors ~~~~~~~ From 89dc02b44c75ba46689f5a089d960a8288e1fd7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Sat, 30 Nov 2024 19:59:46 -0300 Subject: [PATCH 08/15] Update types.rst once again, add print to shift_point_up() --- docs/source/contributing/docs/types.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/source/contributing/docs/types.rst b/docs/source/contributing/docs/types.rst index 079950b70d..84cd5139a5 100644 --- a/docs/source/contributing/docs/types.rst +++ b/docs/source/contributing/docs/types.rst @@ -44,6 +44,7 @@ in space. For example: def shift_point_up(coord: Point3DLike) -> Point3D: result = np.asarray(coord) result += UP + print(f"New point: {result}") return result Notice that the last function, ``shift_point_up()``, accepts a @@ -58,11 +59,11 @@ same happens with :class:`~.Point2D`, :class:`~.Point2D_Array` and The rule for typing functions is: **make parameter types as broad as possible, and return types as specific as possible.** Therefore, for functions which are -intended to be called by users, **we should always, if possible, accept ``Like`` -types as parameters and return NumPy, non-``Like`` types.** The main reason is to -be more flexible with users who might want to pass tuples or lists as arguments -rather than NumPy arrays, because it's more convenient. The last function, -``shift_point_up()``, is an example of it. +intended to be called by users, **we should always, if possible, accept +``Like`` types as parameters and return NumPy, non-``Like`` types.** The main +reason is to be more flexible with users who might want to pass tuples or lists +as arguments rather than NumPy arrays, because it's more convenient. The last +function, ``shift_point_up()``, is an example of it. Internal functions which are *not* meant to be called by users may accept non-``Like`` parameters if necessary. From 68fddd4dfa6cb70051446ae57fc79f1d4b7a15ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Sat, 30 Nov 2024 20:24:55 -0300 Subject: [PATCH 09/15] Update Vectors section in types.rst and revert Vector3D to Point3DLike in Vector.__init__() --- docs/source/contributing/docs/types.rst | 13 ++++++------- manim/mobject/geometry/line.py | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/source/contributing/docs/types.rst b/docs/source/contributing/docs/types.rst index 84cd5139a5..6cf72faa2a 100644 --- a/docs/source/contributing/docs/types.rst +++ b/docs/source/contributing/docs/types.rst @@ -80,11 +80,9 @@ consider this slightly contrived function: def shift_mobject(mob: M, direction: Vector3D, scale_factor: float = 1) -> M: return mob.shift(direction * scale_factor) -Here we see an important example of the difference. ``direction`` can not, and -should not, be typed as a :class:`~.typing.Point3D` because the function does not accept tuples/lists, -like ``direction=(0, 1, 0)``. You could type it as :class:`~.typing.InternalPoint3D` and -the type checker and linter would be happy; however, this makes the code harder -to understand. +Here we see an important example of the difference. ``direction`` should not be +typed as a :class:`~.Point3D`, because it represents a direction along +which to shift a :class:`~.Mobject`, not a position in space. As a general rule, if a parameter is called ``direction`` or ``axis``, it should be type hinted as some form of :class:`~.VectorND`. @@ -92,8 +90,9 @@ it should be type hinted as some form of :class:`~.VectorND`. .. warning:: This is not always true. For example, as of Manim 0.18.0, the direction - parameter of the :class:`.Vector` Mobject should be ``Point2D | Point3D``, - as it can also accept ``tuple[float, float]`` and ``tuple[float, float, float]``. + parameter of the :class:`.Vector` Mobject should be + ``Point2DLike | Point3DLike``, as it can also accept ``tuple[float, float]`` + and ``tuple[float, float, float]``. Colors ------ diff --git a/manim/mobject/geometry/line.py b/manim/mobject/geometry/line.py index 55a27e5da7..8458eff896 100644 --- a/manim/mobject/geometry/line.py +++ b/manim/mobject/geometry/line.py @@ -34,7 +34,7 @@ from typing_extensions import Literal, Self - from manim.typing import Point3D, Point3DLike, Vector2D, Vector3D + from manim.typing import Point2DLike, Point3D, Point3DLike, Vector3D from manim.utils.color import ParsableManimColor from ..matrix import Matrix # Avoid circular import @@ -692,7 +692,7 @@ def construct(self): def __init__( self, - direction: Vector2D | Vector3D = RIGHT, + direction: Point2DLike | Point3DLike = RIGHT, buff: float = 0, **kwargs: Any, ) -> None: From 2d0917a65182e22e09cdbb14d95e7e5fdc172bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Sat, 30 Nov 2024 20:41:07 -0300 Subject: [PATCH 10/15] Fix words in double backticks not rendering correctly --- docs/source/contributing/docs/types.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/contributing/docs/types.rst b/docs/source/contributing/docs/types.rst index 6cf72faa2a..840d29e6c0 100644 --- a/docs/source/contributing/docs/types.rst +++ b/docs/source/contributing/docs/types.rst @@ -59,11 +59,11 @@ same happens with :class:`~.Point2D`, :class:`~.Point2D_Array` and The rule for typing functions is: **make parameter types as broad as possible, and return types as specific as possible.** Therefore, for functions which are -intended to be called by users, **we should always, if possible, accept -``Like`` types as parameters and return NumPy, non-``Like`` types.** The main -reason is to be more flexible with users who might want to pass tuples or lists -as arguments rather than NumPy arrays, because it's more convenient. The last -function, ``shift_point_up()``, is an example of it. +intended to be called by users, **we should always, if possible, accept** +``Like`` **types as parameters and return NumPy, non-** ``Like`` **types.** The +main reason is to be more flexible with users who might want to pass tuples or +lists as arguments rather than NumPy arrays, because it's more convenient. The +last function, ``shift_point_up()``, is an example of it. Internal functions which are *not* meant to be called by users may accept non-``Like`` parameters if necessary. From 6ca5daf7c12b88415b8687d14f154a2a9258050a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Fri, 6 Dec 2024 20:15:34 -0300 Subject: [PATCH 11/15] Use named function for ParametricFunction.function --- manim/mobject/graphing/functions.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/manim/mobject/graphing/functions.py b/manim/mobject/graphing/functions.py index b74129d067..83c48b1092 100644 --- a/manim/mobject/graphing/functions.py +++ b/manim/mobject/graphing/functions.py @@ -113,7 +113,11 @@ def __init__( use_vectorized: bool = False, **kwargs, ): - self.function: Callable[[float], Point3D] = lambda t: np.asarray(function(t)) + def internal_parametric_function(t: float) -> Point3D: + """Wrap ``function``'s output inside a NumPy array.""" + return np.asarray(function(t)) + + self.function = internal_parametric_function if len(t_range) == 2: t_range = (*t_range, 0.01) From 2956e756dd177f19302790f0d909171a11023a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Fri, 6 Dec 2024 22:52:30 -0300 Subject: [PATCH 12/15] Address requested changes --- manim/mobject/geometry/line.py | 28 ++++++++++++++++++++++++-- manim/mobject/graph.py | 2 +- manim/mobject/mobject.py | 26 +++++++++++++++--------- manim/mobject/opengl/opengl_mobject.py | 17 ++++++++++------ manim/typing.py | 13 ++++++++---- manim/utils/bezier.py | 22 +++++++++----------- manim/utils/polylabel.py | 14 +++++++------ 7 files changed, 80 insertions(+), 42 deletions(-) diff --git a/manim/mobject/geometry/line.py b/manim/mobject/geometry/line.py index 8458eff896..f61090b8de 100644 --- a/manim/mobject/geometry/line.py +++ b/manim/mobject/geometry/line.py @@ -32,13 +32,37 @@ if TYPE_CHECKING: from typing import Any - from typing_extensions import Literal, Self + from typing_extensions import Literal, Self, TypeAlias from manim.typing import Point2DLike, Point3D, Point3DLike, Vector3D from manim.utils.color import ParsableManimColor from ..matrix import Matrix # Avoid circular import + AngleQuadrant: TypeAlias = tuple[Literal[-1, 1], Literal[-1, 1]] + """A tuple of 2 integers which can be either +1 or -1, allowing to select + one of the 4 quadrants of the Cartesian plane. + + Let :math:`L_1,\ L_2` be two lines defined by start points + :math:`S_1,\ S_2` and end points :math:`E_1,\ E_2`. We define the "positive + direction" of :math:`L_1` as the direction from :math:`S_1` to :math:`E_1`, + and its "negative direction" as the opposite one. We do the same with + :math:`L_2`. + + If :math:`L_1` and :math:`L_2` intersect, they divide the plane into 4 + quadrants. To pick one quadrant, choose the integers in this tuple in the + following way: + + - If the 1st integer is +1, select one of the 2 quadrants towards the + positive direction of :math:`L_1`, i.e. closest to `E_1`. Otherwise, if + the 1st integer is -1, select one of the 2 quadrants towards the + negative direction of :math:`L_1`, i.e. closest to `S_1`. + + - Similarly, the sign of the 2nd integer picks the positive or negative + direction of :math:`L_2` and, thus, selects one of the 2 quadrants + which are closest to :math:`E_2` or :math:`S_2` respectively. + """ + class Line(TipableVMobject): def __init__( @@ -932,7 +956,7 @@ def __init__( line1: Line, line2: Line, radius: float | None = None, - quadrant: tuple[Literal[-1, 1], Literal[-1, 1]] = (1, 1), + quadrant: AngleQuadrant = (1, 1), other_angle: bool = False, dot: bool = False, dot_radius: float | None = None, diff --git a/manim/mobject/graph.py b/manim/mobject/graph.py index 1218b22c0d..1ccebd9bb0 100644 --- a/manim/mobject/graph.py +++ b/manim/mobject/graph.py @@ -446,7 +446,7 @@ def _determine_graph_layout( layout: LayoutName | dict[Hashable, Point3DLike] | LayoutFunction = "spring", layout_scale: float | tuple[float, float, float] = 2, layout_config: dict[str, Any] | None = None, -) -> dict[Hashable, Point3D]: +) -> dict[Hashable, Point3DLike]: if layout_config is None: layout_config = {} diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 12e5b15470..5ba088bd4c 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -47,6 +47,7 @@ from manim.typing import ( FunctionOverride, MappingFunction, + MultiMappingFunction, PathFuncType, PixelArray, Point3D, @@ -815,7 +816,7 @@ def depth(self, value: float): def get_array_attrs(self) -> list[Literal["points"]]: return ["points"] - def apply_over_attr_arrays(self, func: MappingFunction) -> Self: + def apply_over_attr_arrays(self, func: MultiMappingFunction) -> Self: for attr in self.get_array_attrs(): setattr(self, attr, func(getattr(self, attr))) return self @@ -1306,7 +1307,7 @@ def construct(self): return self.rotate(TAU / 2, axis, **kwargs) def stretch(self, factor: float, dim: int, **kwargs) -> Self: - def func(points): + def func(points: Point3D_Array) -> Point3D_Array: points[:, dim] *= factor return points @@ -1317,9 +1318,12 @@ def apply_function(self, function: MappingFunction, **kwargs) -> Self: # Default to applying matrix about the origin, not mobjects center if len(kwargs) == 0: kwargs["about_point"] = ORIGIN - self.apply_points_function_about_point( - lambda points: np.apply_along_axis(function, 1, points), **kwargs - ) + + 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) return self def apply_function_to_position(self, function: MappingFunction) -> Self: @@ -1398,11 +1402,12 @@ def repeat_array(array): # Note, much of these are now redundant with default behavior of # above methods + # TODO: name is inconsistent with OpenGLMobject.apply_points_function() def apply_points_function_about_point( self, - func: MappingFunction, - about_point: Point3DLike = None, - about_edge=None, + func: MultiMappingFunction, + about_point: Point3DLike | None = None, + about_edge: Vector3D | None = None, ) -> Self: if about_point is None: if about_edge is None: @@ -1771,9 +1776,10 @@ def put_start_and_end_on(self, start: Point3DLike, end: Point3DLike) -> Self: curr_start, curr_end = self.get_start_and_end() curr_vect = curr_end - curr_start if np.all(curr_vect == 0): - self.points = start + # TODO: this makes self.points contain a single point + self.points = np.array(start, ndmin=2) return self - target_vect = np.array(end) - np.array(start) + target_vect = np.asarray(end) - np.asarray(start) axis = ( normalize(np.cross(curr_vect, target_vect)) if np.linalg.norm(np.cross(curr_vect, target_vect)) != 0 diff --git a/manim/mobject/opengl/opengl_mobject.py b/manim/mobject/opengl/opengl_mobject.py index 577bae8afc..576ebe0318 100644 --- a/manim/mobject/opengl/opengl_mobject.py +++ b/manim/mobject/opengl/opengl_mobject.py @@ -55,6 +55,7 @@ ManimFloat, MappingFunction, MatrixMN, + MultiMappingFunction, PathFuncType, Point3D, Point3D_Array, @@ -627,9 +628,10 @@ def construct(self): """ return self.point_from_proportion(0.5) + # TODO: name is inconsistent with Mobject.apply_points_function_about_point() def apply_points_function( self, - func: MappingFunction, + func: MultiMappingFunction, about_point: Point3DLike | None = None, about_edge: Vector3D | None = ORIGIN, works_on_bounding_box: bool = False, @@ -1581,7 +1583,7 @@ def scale( if :math:`\alpha < 0`, the mobject is also flipped. kwargs Additional keyword arguments passed to - :meth:`apply_points_function_about_point`. + :meth:`apply_points_function`. Returns ------- @@ -1619,7 +1621,7 @@ def construct(self): return self def stretch(self, factor: float, dim: int, **kwargs) -> Self: - def func(points): + def func(points: Point3D_Array) -> Point3D_Array: points[:, dim] *= factor return points @@ -1668,9 +1670,12 @@ def apply_function(self, function: MappingFunction, **kwargs) -> Self: # Default to applying matrix about the origin, not mobjects center if len(kwargs) == 0: kwargs["about_point"] = ORIGIN - self.apply_points_function( - lambda points: np.array([function(p) for p in points]), **kwargs - ) + + 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(multi_mapping_function, **kwargs) return self def apply_function_to_position(self, function: MappingFunction) -> Self: diff --git a/manim/typing.py b/manim/typing.py index 448d1d7fea..660b4a1821 100644 --- a/manim/typing.py +++ b/manim/typing.py @@ -98,6 +98,7 @@ "FunctionOverride", "PathFuncType", "MappingFunction", + "MultiMappingFunction", "PixelArray", "GrayscalePixelArray", "RGBPixelArray", @@ -824,13 +825,17 @@ """ PathFuncType: TypeAlias = Callable[[Point3DLike, Point3DLike, float], Point3DLike] -"""Function mapping two `Point3D` objects and an alpha value to a new -`Point3D`. +"""Function mapping two :class:`Point3D` objects and an alpha value to a new +:class:`Point3D`. """ -MappingFunction: TypeAlias = Callable[[Point3DLike], Point3DLike] -"""A function mapping a `Point3D` to another `Point3D`.""" +MappingFunction: TypeAlias = Callable[[Point3D], Point3D] +"""A function mapping a :class:`Point3D` to another :class:`Point3D`.""" +MultiMappingFunction: TypeAlias = Callable[[Point3D_Array], Point3D_Array] +"""A function mapping a :class:`Point3D_Array` to another +:class:`Point3D_Array`. +""" """ [CATEGORY] diff --git a/manim/utils/bezier.py b/manim/utils/bezier.py index 34a57bb029..b089a1b034 100644 --- a/manim/utils/bezier.py +++ b/manim/utils/bezier.py @@ -1019,13 +1019,11 @@ def interpolate(start: float, end: float, alpha: ColVector) -> ColVector: ... @overload -def interpolate(start: Point3DLike, end: Point3DLike, alpha: float) -> Point3D: ... +def interpolate(start: Point3D, end: Point3D, alpha: float) -> Point3D: ... @overload -def interpolate( - start: Point3DLike, end: Point3DLike, alpha: ColVector -) -> Point3D_Array: ... +def interpolate(start: Point3D, end: Point3D, alpha: ColVector) -> Point3D_Array: ... def interpolate(start, end, alpha): @@ -1108,10 +1106,10 @@ def mid(start: float, end: float) -> float: ... @overload -def mid(start: Point3DLike, end: Point3DLike) -> Point3D: ... +def mid(start: Point3D, end: Point3D) -> Point3D: ... -def mid(start: float | Point3DLike, end: float | Point3DLike) -> float | Point3D: +def mid(start: float | Point3D, end: float | Point3D) -> float | Point3D: """Returns the midpoint between two values. Parameters @@ -1133,17 +1131,15 @@ def inverse_interpolate(start: float, end: float, value: float) -> float: ... @overload -def inverse_interpolate(start: float, end: float, value: Point3DLike) -> Point3D: ... +def inverse_interpolate(start: float, end: float, value: Point3D) -> Point3D: ... @overload -def inverse_interpolate( - start: Point3DLike, end: Point3DLike, value: Point3DLike -) -> Point3D: ... +def inverse_interpolate(start: Point3D, end: Point3D, value: Point3D) -> Point3D: ... def inverse_interpolate( - start: float | Point3DLike, end: float | Point3DLike, value: float | Point3DLike + start: float | Point3D, end: float | Point3D, value: float | Point3D ) -> float | Point3D: """Perform inverse interpolation to determine the alpha values that would produce the specified ``value`` @@ -1197,7 +1193,7 @@ def match_interpolate( new_end: float, old_start: float, old_end: float, - old_value: Point3DLike, + old_value: Point3D, ) -> Point3D: ... @@ -1206,7 +1202,7 @@ def match_interpolate( new_end: float, old_start: float, old_end: float, - old_value: float | Point3DLike, + old_value: float | Point3D, ) -> float | Point3D: """Interpolate a value from an old range to a new range. diff --git a/manim/utils/polylabel.py b/manim/utils/polylabel.py index 376645f7ee..03c87a9f63 100644 --- a/manim/utils/polylabel.py +++ b/manim/utils/polylabel.py @@ -35,17 +35,19 @@ def __init__(self, rings: Sequence[Point2DLike_Array]) -> None: self.array: Point2D_Array = np.concatenate(np_rings, axis=0) # Compute Boundary - self.start = np.delete(self.array, csum - 1, axis=0) - self.stop = np.delete(self.array, csum % csum[-1], axis=0) - self.diff = np.delete(np.diff(self.array, axis=0), csum[:-1] - 1, axis=0) - self.norm = self.diff / np.einsum("ij,ij->i", self.diff, self.diff).reshape( - -1, 1 + self.start: Point2D_Array = np.delete(self.array, csum - 1, axis=0) + self.stop: Point2D_Array = np.delete(self.array, csum % csum[-1], axis=0) + self.diff: Point2D_Array = np.delete( + np.diff(self.array, axis=0), csum[:-1] - 1, axis=0 ) + self.norm: Point2D_Array = self.diff / np.einsum( + "ij,ij->i", self.diff, self.diff + ).reshape(-1, 1) # Compute Centroid x, y = self.start[:, 0], self.start[:, 1] xr, yr = self.stop[:, 0], self.stop[:, 1] - self.area = 0.5 * (np.dot(x, yr) - np.dot(xr, y)) + self.area: float = 0.5 * (np.dot(x, yr) - np.dot(xr, y)) if self.area: factor = x * yr - xr * y cx = np.sum((x + xr) * factor) / (6.0 * self.area) From 5b5ae706fc8d83433046c6461e7c068c2d8f5dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Fri, 6 Dec 2024 23:09:49 -0300 Subject: [PATCH 13/15] Revert attempted fix in put_start_and_end_on --- manim/mobject/geometry/line.py | 2 +- manim/mobject/mobject.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/manim/mobject/geometry/line.py b/manim/mobject/geometry/line.py index f61090b8de..652204cfe8 100644 --- a/manim/mobject/geometry/line.py +++ b/manim/mobject/geometry/line.py @@ -40,7 +40,7 @@ from ..matrix import Matrix # Avoid circular import AngleQuadrant: TypeAlias = tuple[Literal[-1, 1], Literal[-1, 1]] - """A tuple of 2 integers which can be either +1 or -1, allowing to select + r"""A tuple of 2 integers which can be either +1 or -1, allowing to select one of the 4 quadrants of the Cartesian plane. Let :math:`L_1,\ L_2` be two lines defined by start points diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 5ba088bd4c..f0f9aff75c 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -1776,7 +1776,9 @@ def put_start_and_end_on(self, start: Point3DLike, end: Point3DLike) -> Self: curr_start, curr_end = self.get_start_and_end() curr_vect = curr_end - curr_start if np.all(curr_vect == 0): - # TODO: this makes self.points contain a single point + # TODO: this looks broken. It makes self.points a Point3D instead + # of a Point3D_Array. However, modifying this breaks some tests + # where this is currently expected. self.points = np.array(start, ndmin=2) return self target_vect = np.asarray(end) - np.asarray(start) From 9514d92b94e837957ee5cd55a608570f6b537e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Fri, 6 Dec 2024 23:14:31 -0300 Subject: [PATCH 14/15] Generator -> Iterable --- manim/mobject/types/vectorized_mobject.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 0b3b912c59..e03825c96f 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -14,7 +14,7 @@ import itertools as it import sys -from collections.abc import Generator, Hashable, Iterable, Mapping, Sequence +from collections.abc import Hashable, Iterable, Mapping, Sequence from typing import TYPE_CHECKING, Callable, Literal import numpy as np @@ -1302,7 +1302,7 @@ def _gen_subpaths_from_points( self, points: CubicBezierPath, filter_func: Callable[[int], bool], - ) -> Generator[CubicSpline]: + ) -> Iterable[CubicSpline]: """Given an array of points defining the bezier curves of the vmobject, return subpaths formed by these points. Here, Two bezier curves form a path if at least two of their anchors are evaluated True by the relation defined by filter_func. @@ -1320,7 +1320,7 @@ def _gen_subpaths_from_points( Returns ------- - Generator[CubicSpline] + Iterable[CubicSpline] subpaths formed by the points. """ nppcc = self.n_points_per_cubic_curve @@ -1342,7 +1342,7 @@ def get_subpaths_from_points(self, points: CubicBezierPath) -> list[CubicSpline] def gen_subpaths_from_points_2d( self, points: CubicBezierPath - ) -> Generator[CubicSpline]: + ) -> Iterable[CubicSpline]: return self._gen_subpaths_from_points( points, lambda n: not self.consider_points_equals_2d(points[n - 1], points[n]), @@ -1483,12 +1483,12 @@ def get_num_curves(self) -> int: def get_curve_functions( self, - ) -> Generator[Callable[[float], Point3D]]: + ) -> Iterable[Callable[[float], Point3D]]: """Gets the functions for the curves of the mobject. Returns ------- - Generator[Callable[[float], Point3D]] + Iterable[Callable[[float], Point3D]] The functions for the curves. """ num_curves = self.get_num_curves() @@ -1498,7 +1498,7 @@ def get_curve_functions( def get_curve_functions_with_lengths( self, **kwargs - ) -> Generator[tuple[Callable[[float], Point3D], float]]: + ) -> Iterable[tuple[Callable[[float], Point3D], float]]: """Gets the functions and lengths of the curves for the mobject. Parameters @@ -1508,7 +1508,7 @@ def get_curve_functions_with_lengths( Returns ------- - Generator[tuple[Callable[[float], Point3D], float]] + Iterable[tuple[Callable[[float], Point3D], float]] The functions and lengths of the curves. """ num_curves = self.get_num_curves() From 28b39041e1abfaeeb3bd07ccecb5f7478c373448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez?= Date: Fri, 6 Dec 2024 23:16:03 -0300 Subject: [PATCH 15/15] Actually apply reversal of previous fix --- manim/mobject/mobject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index f0f9aff75c..8557490ed9 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -1779,7 +1779,7 @@ def put_start_and_end_on(self, start: Point3DLike, end: Point3DLike) -> Self: # TODO: this looks broken. It makes self.points a Point3D instead # of a Point3D_Array. However, modifying this breaks some tests # where this is currently expected. - self.points = np.array(start, ndmin=2) + self.points = np.array(start) return self target_vect = np.asarray(end) - np.asarray(start) axis = (