Add VectorNDLike type aliases (#4068)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Francisco Manríquez Novoa 2025-08-01 00:31:30 -04:00 committed by GitHub
commit d18dc8f89b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 317 additions and 229 deletions

View file

@ -85,14 +85,8 @@ 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`.
.. warning::
This is not always true. For example, as of Manim 0.18.0, the direction
parameter of the :class:`.Vector` Mobject should be
``Point2DLike | Point3DLike``, as it can also accept ``tuple[float, float]``
and ``tuple[float, float, float]``.
it should be type hinted as some form of :class:`~.VectorND` or
:class:`~.VectorNDLike`.
Colors
------

View file

@ -74,7 +74,7 @@ if TYPE_CHECKING:
Point3D,
Point3DLike,
QuadraticSpline,
Vector3D,
Vector3DLike,
)
@ -99,12 +99,12 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
def __init__(
self,
tip_length: float = DEFAULT_ARROW_TIP_LENGTH,
normal_vector: Vector3D = OUT,
normal_vector: Vector3DLike = OUT,
tip_style: dict = {},
**kwargs: Any,
) -> None:
self.tip_length: float = tip_length
self.normal_vector: Vector3D = normal_vector
self.normal_vector = normal_vector
self.tip_style: dict = tip_style
super().__init__(**kwargs)

View file

@ -34,7 +34,7 @@ if TYPE_CHECKING:
from typing_extensions import Literal, Self, TypeAlias
from manim.typing import Point2DLike, Point3D, Point3DLike, Vector3D
from manim.typing import Point3D, Point3DLike, Vector2DLike, Vector3D, Vector3DLike
from manim.utils.color import ParsableManimColor
from ..matrix import Matrix # Avoid circular import
@ -175,7 +175,7 @@ class Line(TipableVMobject):
def _pointify(
self,
mob_or_point: Mobject | Point3DLike,
direction: Vector3D | None = None,
direction: Vector3DLike | None = None,
) -> Point3D:
"""Transforms a mobject into its corresponding point. Does nothing if a point is passed.
@ -738,7 +738,7 @@ class Vector(Arrow):
def __init__(
self,
direction: Point2DLike | Point3DLike = RIGHT,
direction: Vector2DLike | Vector3DLike = RIGHT,
buff: float = 0,
**kwargs: Any,
) -> None:

View file

@ -64,6 +64,7 @@ if TYPE_CHECKING:
Point3D,
Point3DLike,
Vector3D,
Vector3DLike,
)
LineType = TypeVar("LineType", bound=Line)
@ -353,8 +354,8 @@ class CoordinateSystem:
self,
label: float | str | VMobject,
axis: Mobject,
edge: Vector3D,
direction: Vector3D,
edge: Vector3DLike,
direction: Vector3DLike,
buff: float = SMALL_BUFF,
) -> Mobject:
"""Gets the label for an axis.
@ -2432,7 +2433,7 @@ class ThreeDAxes(Axes):
y_length: float | None = config.frame_height + 2.5,
z_length: float | None = config.frame_height - 1.5,
z_axis_config: dict[str, Any] | None = None,
z_normal: Vector3D = DOWN,
z_normal: Vector3DLike = DOWN,
num_axis_pieces: int = 20,
light_source: Point3DLike = 9 * DOWN + 7 * LEFT + 10 * OUT,
# opengl stuff (?)
@ -2519,11 +2520,11 @@ class ThreeDAxes(Axes):
def get_y_axis_label(
self,
label: float | str | VMobject,
edge: Vector3D = UR,
direction: Vector3D = UR,
edge: Vector3DLike = UR,
direction: Vector3DLike = UR,
buff: float = SMALL_BUFF,
rotation: float = PI / 2,
rotation_axis: Vector3D = OUT,
rotation_axis: Vector3DLike = OUT,
**kwargs: dict[str, Any],
) -> Mobject:
"""Generate a y-axis label.
@ -2569,11 +2570,11 @@ class ThreeDAxes(Axes):
def get_z_axis_label(
self,
label: float | str | VMobject,
edge: Vector3D = OUT,
direction: Vector3D = RIGHT,
edge: Vector3DLike = OUT,
direction: Vector3DLike = RIGHT,
buff: float = SMALL_BUFF,
rotation: float = PI / 2,
rotation_axis: Vector3D = RIGHT,
rotation_axis: Vector3DLike = RIGHT,
**kwargs: Any,
) -> Mobject:
"""Generate a z-axis label.

View file

@ -54,7 +54,7 @@ if TYPE_CHECKING:
Point3D,
Point3DLike,
Point3DLike_Array,
Vector3D,
Vector3DLike,
)
from ..animation.animation import Animation
@ -1204,7 +1204,7 @@ class Mobject:
for mob in self.family_members_with_points():
func(mob)
def shift(self, *vectors: Vector3D) -> Self:
def shift(self, *vectors: Vector3DLike) -> Self:
"""Shift by the given vectors.
Parameters
@ -1275,14 +1275,14 @@ class Mobject:
)
return self
def rotate_about_origin(self, angle: float, axis: Vector3D = OUT, axes=[]) -> Self:
def rotate_about_origin(self, angle: float, axis: Vector3DLike = OUT) -> Self:
"""Rotates the :class:`~.Mobject` about the ORIGIN, which is at [0,0,0]."""
return self.rotate(angle, axis, about_point=ORIGIN)
def rotate(
self,
angle: float,
axis: Vector3D = OUT,
axis: Vector3DLike = OUT,
about_point: Point3DLike | None = None,
**kwargs,
) -> Self:
@ -1350,7 +1350,7 @@ class Mobject:
)
return self
def flip(self, axis: Vector3D = UP, **kwargs) -> Self:
def flip(self, axis: Vector3DLike = UP, **kwargs) -> Self:
"""Flips/Mirrors an mobject about its center.
Examples
@ -1470,7 +1470,7 @@ class Mobject:
self,
func: MultiMappingFunction,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = None,
about_edge: Vector3DLike | None = None,
) -> Self:
if about_point is None:
if about_edge is None:
@ -1500,7 +1500,7 @@ class Mobject:
return self
def align_on_border(
self, direction: Vector3D, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
self, direction: Vector3DLike, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
) -> Self:
"""Direction just needs to be a vector pointing towards side or
corner in the 2d plane.
@ -1517,7 +1517,7 @@ class Mobject:
return self
def to_corner(
self, corner: Vector3D = DL, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
self, corner: Vector3DLike = DL, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
) -> Self:
"""Moves this :class:`~.Mobject` to the given corner of the screen.
@ -1545,7 +1545,7 @@ class Mobject:
return self.align_on_border(corner, buff)
def to_edge(
self, edge: Vector3D = LEFT, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
self, edge: Vector3DLike = LEFT, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
) -> Self:
"""Moves this :class:`~.Mobject` to the given edge of the screen,
without affecting its position in the other dimension.
@ -1577,12 +1577,12 @@ class Mobject:
def next_to(
self,
mobject_or_point: Mobject | Point3DLike,
direction: Vector3D = RIGHT,
direction: Vector3DLike = RIGHT,
buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
aligned_edge: Vector3D = ORIGIN,
aligned_edge: Vector3DLike = ORIGIN,
submobject_to_align: Mobject | None = None,
index_of_submobject_to_align: int | None = None,
coor_mask: Vector3D = np.array([1, 1, 1]),
coor_mask: Vector3DLike = np.array([1, 1, 1]),
) -> Self:
"""Move this :class:`~.Mobject` next to another's :class:`~.Mobject` or Point3D.
@ -1604,13 +1604,18 @@ class Mobject:
self.add(d, c, s, t)
"""
np_direction = np.asarray(direction)
np_aligned_edge = np.asarray(aligned_edge)
if isinstance(mobject_or_point, Mobject):
mob = mobject_or_point
if index_of_submobject_to_align is not None:
target_aligner = mob[index_of_submobject_to_align]
else:
target_aligner = mob
target_point = target_aligner.get_critical_point(aligned_edge + direction)
target_point = target_aligner.get_critical_point(
np_aligned_edge + np_direction
)
else:
target_point = mobject_or_point
if submobject_to_align is not None:
@ -1619,8 +1624,8 @@ class Mobject:
aligner = self[index_of_submobject_to_align]
else:
aligner = self
point_to_align = aligner.get_critical_point(aligned_edge - direction)
self.shift((target_point - point_to_align + buff * direction) * coor_mask)
point_to_align = aligner.get_critical_point(np_aligned_edge - np_direction)
self.shift((target_point - point_to_align + buff * np_direction) * coor_mask)
return self
def shift_onto_screen(self, **kwargs) -> Self:
@ -1766,22 +1771,22 @@ class Mobject:
"""Stretches the :class:`~.Mobject` to fit a depth, not keeping width/height proportional."""
return self.rescale_to_fit(depth, 2, stretch=True, **kwargs)
def set_coord(self, value, dim: int, direction: Vector3D = ORIGIN) -> Self:
def set_coord(self, value, dim: int, direction: Vector3DLike = ORIGIN) -> Self:
curr = self.get_coord(dim, direction)
shift_vect = np.zeros(self.dim)
shift_vect[dim] = value - curr
self.shift(shift_vect)
return self
def set_x(self, x: float, direction: Vector3D = ORIGIN) -> Self:
def set_x(self, x: float, direction: Vector3DLike = ORIGIN) -> Self:
"""Set x value of the center of the :class:`~.Mobject` (``int`` or ``float``)"""
return self.set_coord(x, 0, direction)
def set_y(self, y: float, direction: Vector3D = ORIGIN) -> Self:
def set_y(self, y: float, direction: Vector3DLike = ORIGIN) -> Self:
"""Set y value of the center of the :class:`~.Mobject` (``int`` or ``float``)"""
return self.set_coord(y, 1, direction)
def set_z(self, z: float, direction: Vector3D = ORIGIN) -> Self:
def set_z(self, z: float, direction: Vector3DLike = ORIGIN) -> Self:
"""Set z value of the center of the :class:`~.Mobject` (``int`` or ``float``)"""
return self.set_coord(z, 2, direction)
@ -1794,8 +1799,8 @@ class Mobject:
def move_to(
self,
point_or_mobject: Point3DLike | Mobject,
aligned_edge: Vector3D = ORIGIN,
coor_mask: Vector3D = np.array([1, 1, 1]),
aligned_edge: Vector3DLike = ORIGIN,
coor_mask: Vector3DLike = np.array([1, 1, 1]),
) -> Self:
"""Move center of the :class:`~.Mobject` to certain Point3D."""
if isinstance(point_or_mobject, Mobject):
@ -2114,7 +2119,7 @@ class Mobject:
else:
return np.max(values)
def get_critical_point(self, direction: Vector3D) -> Point3D:
def get_critical_point(self, direction: Vector3DLike) -> Point3D:
"""Picture a box bounding the :class:`~.Mobject`. Such a box has
9 'critical points': 4 corners, 4 edge center, the
center. This returns one of them, along the given direction.
@ -2143,11 +2148,11 @@ class Mobject:
# Pseudonyms for more general get_critical_point method
def get_edge_center(self, direction: Vector3D) -> Point3D:
def get_edge_center(self, direction: Vector3DLike) -> Point3D:
"""Get edge Point3Ds for certain direction."""
return self.get_critical_point(direction)
def get_corner(self, direction: Vector3D) -> Point3D:
def get_corner(self, direction: Vector3DLike) -> Point3D:
"""Get corner Point3Ds for certain direction."""
return self.get_critical_point(direction)
@ -2158,9 +2163,9 @@ class Mobject:
def get_center_of_mass(self) -> Point3D:
return np.apply_along_axis(np.mean, 0, self.get_all_points())
def get_boundary_point(self, direction: Vector3D) -> Point3D:
def get_boundary_point(self, direction: Vector3DLike) -> Point3D:
all_points = self.get_points_defining_boundary()
index = np.argmax(np.dot(all_points, np.array(direction).T))
index = np.argmax(np.dot(all_points, direction))
return all_points[index]
def get_midpoint(self) -> Point3D:
@ -2217,19 +2222,19 @@ class Mobject:
dim,
) - self.reduce_across_dimension(min, dim)
def get_coord(self, dim: int, direction: Vector3D = ORIGIN):
def get_coord(self, dim: int, direction: Vector3DLike = ORIGIN) -> float:
"""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) -> float:
def get_x(self, direction: Vector3DLike = 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) -> float:
def get_y(self, direction: Vector3DLike = 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) -> float:
def get_z(self, direction: Vector3DLike = ORIGIN) -> float:
"""Returns z Point3D of the center of the :class:`~.Mobject` as ``float``"""
return self.get_coord(2, direction)
@ -2300,7 +2305,7 @@ class Mobject:
return self.match_dim_size(mobject, 2, **kwargs)
def match_coord(
self, mobject: Mobject, dim: int, direction: Vector3D = ORIGIN
self, mobject: Mobject, dim: int, direction: Vector3DLike = ORIGIN
) -> Self:
"""Match the Point3Ds with the Point3Ds of another :class:`~.Mobject`."""
return self.set_coord(
@ -2324,7 +2329,7 @@ class Mobject:
def align_to(
self,
mobject_or_point: Mobject | Point3DLike,
direction: Vector3D = ORIGIN,
direction: Vector3DLike = ORIGIN,
) -> Self:
"""Aligns mobject to another :class:`~.Mobject` in a certain direction.
@ -2431,7 +2436,7 @@ class Mobject:
def arrange(
self,
direction: Vector3D = RIGHT,
direction: Vector3DLike = RIGHT,
buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
center: bool = True,
**kwargs,
@ -2464,7 +2469,7 @@ class Mobject:
rows: int | None = None,
cols: int | None = None,
buff: float | tuple[float, float] = MED_SMALL_BUFF,
cell_alignment: Vector3D = ORIGIN,
cell_alignment: Vector3DLike = ORIGIN,
row_alignments: str | None = None, # "ucd"
col_alignments: str | None = None, # "lcr"
row_heights: Iterable[float | None] | None = None,

View file

@ -63,6 +63,7 @@ if TYPE_CHECKING:
Point3DLike,
Point3DLike_Array,
Vector3D,
Vector3DLike,
)
TimeBasedUpdater: TypeAlias = Callable[["Mobject", float], object]
@ -636,7 +637,7 @@ class OpenGLMobject:
self,
func: MultiMappingFunction,
about_point: Point3DLike | None = None,
about_edge: Vector3D | None = ORIGIN,
about_edge: Vector3DLike | None = ORIGIN,
works_on_bounding_box: bool = False,
) -> Self:
if about_point is None and about_edge is not None:
@ -992,7 +993,7 @@ class OpenGLMobject:
# Submobject organization
def arrange(
self, direction: Vector3D = RIGHT, center: bool = True, **kwargs
self, direction: Vector3DLike = RIGHT, center: bool = True, **kwargs
) -> Self:
"""Sorts :class:`~.OpenGLMobject` next to each other on screen.
@ -1022,7 +1023,7 @@ class OpenGLMobject:
rows: int | None = None,
cols: int | None = None,
buff: float | tuple[float, float] = MED_SMALL_BUFF,
cell_alignment: Vector3D = ORIGIN,
cell_alignment: Vector3DLike = ORIGIN,
row_alignments: str | None = None, # "ucd"
col_alignments: str | None = None, # "lcr"
row_heights: Sequence[float | None] | None = None,
@ -1553,7 +1554,7 @@ class OpenGLMobject:
# Transforming operations
def shift(self, vector: Vector3D) -> Self:
def shift(self, vector: Vector3DLike) -> Self:
self.apply_points_function(
lambda points: points + vector,
about_edge=None,
@ -1631,14 +1632,14 @@ class OpenGLMobject:
self.apply_points_function(func, works_on_bounding_box=True, **kwargs)
return self
def rotate_about_origin(self, angle: float, axis: Vector3D = OUT) -> Self:
def rotate_about_origin(self, angle: float, axis: Vector3DLike = OUT) -> Self:
return self.rotate(angle, axis, about_point=ORIGIN)
def rotate(
self,
angle: float,
axis: Vector3D = OUT,
about_point: Sequence[float] | None = None,
axis: Vector3DLike = OUT,
about_point: Point3DLike | None = None,
**kwargs,
) -> Self:
"""Rotates the :class:`~.OpenGLMobject` about a certain point."""
@ -1650,7 +1651,7 @@ class OpenGLMobject:
)
return self
def flip(self, axis: Vector3D = UP, **kwargs) -> Self:
def flip(self, axis: Vector3DLike = UP, **kwargs) -> Self:
"""Flips/Mirrors an mobject about its center.
Examples
@ -1751,8 +1752,8 @@ class OpenGLMobject:
def wag(
self,
direction: Vector3D = RIGHT,
axis: Vector3D = DOWN,
direction: Vector3DLike = RIGHT,
axis: Vector3DLike = DOWN,
wag_factor: float = 1.0,
) -> Self:
for mob in self.family_members_with_points():
@ -1778,7 +1779,7 @@ class OpenGLMobject:
def align_on_border(
self,
direction: Vector3D,
direction: Vector3DLike,
buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER,
) -> Self:
"""
@ -1791,21 +1792,21 @@ class OpenGLMobject:
0,
)
point_to_align = self.get_bounding_box_point(direction)
shift_val = target_point - point_to_align - buff * np.array(direction)
shift_val = target_point - point_to_align - buff * np.asarray(direction)
shift_val = shift_val * abs(np.sign(direction))
self.shift(shift_val)
return self
def to_corner(
self,
corner: Vector3D = LEFT + DOWN,
corner: Vector3DLike = LEFT + DOWN,
buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER,
) -> Self:
return self.align_on_border(corner, buff)
def to_edge(
self,
edge: Vector3D = LEFT,
edge: Vector3DLike = LEFT,
buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER,
) -> Self:
return self.align_on_border(edge, buff)
@ -1813,12 +1814,12 @@ class OpenGLMobject:
def next_to(
self,
mobject_or_point: OpenGLMobject | Point3DLike,
direction: Vector3D = RIGHT,
direction: Vector3DLike = RIGHT,
buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
aligned_edge: Vector3D = ORIGIN,
aligned_edge: Vector3DLike = ORIGIN,
submobject_to_align: OpenGLMobject | None = None,
index_of_submobject_to_align: int | None = None,
coor_mask: Point3DLike = np.array([1, 1, 1]),
coor_mask: Vector3DLike = np.array([1, 1, 1]),
) -> Self:
"""Move this :class:`~.OpenGLMobject` next to another's :class:`~.OpenGLMobject` or coordinate.
@ -1840,6 +1841,9 @@ class OpenGLMobject:
self.add(d, c, s, t)
"""
np_direction = np.asarray(direction)
np_aligned_edge = np.asarray(aligned_edge)
if isinstance(mobject_or_point, OpenGLMobject):
mob = mobject_or_point
if index_of_submobject_to_align is not None:
@ -1847,7 +1851,7 @@ class OpenGLMobject:
else:
target_aligner = mob
target_point = target_aligner.get_bounding_box_point(
aligned_edge + direction,
np_aligned_edge + np_direction,
)
else:
target_point = mobject_or_point
@ -1857,8 +1861,8 @@ class OpenGLMobject:
aligner = self[index_of_submobject_to_align]
else:
aligner = self
point_to_align = aligner.get_bounding_box_point(aligned_edge - direction)
self.shift((target_point - point_to_align + buff * direction) * coor_mask)
point_to_align = aligner.get_bounding_box_point(np_aligned_edge - np_direction)
self.shift((target_point - point_to_align + buff * np_direction) * coor_mask)
return self
def shift_onto_screen(self, **kwargs) -> Self:
@ -1970,22 +1974,24 @@ class OpenGLMobject:
scale_to_fit_depth = set_depth
def set_coord(self, value: float, dim: int, direction: Vector3D = ORIGIN) -> Self:
def set_coord(
self, value: float, dim: int, direction: Vector3DLike = ORIGIN
) -> Self:
curr = self.get_coord(dim, direction)
shift_vect = np.zeros(self.dim)
shift_vect[dim] = value - curr
self.shift(shift_vect)
return self
def set_x(self, x: float, direction: Vector3D = ORIGIN) -> Self:
def set_x(self, x: float, direction: Vector3DLike = ORIGIN) -> Self:
"""Set x value of the center of the :class:`~.OpenGLMobject` (``int`` or ``float``)"""
return self.set_coord(x, 0, direction)
def set_y(self, y: float, direction: Vector3D = ORIGIN) -> Self:
def set_y(self, y: float, direction: Vector3DLike = ORIGIN) -> Self:
"""Set y value of the center of the :class:`~.OpenGLMobject` (``int`` or ``float``)"""
return self.set_coord(y, 1, direction)
def set_z(self, z: float, direction: Vector3D = ORIGIN) -> Self:
def set_z(self, z: float, direction: Vector3DLike = ORIGIN) -> Self:
"""Set z value of the center of the :class:`~.OpenGLMobject` (``int`` or ``float``)"""
return self.set_coord(z, 2, direction)
@ -1998,8 +2004,8 @@ class OpenGLMobject:
def move_to(
self,
point_or_mobject: Point3DLike | OpenGLMobject,
aligned_edge: Vector3D = ORIGIN,
coor_mask: Point3DLike = np.array([1, 1, 1]),
aligned_edge: Vector3DLike = ORIGIN,
coor_mask: Vector3DLike = np.array([1, 1, 1]),
) -> Self:
"""Move center of the :class:`~.OpenGLMobject` to certain coordinate."""
if isinstance(point_or_mobject, OpenGLMobject):
@ -2252,16 +2258,16 @@ class OpenGLMobject:
# Getters
def get_bounding_box_point(self, direction: Vector3D) -> Point3D:
def get_bounding_box_point(self, direction: Vector3DLike) -> Point3D:
bb = self.get_bounding_box()
indices = (np.sign(direction) + 1).astype(int)
return np.array([bb[indices[i]][i] for i in range(3)])
def get_edge_center(self, direction: Vector3D) -> Point3D:
def get_edge_center(self, direction: Vector3DLike) -> Point3D:
"""Get edge coordinates for certain direction."""
return self.get_bounding_box_point(direction)
def get_corner(self, direction: Vector3D) -> Point3D:
def get_corner(self, direction: Vector3DLike) -> Point3D:
"""Get corner coordinates for certain direction."""
return self.get_bounding_box_point(direction)
@ -2272,23 +2278,24 @@ class OpenGLMobject:
def get_center_of_mass(self) -> Point3D:
return self.get_all_points().mean(0)
def get_boundary_point(self, direction: Vector3D) -> Point3D:
def get_boundary_point(self, direction: Vector3DLike) -> Point3D:
all_points = self.get_all_points()
boundary_directions = all_points - self.get_center()
norms = np.linalg.norm(boundary_directions, axis=1)
boundary_directions /= np.repeat(norms, 3).reshape((len(norms), 3))
index = np.argmax(np.dot(boundary_directions, np.array(direction).T))
index = np.argmax(np.dot(boundary_directions, direction))
return all_points[index]
def get_continuous_bounding_box_point(self, direction: Vector3D) -> Point3D:
def get_continuous_bounding_box_point(self, direction: Vector3DLike) -> Point3D:
dl, center, ur = self.get_bounding_box()
corner_vect = ur - center
return center + direction / np.max(
np_direction = np.asarray(direction)
return center + np_direction / np.max(
np.abs(
np.true_divide(
direction,
np_direction,
corner_vect,
out=np.zeros(len(direction)),
out=np.zeros(len(np_direction)),
where=((corner_vect) != 0),
),
),
@ -2334,19 +2341,19 @@ class OpenGLMobject:
"""Returns the depth of the mobject."""
return self.length_over_dim(2)
def get_coord(self, dim: int, direction: Vector3D = ORIGIN) -> ManimFloat:
def get_coord(self, dim: int, direction: Vector3DLike = ORIGIN) -> ManimFloat:
"""Meant to generalize ``get_x``, ``get_y`` and ``get_z``"""
return self.get_bounding_box_point(direction)[dim]
def get_x(self, direction: Vector3D = ORIGIN) -> ManimFloat:
def get_x(self, direction: Vector3DLike = ORIGIN) -> ManimFloat:
"""Returns x coordinate of the center of the :class:`~.OpenGLMobject` as ``float``"""
return self.get_coord(0, direction)
def get_y(self, direction: Vector3D = ORIGIN) -> ManimFloat:
def get_y(self, direction: Vector3DLike = ORIGIN) -> ManimFloat:
"""Returns y coordinate of the center of the :class:`~.OpenGLMobject` as ``float``"""
return self.get_coord(1, direction)
def get_z(self, direction: Vector3D = ORIGIN) -> ManimFloat:
def get_z(self, direction: Vector3DLike = ORIGIN) -> ManimFloat:
"""Returns z coordinate of the center of the :class:`~.OpenGLMobject` as ``float``"""
return self.get_coord(2, direction)
@ -2412,7 +2419,7 @@ class OpenGLMobject:
return self.match_dim_size(mobject, 2, **kwargs)
def match_coord(
self, mobject: OpenGLMobject, dim: int, direction: Vector3D = ORIGIN
self, mobject: OpenGLMobject, dim: int, direction: Vector3DLike = ORIGIN
) -> Self:
"""Match the coordinates with the coordinates of another :class:`~.OpenGLMobject`."""
return self.set_coord(
@ -2421,22 +2428,22 @@ class OpenGLMobject:
direction=direction,
)
def match_x(self, mobject: OpenGLMobject, direction: Vector3D = ORIGIN) -> Self:
def match_x(self, mobject: OpenGLMobject, direction: Vector3DLike = ORIGIN) -> Self:
"""Match x coord. to the x coord. of another :class:`~.OpenGLMobject`."""
return self.match_coord(mobject, 0, direction)
def match_y(self, mobject: OpenGLMobject, direction: Vector3D = ORIGIN) -> Self:
def match_y(self, mobject: OpenGLMobject, direction: Vector3DLike = ORIGIN) -> Self:
"""Match y coord. to the x coord. of another :class:`~.OpenGLMobject`."""
return self.match_coord(mobject, 1, direction)
def match_z(self, mobject: OpenGLMobject, direction: Vector3D = ORIGIN) -> Self:
def match_z(self, mobject: OpenGLMobject, direction: Vector3DLike = ORIGIN) -> Self:
"""Match z coord. to the x coord. of another :class:`~.OpenGLMobject`."""
return self.match_coord(mobject, 2, direction)
def align_to(
self,
mobject_or_point: OpenGLMobject | Point3DLike,
direction: Vector3D = ORIGIN,
direction: Vector3DLike = ORIGIN,
) -> Self:
"""
Examples:

View file

@ -28,7 +28,7 @@ from ...utils.color import BLACK
from ..svg.svg_mobject import VMobjectFromSVGPath
if TYPE_CHECKING:
from manim.typing import Point3D, Point3DLike, Vector3D
from manim.typing import Point3D, Point3DLike, Vector3D, Vector3DLike
from manim.utils.color.core import ParsableManimColor
@ -70,7 +70,7 @@ class Brace(VMobjectFromSVGPath):
def __init__(
self,
mobject: Mobject,
direction: Point3DLike = DOWN,
direction: Vector3DLike = DOWN,
buff: float = 0.2,
sharpness: float = 2,
stroke_width: float = 0,
@ -233,7 +233,7 @@ class BraceLabel(VMobject, metaclass=ConvertToOpenGL):
self,
obj: Mobject,
text: str,
brace_direction: Point3DLike = DOWN,
brace_direction: Vector3DLike = DOWN,
label_constructor: type[SingleStringMathTex | Text] = MathTex,
font_size: float = DEFAULT_FONT_SIZE,
buff: float = 0.2,
@ -376,7 +376,7 @@ class BraceBetweenPoints(Brace):
self,
point_1: Point3DLike,
point_2: Point3DLike,
direction: Point3DLike = ORIGIN,
direction: Vector3DLike = ORIGIN,
**kwargs: Any,
):
if all(direction == ORIGIN):
@ -443,7 +443,7 @@ class ArcBrace(Brace):
def __init__(
self,
arc: Arc | None = None,
direction: Point3DLike = RIGHT,
direction: Vector3DLike = RIGHT,
**kwargs: Any,
):
if arc is None:

View file

@ -16,7 +16,7 @@ from manim.mobject.text.tex_mobject import MathTex, SingleStringMathTex, Tex
from manim.mobject.text.text_mobject import Text
from manim.mobject.types.vectorized_mobject import VMobject
from manim.mobject.value_tracker import ValueTracker
from manim.typing import Point3DLike
from manim.typing import Vector3DLike
string_to_mob_map: dict[str, VMobject] = {}
@ -93,7 +93,7 @@ class DecimalNumber(VMobject, metaclass=ConvertToOpenGL):
unit: str | None = None, # Aligned to bottom unless it starts with "^"
unit_buff_per_font_unit: float = 0,
include_background_rectangle: bool = False,
edge_to_fix: Point3DLike = LEFT,
edge_to_fix: Vector3DLike = LEFT,
font_size: float = DEFAULT_FONT_SIZE,
stroke_width: float = 0,
fill_opacity: float = 1.0,

View file

@ -2,9 +2,6 @@
from __future__ import annotations
from manim.typing import Point3DLike, Vector3D
from manim.utils.color import BLUE, BLUE_D, BLUE_E, LIGHT_GREY, WHITE, interpolate_color
__all__ = [
"ThreeDVMobject",
"Surface",
@ -20,7 +17,7 @@ __all__ = [
]
from collections.abc import Iterable, Sequence
from typing import Any, Callable
from typing import TYPE_CHECKING, Any, Callable
import numpy as np
from typing_extensions import Self
@ -34,12 +31,21 @@ from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.types.vectorized_mobject import VectorizedPoint, VGroup, VMobject
from manim.utils.color import (
BLUE,
BLUE_D,
BLUE_E,
LIGHT_GREY,
WHITE,
ManimColor,
ParsableManimColor,
interpolate_color,
)
from manim.utils.iterables import tuplify
from manim.utils.space_ops import normalize, perpendicular_bisector, z_to_vector
if TYPE_CHECKING:
from manim.typing import Point3D, Point3DLike, Vector3DLike
class ThreeDVMobject(VMobject, metaclass=ConvertToOpenGL):
def __init__(self, shade_in_3d: bool = True, **kwargs):
@ -970,8 +976,8 @@ class Line3D(Cylinder):
def pointify(
self,
mob_or_point: Mobject | Point3DLike,
direction: Vector3D = None,
) -> np.ndarray:
direction: Vector3DLike | None = None,
) -> Point3D:
"""Gets a point representing the center of the :class:`Mobjects <.Mobject>`.
Parameters
@ -1018,7 +1024,7 @@ class Line3D(Cylinder):
def parallel_to(
cls,
line: Line3D,
point: Vector3D = ORIGIN,
point: Point3DLike = ORIGIN,
length: float = 5,
**kwargs,
) -> Line3D:
@ -1054,11 +1060,11 @@ class Line3D(Cylinder):
line2 = Line3D.parallel_to(line1, color=YELLOW)
self.add(ax, line1, line2)
"""
point = np.array(point)
np_point = np.asarray(point)
vect = normalize(line.vect)
return cls(
point + vect * length / 2,
point - vect * length / 2,
np_point + vect * length / 2,
np_point - vect * length / 2,
**kwargs,
)
@ -1066,7 +1072,7 @@ class Line3D(Cylinder):
def perpendicular_to(
cls,
line: Line3D,
point: Vector3D = ORIGIN,
point: Vector3DLike = ORIGIN,
length: float = 5,
**kwargs,
) -> Line3D:
@ -1102,17 +1108,17 @@ class Line3D(Cylinder):
line2 = Line3D.perpendicular_to(line1, color=BLUE)
self.add(ax, line1, line2)
"""
point = np.array(point)
np_point = np.asarray(point)
norm = np.cross(line.vect, point - line.start)
norm = np.cross(line.vect, np_point - line.start)
if all(np.linalg.norm(norm) == np.zeros(3)):
raise ValueError("Could not find the perpendicular.")
start, end = perpendicular_bisector([line.start, line.end], norm)
vect = normalize(end - start)
return cls(
point + vect * length / 2,
point - vect * length / 2,
np_point + vect * length / 2,
np_point - vect * length / 2,
**kwargs,
)

View file

@ -35,7 +35,7 @@ if TYPE_CHECKING:
import numpy.typing as npt
from typing_extensions import Self
from manim.typing import ManimFloat, Point3DLike, Vector3D
from manim.typing import ManimFloat, Point3DLike
class PMobject(Mobject, metaclass=ConvertToOpenGL):
@ -349,7 +349,7 @@ class PointCloudDot(Mobject1D):
def __init__(
self,
center: Vector3D = ORIGIN,
center: Point3DLike = ORIGIN,
radius: float = 2.0,
stroke_width: int = 2,
density: int = DEFAULT_POINT_DENSITY_1D,
@ -406,7 +406,7 @@ class Point(PMobject):
"""
def __init__(
self, location: Vector3D = ORIGIN, color: ManimColor = BLACK, **kwargs: Any
self, location: Point3DLike = ORIGIN, color: ManimColor = BLACK, **kwargs: Any
) -> None:
self.location = location
super().__init__(color=color, **kwargs)

View file

@ -65,6 +65,7 @@ if TYPE_CHECKING:
Point3DLike_Array,
RGBA_Array_Float,
Vector3D,
Vector3DLike,
Zeros,
)
@ -116,7 +117,7 @@ class VMobject(Mobject):
background_stroke_width: float = 0,
sheen_factor: float = 0.0,
joint_type: LineJointType | None = None,
sheen_direction: Vector3D = UL,
sheen_direction: Vector3DLike = UL,
close_new_points: bool = False,
pre_function_handle_to_anchor_scale_factor: float = 0.01,
make_smooth_after_applying_functions: bool = False,
@ -141,7 +142,7 @@ class VMobject(Mobject):
self.joint_type: LineJointType = (
LineJointType.AUTO if joint_type is None else joint_type
)
self.sheen_direction: Vector3D = sheen_direction
self.sheen_direction = sheen_direction
self.close_new_points: bool = close_new_points
self.pre_function_handle_to_anchor_scale_factor: float = (
pre_function_handle_to_anchor_scale_factor
@ -394,7 +395,7 @@ class VMobject(Mobject):
background_stroke_width: float | None = None,
background_stroke_opacity: float | None = None,
sheen_factor: float | None = None,
sheen_direction: Vector3D | None = None,
sheen_direction: Vector3DLike | None = None,
background_image: Image | str | None = None,
family: bool = True,
) -> Self:
@ -619,7 +620,7 @@ class VMobject(Mobject):
color = property(get_color, set_color)
def set_sheen_direction(self, direction: Vector3D, family: bool = True) -> Self:
def set_sheen_direction(self, direction: Vector3DLike, family: bool = True) -> Self:
"""Sets the direction of the applied sheen.
Parameters
@ -638,16 +639,16 @@ class VMobject(Mobject):
:meth:`~.VMobject.set_sheen`
:meth:`~.VMobject.rotate_sheen_direction`
"""
direction = np.array(direction)
direction_copy = np.array(direction)
if family:
for submob in self.get_family():
submob.sheen_direction = direction
submob.sheen_direction = direction_copy.copy()
else:
self.sheen_direction: Vector3D = direction
self.sheen_direction = direction_copy
return self
def rotate_sheen_direction(
self, angle: float, axis: Vector3D = OUT, family: bool = True
self, angle: float, axis: Vector3DLike = OUT, family: bool = True
) -> Self:
"""Rotates the direction of the applied sheen.
@ -680,7 +681,7 @@ class VMobject(Mobject):
return self
def set_sheen(
self, factor: float, direction: Vector3D | None = None, family: bool = True
self, factor: float, direction: Vector3DLike | None = None, family: bool = True
) -> Self:
"""Applies a color gradient from a direction.
@ -1188,7 +1189,7 @@ class VMobject(Mobject):
def rotate(
self,
angle: float,
axis: Vector3D = OUT,
axis: Vector3DLike = OUT,
about_point: Point3DLike | None = None,
**kwargs,
) -> Self:

View file

@ -49,7 +49,14 @@ if TYPE_CHECKING:
from typing_extensions import Self
from manim.typing import MappingFunction, Point2DLike, Point3D, Point3DLike
from manim.typing import (
MappingFunction,
Point3D,
Point3DLike,
Vector2DLike,
Vector3D,
Vector3DLike,
)
X_COLOR = GREEN_C
@ -139,7 +146,7 @@ class VectorScene(Scene):
self.renderer.camera = Camera(self.renderer.get_frame())
self.clear()
def get_vector(self, numerical_vector: Point3DLike, **kwargs: Any) -> Arrow:
def get_vector(self, numerical_vector: Vector3DLike, **kwargs: Any) -> Arrow:
"""
Returns an arrow on the Plane given an input numerical vector.
@ -166,7 +173,7 @@ class VectorScene(Scene):
def add_vector(
self,
vector: Arrow | Point3DLike,
vector: Arrow | Vector3DLike,
color: ParsableManimColor | Iterable[ParsableManimColor] = YELLOW,
animate: bool = True,
**kwargs: Any,
@ -346,10 +353,10 @@ class VectorScene(Scene):
if not rotate:
label.rotate(-angle, about_point=ORIGIN)
if direction == "left":
temp_shift_1: Point3D = np.asarray(label.get_bottom())
temp_shift_1: Vector3D = np.asarray(label.get_bottom())
label.shift(-temp_shift_1 + 0.1 * UP)
else:
temp_shift_2: Point3D = np.asarray(label.get_top())
temp_shift_2: Vector3D = np.asarray(label.get_top())
label.shift(-temp_shift_2 + 0.1 * DOWN)
label.rotate(angle, about_point=ORIGIN)
label.shift((vector.get_end() - vector.get_start()) / 2)
@ -391,7 +398,7 @@ class VectorScene(Scene):
self,
x_coord: MathTex,
x_line: Line,
vector: Point3DLike,
vector: Vector3DLike,
) -> MathTex: # TODO Write DocStrings for this.
x_coord.next_to(x_line, -np.sign(vector[1]) * UP)
x_coord.set_color(X_COLOR)
@ -401,7 +408,7 @@ class VectorScene(Scene):
self,
y_coord: MathTex,
y_line: Line,
vector: Point3DLike,
vector: Vector3DLike,
) -> MathTex: # TODO Write DocStrings for this.
y_coord.next_to(y_line, np.sign(vector[0]) * RIGHT)
y_coord.set_color(Y_COLOR)
@ -409,7 +416,7 @@ class VectorScene(Scene):
def coords_to_vector(
self,
vector: Point2DLike,
vector: Vector2DLike,
coords_start: Point3DLike = 2 * RIGHT + 2 * UP,
clean_up: bool = True,
) -> None:
@ -475,7 +482,7 @@ class VectorScene(Scene):
def vector_to_coords(
self,
vector: Point3DLike,
vector: Vector3DLike,
integer_labels: bool = True,
clean_up: bool = True,
) -> tuple[Matrix, Line, Line]:
@ -536,7 +543,7 @@ class VectorScene(Scene):
self.add(*starting_mobjects)
return array, x_line, y_line
def show_ghost_movement(self, vector: Arrow | Point2DLike | Point3DLike) -> None:
def show_ghost_movement(self, vector: Arrow | Vector2DLike | Vector3DLike) -> None:
"""
This method plays an animation that partially shows the entire plane moving
in the direction of a particular vector. This is useful when you wish to
@ -554,7 +561,7 @@ class VectorScene(Scene):
vector = np.asarray(vector)
if len(vector) == 2:
vector = np.append(np.array(vector), 0.0)
vector_cleaned: Point3D = vector
vector_cleaned: Vector3D = vector
x_max = int(config["frame_x_radius"] + abs(vector_cleaned[0]))
y_max = int(config["frame_y_radius"] + abs(vector_cleaned[1]))

View file

@ -61,11 +61,17 @@ __all__ = [
"PointND_Array",
"PointNDLike_Array",
"Vector2D",
"Vector2DLike",
"Vector2D_Array",
"Vector2DLike_Array",
"Vector3D",
"Vector3DLike",
"Vector3D_Array",
"Vector3DLike_Array",
"VectorND",
"VectorNDLike",
"VectorND_Array",
"VectorNDLike_Array",
"RowVector",
"ColVector",
"MatrixMN",
@ -312,29 +318,23 @@ 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.
"""
Point2D_Array: TypeAlias = npt.NDArray[PointDType]
"""``shape: (M, 2)``
A NumPy array representing a sequence of `Point2D` objects:
A NumPy array representing a sequence of :class:`Point2D` objects:
``[[float, float], ...]``.
"""
Point2DLike_Array: TypeAlias = Union[Point2D_Array, Sequence[Point2DLike]]
"""``shape: (M, 2)``
An array of `Point2DLike` objects: ``[[float, float], ...]``.
An array of :class:`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.
Please refer to the documentation of the function you are using for
further type information.
"""
@ -357,14 +357,14 @@ array.
Point3D_Array: TypeAlias = npt.NDArray[PointDType]
"""``shape: (M, 3)``
A NumPy array representing a sequence of `Point3D` objects:
A NumPy array representing a sequence of :class:`Point3D` objects:
``[[float, float, float], ...]``.
"""
Point3DLike_Array: TypeAlias = Union[Point3D_Array, Sequence[Point3DLike]]
"""``shape: (M, 3)``
An array of `Point3D` objects: ``[[float, float, float], ...]``.
An array of :class:`Point3DLike` objects: ``[[float, float, float], ...]``.
This represents anything which can be converted to a :class:`Point3D_Array`
NumPy array.
@ -391,14 +391,14 @@ array.
PointND_Array: TypeAlias = npt.NDArray[PointDType]
"""``shape: (M, N)``
A NumPy array representing a sequence of `PointND` objects:
A NumPy array representing a sequence of :class:`PointND` objects:
``[[float, ...], ...]``.
"""
PointNDLike_Array: TypeAlias = Union[PointND_Array, Sequence[PointNDLike]]
"""``shape: (M, N)``
An array of `PointND` objects: ``[[float, ...], ...]``.
An array of :class:`PointNDLike` objects: ``[[float, ...], ...]``.
This represents anything which can be converted to a :class:`PointND_Array`
NumPy array.
@ -416,10 +416,20 @@ Vector types
Vector2D: TypeAlias = npt.NDArray[PointDType]
"""``shape: (2,)``
A NumPy array representing a 2-dimensional vector: ``[float, float]``.
.. caution::
Do not confuse with the :class:`~.Vector` or :class:`~.Arrow`
VMobjects!
"""
Vector2DLike: TypeAlias = Union[npt.NDArray[PointDType], tuple[float, float]]
"""``shape: (2,)``
A 2-dimensional vector: ``[float, float]``.
Normally, a function or method which expects a `Vector2D` as a
parameter can handle being passed a `Vector3D` instead.
This represents anything which can be converted to a :class:`Vector2D` NumPy
array.
.. caution::
Do not confuse with the :class:`~.Vector` or :class:`~.Arrow`
@ -429,17 +439,37 @@ parameter can handle being passed a `Vector3D` instead.
Vector2D_Array: TypeAlias = npt.NDArray[PointDType]
"""``shape: (M, 2)``
An array of `Vector2D` objects: ``[[float, float], ...]``.
A NumPy array representing a sequence of :class:`Vector2D` objects:
``[[float, float], ...]``.
"""
Normally, a function or method which expects a `Vector2D_Array` as a
parameter can handle being passed a `Vector3D_Array` instead.
Vector2DLike_Array: TypeAlias = Union[Vector2D_Array, Sequence[Vector2DLike]]
"""``shape: (M, 2)``
An array of :class:`Vector2DLike` objects: ``[[float, float], ...]``.
This represents anything which can be converted to a :class:`Vector2D_Array`
NumPy array.
"""
Vector3D: TypeAlias = npt.NDArray[PointDType]
"""``shape: (3,)``
A NumPy array representing a 3-dimensional vector: ``[float, float, float]``.
.. caution::
Do not confuse with the :class:`~.Vector` or :class:`~.Arrow3D`
VMobjects!
"""
Vector3DLike: TypeAlias = Union[npt.NDArray[PointDType], tuple[float, float, float]]
"""``shape: (3,)``
A 3-dimensional vector: ``[float, float, float]``.
This represents anything which can be converted to a :class:`Vector3D` NumPy
array.
.. caution::
Do not confuse with the :class:`~.Vector` or :class:`~.Arrow3D`
VMobjects!
@ -448,14 +478,38 @@ A 3-dimensional vector: ``[float, float, float]``.
Vector3D_Array: TypeAlias = npt.NDArray[PointDType]
"""``shape: (M, 3)``
An array of `Vector3D` objects: ``[[float, float, float], ...]``.
An NumPy array representing a sequence of :class:`Vector3D` objects:
``[[float, float, float], ...]``.
"""
Vector3DLike_Array: TypeAlias = Union[npt.NDArray[PointDType], Sequence[Vector3DLike]]
"""``shape: (M, 3)``
An array of :class:`Vector3DLike` objects: ``[[float, float, float], ...]``.
This represents anything which can be converted to a :class:`Vector3D_Array`
NumPy array.
"""
VectorND: TypeAlias = npt.NDArray[PointDType]
"""``shape (N,)``
A NumPy array representing an :math:`N`-dimensional vector: ``[float, ...]``.
.. caution::
Do not confuse with the :class:`~.Vector` VMobject! This type alias
is named "VectorND" instead of "Vector" to avoid potential name
collisions.
"""
VectorNDLike: TypeAlias = Union[npt.NDArray[PointDType], Sequence[float]]
"""``shape (N,)``
An :math:`N`-dimensional vector: ``[float, ...]``.
This represents anything which can be converted to a :class:`VectorND` NumPy
array.
.. caution::
Do not confuse with the :class:`~.Vector` VMobject! This type alias
is named "VectorND" instead of "Vector" to avoid potential name
@ -465,7 +519,17 @@ An :math:`N`-dimensional vector: ``[float, ...]``.
VectorND_Array: TypeAlias = npt.NDArray[PointDType]
"""``shape (M, N)``
An array of `VectorND` objects: ``[[float, ...], ...]``.
A NumPy array representing a sequence of :class:`VectorND` objects:
``[[float, ...], ...]``.
"""
VectorNDLike_Array: TypeAlias = Union[npt.NDArray[PointDType], Sequence[VectorNDLike]]
"""``shape (M, N)``
An array of :class:`VectorNDLike` objects: ``[[float, ...], ...]``.
This represents anything which can be converted to a :class:`VectorND_Array`
NumPy array.
"""
RowVector: TypeAlias = npt.NDArray[PointDType]
@ -495,7 +559,7 @@ A matrix: ``[[float, ...], [float, ...], ...]``.
Zeros: TypeAlias = MatrixMN
"""``shape: (M, N)``
A `MatrixMN` filled with zeros, typically created with
A :class:`MatrixMN` filled with zeros, typically created with
``numpy.zeros((M, N))``.
"""
@ -508,7 +572,7 @@ Bézier types
QuadraticBezierPoints: TypeAlias = Point3D_Array
"""``shape: (3, 3)``
A `Point3D_Array` of three 3D control points for a single quadratic Bézier
A :class:`Point3D_Array` of three 3D control points for a single quadratic Bézier
curve:
``[[float, float, float], [float, float, float], [float, float, float]]``.
"""
@ -518,7 +582,7 @@ QuadraticBezierPointsLike: TypeAlias = Union[
]
"""``shape: (3, 3)``
A `Point3DLike_Array` of three 3D control points for a single quadratic Bézier
A :class:`Point3DLike_Array` of three 3D control points for a single quadratic Bézier
curve:
``[[float, float, float], [float, float, float], [float, float, float]]``.
@ -529,7 +593,7 @@ This represents anything which can be converted to a
QuadraticBezierPoints_Array: TypeAlias = npt.NDArray[PointDType]
"""``shape: (N, 3, 3)``
A NumPy array containing :math:`N` `QuadraticBezierPoints` objects:
A NumPy array containing :math:`N` :class:`QuadraticBezierPoints` objects:
``[[[float, float, float], [float, float, float], [float, float, float]], ...]``.
"""
@ -538,7 +602,7 @@ QuadraticBezierPointsLike_Array: TypeAlias = Union[
]
"""``shape: (N, 3, 3)``
A sequence of :math:`N` `QuadraticBezierPointsLike` objects:
A sequence of :math:`N` :class:`QuadraticBezierPointsLike` objects:
``[[[float, float, float], [float, float, float], [float, float, float]], ...]``.
This represents anything which can be converted to a
@ -548,7 +612,7 @@ This represents anything which can be converted to a
QuadraticBezierPath: TypeAlias = Point3D_Array
"""``shape: (3*N, 3)``
A `Point3D_Array` of :math:`3N` points, where each one of the
A :class:`Point3D_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], ...], ...]``.
@ -560,7 +624,7 @@ further type information.
QuadraticBezierPathLike: TypeAlias = Point3DLike_Array
"""``shape: (3*N, 3)``
A `Point3DLike_Array` of :math:`3N` points, where each one of the
A :class:`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], ...], ...]``.
@ -575,7 +639,7 @@ further type information.
QuadraticSpline: TypeAlias = QuadraticBezierPath
"""``shape: (3*N, 3)``
A special case of `QuadraticBezierPath` where all the :math:`N`
A special case of :class:`QuadraticBezierPath` where all the :math:`N`
quadratic Bézier curves are connected, forming a quadratic spline:
``[[float, float, float], ...], ...]``.
@ -586,7 +650,7 @@ further type information.
QuadraticSplineLike: TypeAlias = QuadraticBezierPathLike
"""``shape: (3*N, 3)``
A special case of `QuadraticBezierPathLike` where all the :math:`N`
A special case of :class:`QuadraticBezierPathLike` where all the :math:`N`
quadratic Bézier curves are connected, forming a quadratic spline:
``[[float, float, float], ...], ...]``.
@ -600,7 +664,7 @@ 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:
A :class:`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]]``.
"""
@ -609,7 +673,7 @@ CubicBezierPointsLike: TypeAlias = Union[
]
"""``shape: (4, 3)``
A `Point3DLike_Array` of 4 control points for a single cubic Bézier curve:
A :class:`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`
@ -619,7 +683,7 @@ NumPy array.
CubicBezierPoints_Array: TypeAlias = npt.NDArray[PointDType]
"""``shape: (N, 4, 3)``
A NumPy array containing :math:`N` `CubicBezierPoints` objects:
A NumPy array containing :math:`N` :class:`CubicBezierPoints` objects:
``[[[float, float, float], [float, float, float], [float, float, float], [float, float, float]], ...]``.
"""
@ -628,7 +692,7 @@ CubicBezierPointsLike_Array: TypeAlias = Union[
]
"""``shape: (N, 4, 3)``
A sequence of :math:`N` `CubicBezierPointsLike` objects:
A sequence of :math:`N` :class:`CubicBezierPointsLike` objects:
``[[[float, float, float], [float, float, float], [float, float, float], [float, float, float]], ...]``.
This represents anything which can be converted to a
@ -638,7 +702,7 @@ This represents anything which can be converted to a
CubicBezierPath: TypeAlias = Point3D_Array
"""``shape: (4*N, 3)``
A `Point3D_Array` of :math:`4N` points, where each one of the
A :class:`Point3D_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], ...], ...]``.
@ -650,7 +714,7 @@ further type information.
CubicBezierPathLike: TypeAlias = Point3DLike_Array
"""``shape: (4*N, 3)``
A `Point3DLike_Array` of :math:`4N` points, where each one of the
A :class:`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], ...], ...]``.
@ -665,7 +729,7 @@ further type information.
CubicSpline: TypeAlias = CubicBezierPath
"""``shape: (4*N, 3)``
A special case of `CubicBezierPath` where all the :math:`N` cubic
A special case of :class:`CubicBezierPath` where all the :math:`N` cubic
Bézier curves are connected, forming a quadratic spline:
``[[float, float, float], ...], ...]``.
@ -676,7 +740,7 @@ further type information.
CubicSplineLike: TypeAlias = CubicBezierPathLike
"""``shape: (4*N, 3)``
A special case of `CubicBezierPath` where all the :math:`N` cubic
A special case of :class:`CubicBezierPath` where all the :math:`N` cubic
Bézier curves are connected, forming a quadratic spline:
``[[float, float, float], ...], ...]``.
@ -690,7 +754,7 @@ further type information.
BezierPoints: TypeAlias = Point3D_Array
r"""``shape: (PPC, 3)``
A `Point3D_Array` of :math:`\text{PPC}` control points
A :class:`Point3D_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], ...]``.
@ -702,7 +766,7 @@ further type information.
BezierPointsLike: TypeAlias = Point3DLike_Array
r"""``shape: (PPC, 3)``
A `Point3DLike_Array` of :math:`\text{PPC}` control points
A :class:`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], ...]``.
@ -717,8 +781,8 @@ further type information.
BezierPoints_Array: TypeAlias = npt.NDArray[PointDType]
r"""``shape: (N, PPC, 3)``
A NumPy array of :math:`N` `BezierPoints` objects containing
:math:`\text{PPC}` `Point3D` objects each
A NumPy array of :math:`N` :class:`BezierPoints` objects containing
:math:`\text{PPC}` :class:`Point3D` objects each
(:math:`\text{PPC: Points Per Curve} = n + 1`):
``[[[float, float, float], ...], ...]``.
@ -731,8 +795,8 @@ BezierPointsLike_Array: TypeAlias = Union[
]
r"""``shape: (N, PPC, 3)``
A sequence of :math:`N` `BezierPointsLike` objects containing
:math:`\text{PPC}` `Point3DLike` objects each
A sequence of :math:`N` :class:`BezierPointsLike` objects containing
:math:`\text{PPC}` :class:`Point3DLike` objects each
(:math:`\text{PPC: Points Per Curve} = n + 1`):
``[[[float, float, float], ...], ...]``.
@ -746,7 +810,7 @@ further type information.
BezierPath: TypeAlias = Point3D_Array
r"""``shape: (PPC*N, 3)``
A `Point3D_Array` of :math:`\text{PPC} \cdot N` points, where each
A :class:`Point3D_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:
@ -759,7 +823,7 @@ further type information.
BezierPathLike: TypeAlias = Point3DLike_Array
r"""``shape: (PPC*N, 3)``
A `Point3DLike_Array` of :math:`\text{PPC} \cdot N` points, where each
A :class:`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:
@ -775,8 +839,8 @@ further type information.
Spline: TypeAlias = BezierPath
r"""``shape: (PPC*N, 3)``
A special case of `BezierPath` where all the :math:`N` Bézier curves
consisting of :math:`\text{PPC}` `Point3D` objects
A special case of :class:`BezierPath` where all the :math:`N` Bézier curves
consisting of :math:`\text{PPC}` :class:`Point3D` objects
(:math:`\text{PPC: Points Per Curve} = n + 1`) are connected, forming
an :math:`n`-th degree spline:
``[[float, float, float], ...], ...]``.
@ -788,8 +852,8 @@ 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
A special case of :class:`BezierPathLike` where all the :math:`N` Bézier curves
consisting of :math:`\text{PPC}` :class:`Point3D` objects
(:math:`\text{PPC: Points Per Curve} = n + 1`) are connected, forming
an :math:`n`-th degree spline:
``[[float, float, float], ...], ...]``.
@ -851,29 +915,29 @@ A rasterized image with a height of ``height`` pixels and a width of
Every value in the array is an integer from 0 to 255.
Every pixel is represented either by a single integer indicating its
lightness (for greyscale images), an `RGB_Array_Int` or an
lightness (for greyscale images), an :class:`RGB_Array_Int` or an
`RGBA_Array_Int`.
"""
GrayscalePixelArray: TypeAlias = PixelArray
"""``shape: (height, width)``
A 100% opaque grayscale `PixelArray`, where every pixel value is a
A 100% opaque grayscale :class:`PixelArray`, where every pixel value is a
`ManimInt` indicating its lightness (black -> gray -> white).
"""
RGBPixelArray: TypeAlias = PixelArray
"""``shape: (height, width, 3)``
A 100% opaque `PixelArray` in color, where every pixel value is an
A 100% opaque :class:`PixelArray` in color, where every pixel value is an
`RGB_Array_Int` object.
"""
RGBAPixelArray: TypeAlias = PixelArray
"""``shape: (height, width, 4)``
A `PixelArray` in color where pixels can be transparent. Every pixel
value is an `RGBA_Array_Int` object.
A :class:`PixelArray` in color where pixels can be transparent. Every pixel
value is an :class:`RGBA_Array_Int` object.
"""

View file

@ -16,10 +16,15 @@ import numpy as np
from ..constants import OUT
from ..utils.bezier import interpolate
from ..utils.space_ops import rotation_matrix
from ..utils.space_ops import normalize, rotation_matrix
if TYPE_CHECKING:
from manim.typing import PathFuncType, Point3D_Array, Vector3D
from manim.typing import (
PathFuncType,
Point3D_Array,
Point3DLike_Array,
Vector3DLike,
)
STRAIGHT_PATH_THRESHOLD = 0.01
@ -72,7 +77,7 @@ def straight_path() -> PathFuncType:
def path_along_circles(
arc_angle: float, circles_centers: np.ndarray, axis: Vector3D = OUT
arc_angle: float, circles_centers: Point3DLike_Array, axis: Vector3DLike = OUT
) -> PathFuncType:
"""This function transforms each point by moving it roughly along a circle, each with its own specified center.
@ -132,9 +137,7 @@ def path_along_circles(
self.wait()
"""
if np.linalg.norm(axis) == 0:
axis = OUT
unit_axis = axis / np.linalg.norm(axis)
unit_axis = normalize(axis, fall_back=OUT)
def path(
start_points: Point3D_Array, end_points: Point3D_Array, alpha: float
@ -152,7 +155,7 @@ def path_along_circles(
return path
def path_along_arc(arc_angle: float, axis: Vector3D = OUT) -> PathFuncType:
def path_along_arc(arc_angle: float, axis: Vector3DLike = OUT) -> PathFuncType:
"""This function transforms each point by moving it along a circular arc.
Parameters
@ -204,9 +207,7 @@ def path_along_arc(arc_angle: float, axis: Vector3D = OUT) -> PathFuncType:
"""
if abs(arc_angle) < STRAIGHT_PATH_THRESHOLD:
return straight_path()
if np.linalg.norm(axis) == 0:
axis = OUT
unit_axis = axis / np.linalg.norm(axis)
unit_axis = normalize(axis, fall_back=OUT)
def path(
start_points: Point3D_Array, end_points: Point3D_Array, alpha: float
@ -313,7 +314,7 @@ def counterclockwise_path() -> PathFuncType:
return path_along_arc(np.pi)
def spiral_path(angle: float, axis: Vector3D = OUT) -> PathFuncType:
def spiral_path(angle: float, axis: Vector3DLike = OUT) -> PathFuncType:
"""This function transforms each point by moving along a spiral to its destination.
Parameters
@ -365,9 +366,7 @@ def spiral_path(angle: float, axis: Vector3D = OUT) -> PathFuncType:
"""
if abs(angle) < STRAIGHT_PATH_THRESHOLD:
return straight_path()
if np.linalg.norm(axis) == 0:
axis = OUT
unit_axis = axis / np.linalg.norm(axis)
unit_axis = normalize(axis, fall_back=OUT)
def path(
start_points: Point3D_Array, end_points: Point3D_Array, alpha: float

View file

@ -28,7 +28,8 @@ if TYPE_CHECKING:
Vector2D,
Vector2D_Array,
Vector3D,
Vector3D_Array,
Vector3DLike,
Vector3DLike_Array,
)
__all__ = [
@ -70,7 +71,7 @@ def norm_squared(v: float) -> float:
return val
def cross(v1: Vector3D, v2: Vector3D) -> Vector3D:
def cross(v1: Vector3DLike, v2: Vector3DLike) -> Vector3D:
return np.array(
[
v1[1] * v2[2] - v1[2] * v2[1],
@ -178,8 +179,8 @@ def quaternion_conjugate(quaternion: Sequence[float]) -> np.ndarray:
def rotate_vector(
vector: np.ndarray, angle: float, axis: np.ndarray = OUT
) -> np.ndarray:
vector: Vector3DLike, angle: float, axis: Vector3DLike = OUT
) -> Vector3D:
"""Function for rotating a vector.
Parameters
@ -245,7 +246,7 @@ def rotation_matrix_from_quaternion(quat: np.ndarray) -> np.ndarray:
return np.transpose(rotation_matrix_transpose_from_quaternion(quat))
def rotation_matrix_transpose(angle: float, axis: np.ndarray) -> np.ndarray:
def rotation_matrix_transpose(angle: float, axis: Vector3DLike) -> np.ndarray:
if all(np.array(axis)[:2] == np.zeros(2)):
return rotation_about_z(angle * np.sign(axis[2])).T
return rotation_matrix(angle, axis).T
@ -253,12 +254,12 @@ def rotation_matrix_transpose(angle: float, axis: np.ndarray) -> np.ndarray:
def rotation_matrix(
angle: float,
axis: np.ndarray,
axis: Vector3DLike,
homogeneous: bool = False,
) -> np.ndarray:
"""Rotation in R^3 about a specified axis of rotation."""
inhomogeneous_rotation_matrix = Rotation.from_rotvec(
angle * normalize(np.array(axis))
angle * normalize(axis)
).as_matrix()
if not homogeneous:
return inhomogeneous_rotation_matrix
@ -388,7 +389,7 @@ def normalize_along_axis(array: np.ndarray, axis: np.ndarray) -> np.ndarray:
return array
def get_unit_normal(v1: Vector3D, v2: Vector3D, tol: float = 1e-6) -> Vector3D:
def get_unit_normal(v1: Vector3DLike, v2: Vector3DLike, tol: float = 1e-6) -> Vector3D:
"""Gets the unit normal of the vectors.
Parameters
@ -405,18 +406,21 @@ def get_unit_normal(v1: Vector3D, v2: Vector3D, tol: float = 1e-6) -> Vector3D:
np.ndarray
The normal of the two vectors.
"""
np_v1 = np.asarray(v1)
np_v2 = np.asarray(v2)
# Instead of normalizing v1 and v2, just divide by the greatest
# of all their absolute components, which is just enough
div1, div2 = max(np.abs(v1)), max(np.abs(v2))
div1, div2 = max(np.abs(np_v1)), max(np.abs(np_v2))
if div1 == 0.0:
if div2 == 0.0:
return DOWN
u = v2 / div2
u = np_v2 / div2
elif div2 == 0.0:
u = v1 / div1
u = np_v1 / div1
else:
# Normal scenario: v1 and v2 are both non-null
u1, u2 = v1 / div1, v2 / div2
u1, u2 = np_v1 / div1, np_v2 / div2
cp = cross(u1, u2)
cp_norm = np.sqrt(norm_squared(cp))
if cp_norm > tol:
@ -590,9 +594,9 @@ def line_intersection(
def find_intersection(
p0s: Point3DLike_Array,
v0s: Vector3D_Array,
v0s: Vector3DLike_Array,
p1s: Point3DLike_Array,
v1s: Vector3D_Array,
v1s: Vector3DLike_Array,
threshold: float = 1e-5,
) -> list[Point3D]:
"""
@ -605,7 +609,7 @@ def find_intersection(
# algorithm from https://en.wikipedia.org/wiki/Skew_lines#Nearest_points
result = []
for p0, v0, p1, v1 in zip(*[p0s, v0s, p1s, v1s]):
for p0, v0, p1, v1 in zip(p0s, v0s, p1s, v1s):
normal = cross(v1, cross(v0, v1))
denom = max(np.dot(v0, normal), threshold)
result += [p0 + np.dot(p1 - p0, normal) / denom * v0]