Skip to content

Commit dbad8a8

Browse files
authored
Rename InternalPoint3D to Point3D, Point3D to Point3DLike and other point-related type aliases (#4027)
1 parent 112c99b commit dbad8a8

File tree

21 files changed

+655
-368
lines changed

21 files changed

+655
-368
lines changed

docs/source/contributing/docs/types.rst

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,51 @@ in space. For example:
2222

2323
.. code-block:: python
2424
25-
def status2D(coord: Point2D) -> None:
25+
def print_point2D(coord: Point2DLike) -> None:
2626
x, y = coord
2727
print(f"Point at {x=},{y=}")
2828
2929
30-
def status3D(coord: Point3D) -> None:
30+
def print_point3D(coord: Point3DLike) -> None:
3131
x, y, z = coord
3232
print(f"Point at {x=},{y=},{z=}")
3333
3434
35-
def get_statuses(coords: Point2D_Array | Point3D_Array) -> None:
35+
def print_point_array(coords: Point2DLike_Array | Point3DLike_Array) -> None:
3636
for coord in coords:
3737
if len(coord) == 2:
38-
# it's a Point2D
39-
status2D(coord)
38+
# it's a Point2DLike
39+
print_point2D(coord)
4040
else:
41-
# it's a point3D
42-
status3D(coord)
43-
44-
It's important to realize that the status functions accepted both
45-
tuples/lists of the correct length, and ``NDArray``'s of the correct shape.
46-
If they only accepted ``NDArray``'s, we would use their ``Internal`` counterparts:
47-
:class:`~.typing.InternalPoint2D`, :class:`~.typing.InternalPoint3D`, :class:`~.typing.InternalPoint2D_Array` and :class:`~.typing.InternalPoint3D_Array`.
48-
49-
In general, the type aliases prefixed with ``Internal`` should never be used on
50-
user-facing classes and functions, but should be reserved for internal behavior.
41+
# it's a Point3DLike
42+
print_point3D(coord)
43+
44+
def shift_point_up(coord: Point3DLike) -> Point3D:
45+
result = np.asarray(coord)
46+
result += UP
47+
print(f"New point: {result}")
48+
return result
49+
50+
Notice that the last function, ``shift_point_up()``, accepts a
51+
:class:`~.Point3DLike` as a parameter and returns a :class:`~.Point3D`. A
52+
:class:`~.Point3D` always represents a NumPy array consisting of 3 floats,
53+
whereas a :class:`~.Point3DLike` can represent anything resembling a 3D point:
54+
either a NumPy array or a tuple/list of 3 floats, hence the ``Like`` word. The
55+
same happens with :class:`~.Point2D`, :class:`~.Point2D_Array` and
56+
:class:`~.Point3D_Array`, and their ``Like`` counterparts
57+
:class:`~.Point2DLike`, :class:`~.Point2DLike_Array` and
58+
:class:`~.Point3DLike_Array`.
59+
60+
The rule for typing functions is: **make parameter types as broad as possible,
61+
and return types as specific as possible.** Therefore, for functions which are
62+
intended to be called by users, **we should always, if possible, accept**
63+
``Like`` **types as parameters and return NumPy, non-** ``Like`` **types.** The
64+
main reason is to be more flexible with users who might want to pass tuples or
65+
lists as arguments rather than NumPy arrays, because it's more convenient. The
66+
last function, ``shift_point_up()``, is an example of it.
67+
68+
Internal functions which are *not* meant to be called by users may accept
69+
non-``Like`` parameters if necessary.
5170

5271
Vectors
5372
~~~~~~~
@@ -61,20 +80,19 @@ consider this slightly contrived function:
6180
def shift_mobject(mob: M, direction: Vector3D, scale_factor: float = 1) -> M:
6281
return mob.shift(direction * scale_factor)
6382
64-
Here we see an important example of the difference. ``direction`` can not, and
65-
should not, be typed as a :class:`~.typing.Point3D` because the function does not accept tuples/lists,
66-
like ``direction=(0, 1, 0)``. You could type it as :class:`~.typing.InternalPoint3D` and
67-
the type checker and linter would be happy; however, this makes the code harder
68-
to understand.
83+
Here we see an important example of the difference. ``direction`` should not be
84+
typed as a :class:`~.Point3D`, because it represents a direction along
85+
which to shift a :class:`~.Mobject`, not a position in space.
6986

7087
As a general rule, if a parameter is called ``direction`` or ``axis``,
7188
it should be type hinted as some form of :class:`~.VectorND`.
7289

7390
.. warning::
7491

7592
This is not always true. For example, as of Manim 0.18.0, the direction
76-
parameter of the :class:`.Vector` Mobject should be ``Point2D | Point3D``,
77-
as it can also accept ``tuple[float, float]`` and ``tuple[float, float, float]``.
93+
parameter of the :class:`.Vector` Mobject should be
94+
``Point2DLike | Point3DLike``, as it can also accept ``tuple[float, float]``
95+
and ``tuple[float, float, float]``.
7896

7997
Colors
8098
------

manim/mobject/geometry/arc.py

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,9 @@ def construct(self):
7171
from manim.mobject.text.tex_mobject import SingleStringMathTex, Tex
7272
from manim.mobject.text.text_mobject import Text
7373
from manim.typing import (
74-
CubicBezierPoints,
75-
InternalPoint3D,
7674
Point3D,
77-
QuadraticBezierPoints,
75+
Point3DLike,
76+
QuadraticSpline,
7877
Vector3D,
7978
)
8079

@@ -269,22 +268,27 @@ def get_tip(self) -> VMobject:
269268
def get_default_tip_length(self) -> float:
270269
return self.tip_length
271270

272-
def get_first_handle(self) -> InternalPoint3D:
271+
def get_first_handle(self) -> Point3D:
273272
# Type inference of extracting an element from a list, is not
274273
# supported by numpy, see this numpy issue
275274
# https://github.com/numpy/numpy/issues/16544
276-
return self.points[1]
275+
first_handle: Point3D = self.points[1]
276+
return first_handle
277277

278-
def get_last_handle(self) -> InternalPoint3D:
279-
return self.points[-2]
278+
def get_last_handle(self) -> Point3D:
279+
# Type inference of extracting an element from a list, is not
280+
# supported by numpy, see this numpy issue
281+
# https://github.com/numpy/numpy/issues/16544
282+
last_handle: Point3D = self.points[-2]
283+
return last_handle
280284

281-
def get_end(self) -> InternalPoint3D:
285+
def get_end(self) -> Point3D:
282286
if self.has_tip():
283287
return self.tip.get_start()
284288
else:
285289
return super().get_end()
286290

287-
def get_start(self) -> InternalPoint3D:
291+
def get_start(self) -> Point3D:
288292
if self.has_start_tip():
289293
return self.start_tip.get_start()
290294
else:
@@ -316,14 +320,14 @@ def __init__(
316320
start_angle: float = 0,
317321
angle: float = TAU / 4,
318322
num_components: int = 9,
319-
arc_center: InternalPoint3D = ORIGIN,
323+
arc_center: Point3DLike = ORIGIN,
320324
**kwargs: Any,
321325
):
322326
if radius is None: # apparently None is passed by ArcBetweenPoints
323327
radius = 1.0
324328
self.radius = radius
325329
self.num_components = num_components
326-
self.arc_center = arc_center
330+
self.arc_center: Point3D = np.asarray(arc_center)
327331
self.start_angle = start_angle
328332
self.angle = angle
329333
self._failed_to_get_center: bool = False
@@ -351,7 +355,7 @@ def init_points(self) -> None:
351355
@staticmethod
352356
def _create_quadratic_bezier_points(
353357
angle: float, start_angle: float = 0, n_components: int = 8
354-
) -> QuadraticBezierPoints:
358+
) -> QuadraticSpline:
355359
samples = np.array(
356360
[
357361
[np.cos(a), np.sin(a), 0]
@@ -394,7 +398,7 @@ def _set_pre_positioned_points(self) -> None:
394398
handles2 = anchors[1:] - (d_theta / 3) * tangent_vectors[1:]
395399
self.set_anchors_and_handles(anchors[:-1], handles1, handles2, anchors[1:])
396400

397-
def get_arc_center(self, warning: bool = True) -> InternalPoint3D:
401+
def get_arc_center(self, warning: bool = True) -> Point3D:
398402
"""Looks at the normals to the first two
399403
anchors, and finds their intersection points
400404
"""
@@ -422,7 +426,7 @@ def get_arc_center(self, warning: bool = True) -> InternalPoint3D:
422426
self._failed_to_get_center = True
423427
return np.array(ORIGIN)
424428

425-
def move_arc_center_to(self, point: InternalPoint3D) -> Self:
429+
def move_arc_center_to(self, point: Point3DLike) -> Self:
426430
self.shift(point - self.get_arc_center())
427431
return self
428432

@@ -454,8 +458,8 @@ def construct(self):
454458

455459
def __init__(
456460
self,
457-
start: Point3D,
458-
end: Point3D,
461+
start: Point3DLike,
462+
end: Point3DLike,
459463
angle: float = TAU / 4,
460464
radius: float | None = None,
461465
**kwargs: Any,
@@ -484,14 +488,18 @@ def __init__(
484488
if radius is None:
485489
center = self.get_arc_center(warning=False)
486490
if not self._failed_to_get_center:
487-
temp_radius: float = np.linalg.norm(np.array(start) - np.array(center))
488-
self.radius = temp_radius
491+
# np.linalg.norm returns floating[Any] which is not compatible with float
492+
self.radius = cast(
493+
float, np.linalg.norm(np.array(start) - np.array(center))
494+
)
489495
else:
490496
self.radius = np.inf
491497

492498

493499
class CurvedArrow(ArcBetweenPoints):
494-
def __init__(self, start_point: Point3D, end_point: Point3D, **kwargs: Any) -> None:
500+
def __init__(
501+
self, start_point: Point3DLike, end_point: Point3DLike, **kwargs: Any
502+
) -> None:
495503
from manim.mobject.geometry.tips import ArrowTriangleFilledTip
496504

497505
tip_shape = kwargs.pop("tip_shape", ArrowTriangleFilledTip)
@@ -500,7 +508,9 @@ def __init__(self, start_point: Point3D, end_point: Point3D, **kwargs: Any) -> N
500508

501509

502510
class CurvedDoubleArrow(CurvedArrow):
503-
def __init__(self, start_point: Point3D, end_point: Point3D, **kwargs: Any) -> None:
511+
def __init__(
512+
self, start_point: Point3DLike, end_point: Point3DLike, **kwargs: Any
513+
) -> None:
504514
if "tip_shape_end" in kwargs:
505515
kwargs["tip_shape"] = kwargs.pop("tip_shape_end")
506516
from manim.mobject.geometry.tips import ArrowTriangleFilledTip
@@ -637,7 +647,7 @@ def construct(self):
637647

638648
@staticmethod
639649
def from_three_points(
640-
p1: Point3D, p2: Point3D, p3: Point3D, **kwargs: Any
650+
p1: Point3DLike, p2: Point3DLike, p3: Point3DLike, **kwargs: Any
641651
) -> Circle:
642652
"""Returns a circle passing through the specified
643653
three points.
@@ -661,7 +671,8 @@ def construct(self):
661671
perpendicular_bisector([np.asarray(p1), np.asarray(p2)]),
662672
perpendicular_bisector([np.asarray(p2), np.asarray(p3)]),
663673
)
664-
radius: float = np.linalg.norm(p1 - center)
674+
# np.linalg.norm returns floating[Any] which is not compatible with float
675+
radius = cast(float, np.linalg.norm(p1 - center))
665676
return Circle(radius=radius, **kwargs).shift(center)
666677

667678

@@ -698,7 +709,7 @@ def construct(self):
698709

699710
def __init__(
700711
self,
701-
point: Point3D = ORIGIN,
712+
point: Point3DLike = ORIGIN,
702713
radius: float = DEFAULT_DOT_RADIUS,
703714
stroke_width: float = 0,
704715
fill_opacity: float = 1.0,
@@ -1006,10 +1017,10 @@ def construct(self):
10061017

10071018
def __init__(
10081019
self,
1009-
start_anchor: CubicBezierPoints,
1010-
start_handle: CubicBezierPoints,
1011-
end_handle: CubicBezierPoints,
1012-
end_anchor: CubicBezierPoints,
1020+
start_anchor: Point3DLike,
1021+
start_handle: Point3DLike,
1022+
end_handle: Point3DLike,
1023+
end_anchor: Point3DLike,
10131024
**kwargs: Any,
10141025
) -> None:
10151026
super().__init__(**kwargs)
@@ -1097,7 +1108,7 @@ def construct(self):
10971108

10981109
def __init__(
10991110
self,
1100-
*vertices: Point3D,
1111+
*vertices: Point3DLike,
11011112
angle: float = PI / 4,
11021113
radius: float | None = None,
11031114
arc_config: list[dict] | None = None,

manim/mobject/geometry/boolean_ops.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
if TYPE_CHECKING:
1616
from typing import Any
1717

18-
from manim.typing import InternalPoint3D_Array, Point2D_Array
18+
from manim.typing import Point2DLike_Array, Point3D_Array, Point3DLike_Array
1919

2020
from ...constants import RendererType
2121

@@ -30,17 +30,17 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
3030

3131
def _convert_2d_to_3d_array(
3232
self,
33-
points: Point2D_Array,
33+
points: Point2DLike_Array | Point3DLike_Array,
3434
z_dim: float = 0.0,
35-
) -> InternalPoint3D_Array:
35+
) -> Point3D_Array:
3636
"""Converts an iterable with coordinates in 2D to 3D by adding
3737
:attr:`z_dim` as the Z coordinate.
3838
3939
Parameters
4040
----------
41-
points:
41+
points
4242
An iterable of points.
43-
z_dim:
43+
z_dim
4444
Default value for the Z coordinate.
4545
4646
Returns

manim/mobject/geometry/labeled.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
if TYPE_CHECKING:
2525
from typing import Any
2626

27-
from manim.typing import Point3D_Array
27+
from manim.typing import Point3DLike_Array
2828

2929

3030
class Label(VGroup):
@@ -344,7 +344,7 @@ def construct(self):
344344

345345
def __init__(
346346
self,
347-
*vertex_groups: Point3D_Array,
347+
*vertex_groups: Point3DLike_Array,
348348
label: str | Tex | MathTex | Text,
349349
precision: float = 0.01,
350350
label_config: dict[str, Any] | None = None,

0 commit comments

Comments
 (0)