Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add type annotations to manim.utils.* #3999

Merged
merged 52 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
45d84fd
Handled mypy issues in utils/bezier.py
henrikmidtiby Dec 28, 2024
c2a715a
Disable mypy errors in manim.utils.*
henrikmidtiby Nov 4, 2024
40250de
Fix mypy errors in utils/unit.py
henrikmidtiby Nov 4, 2024
910aa3f
Handle mypy errors in utils/debug.py
henrikmidtiby Nov 4, 2024
8bf07ca
Fix mypy issues in utils.color.*
henrikmidtiby Nov 4, 2024
5d2ce00
Avoid circular import.
henrikmidtiby Nov 4, 2024
ec07a4f
Handle mypy errors in utils.simple_functions.*
henrikmidtiby Nov 5, 2024
bb3ec8a
Handle my errors in utils.testing.*
henrikmidtiby Nov 5, 2024
d6210c3
Avoid circular import.
henrikmidtiby Nov 5, 2024
21599b1
Handle mypy errors in utils/family_ops.py
henrikmidtiby Nov 6, 2024
4997a37
Handle mypy errors in utils/parameter_parsing.py
henrikmidtiby Nov 6, 2024
3be6129
Handle some of the mypy errors in utils.docbuild.*
henrikmidtiby Dec 28, 2024
3cabd9e
Handle mypy errors for utils/config_ops.py
henrikmidtiby Nov 6, 2024
29792df
Handle mypy errors from utils/commands.py
henrikmidtiby Nov 6, 2024
9f0be52
Handle mypy errors in utils/tex_templates.py
henrikmidtiby Nov 6, 2024
111775b
Handle mypy errors in utils/space_ops.py
henrikmidtiby Nov 6, 2024
459092e
Fixed most type errors in utils/rate_functions.py
henrikmidtiby Nov 8, 2024
be12a0f
Handle type errors in utils/paths.py
henrikmidtiby Nov 8, 2024
713a03e
Handled type errors in utils/deprecation.py
henrikmidtiby Nov 8, 2024
0970168
Handle type errors in utils/tex_file_writing.py
henrikmidtiby Nov 8, 2024
37d9071
Handle type error in utils/sounds.py
henrikmidtiby Nov 8, 2024
c1a154a
Handle type errors in utils/opengl.py
henrikmidtiby Nov 9, 2024
15e10b2
Handle type errors in utils/module_ops.py
henrikmidtiby Nov 9, 2024
1569831
Handle type errors in utils/caching.py, utils/familily.py and utils/i…
henrikmidtiby Nov 9, 2024
714b49a
Handle type errors in utils/file_ops.py
henrikmidtiby Nov 9, 2024
1b54065
Handle type errors in utils/ipython_magic.py
henrikmidtiby Nov 9, 2024
7bf0312
Update manim/utils/testing/_frames_testers.py
henrikmidtiby Dec 28, 2024
8e15d0c
Addressing comments from chopan50, block 1
henrikmidtiby Dec 28, 2024
36906b8
Addressing comments from chopan50, block 2
henrikmidtiby Dec 28, 2024
0bfe02f
Addressing comments from chopan50, block 3
henrikmidtiby Dec 28, 2024
7b8a529
Addressing comments from chopan50, block 4
henrikmidtiby Dec 28, 2024
67acc91
Addressing comments from chopan50, block 5
henrikmidtiby Dec 28, 2024
2a29354
Addressing comments from chopan50, block 7
henrikmidtiby Dec 28, 2024
a50dd1a
Addressing comments from chopan50, block 8
henrikmidtiby Dec 28, 2024
e055ba1
Addressing comments from chopan50, block 9
henrikmidtiby Dec 28, 2024
d5686d1
Addressing comments from chopan50, block 9
henrikmidtiby Dec 28, 2024
b6b1ff0
Addressing comments from chopan50, block 10
henrikmidtiby Jan 1, 2025
f3ed2b1
Addressing comments from chopan50, block 11
henrikmidtiby Jan 1, 2025
3a4bd5b
Addressing comments from chopan50, block 12
henrikmidtiby Jan 1, 2025
98ff7a8
Addressing comments from chopan50, block 13
henrikmidtiby Jan 1, 2025
44a8645
Addressing comments from chopan50, block 14
henrikmidtiby Jan 1, 2025
e1db9ea
Addressing comments from chopan50, block 15
henrikmidtiby Jan 1, 2025
de80832
Addressing comments from chopan50, block 16
henrikmidtiby Jan 1, 2025
0736393
Addressing comments from chopan50, block 17
henrikmidtiby Jan 1, 2025
711b2d8
Addressing comments from chopan50, block 18
henrikmidtiby Jan 1, 2025
3f0d9ac
Removed some more files from mypy.ini and fix remaining errors
chopan050 Jan 2, 2025
35e6597
Fix errors in rate_functions.py and be compatible with Python 3.9
chopan050 Jan 2, 2025
8cce30f
Remove incorrect use of typing.Concatenate from rate_functions.py
chopan050 Jan 2, 2025
3bb4f6b
ManimConfig dirs have always been strings - Fix manim_directive.py My…
chopan050 Jan 2, 2025
0436034
Fix StrList error in manim_directive.py
chopan050 Jan 2, 2025
3c5183c
Fix or ignore errors in manim.utils.docbuild
chopan050 Jan 2, 2025
6931d36
Reverse points in manim.utils.rate_functions.running_start
chopan050 Jan 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion manim/mobject/text/numbers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
__all__ = ["DecimalNumber", "Integer", "Variable"]

from collections.abc import Sequence
from typing import Any

import numpy as np

Expand Down Expand Up @@ -327,7 +328,9 @@ def construct(self):
self.add(Integer(number=6.28).set_x(-1.5).set_y(-2).set_color(YELLOW).scale(1.4))
"""

def __init__(self, number=0, num_decimal_places=0, **kwargs):
def __init__(
self, number: float = 0, num_decimal_places: int = 0, **kwargs: Any
) -> None:
super().__init__(number=number, num_decimal_places=num_decimal_places, **kwargs)

def get_value(self):
Expand Down
4 changes: 3 additions & 1 deletion manim/renderer/cairo_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from manim.animation.animation import Animation
from manim.scene.scene import Scene

from ..typing import PixelArray

__all__ = ["CairoRenderer"]


Expand Down Expand Up @@ -158,7 +160,7 @@ def render(self, scene, time, moving_mobjects):
self.update_frame(scene, moving_mobjects)
self.add_frame(self.get_frame())

def get_frame(self):
def get_frame(self) -> PixelArray:
"""
Gets the current frame as NumPy array.

Expand Down
2 changes: 1 addition & 1 deletion manim/renderer/opengl_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ def get_texture_id(self, path):

return self.path_to_texture_id[repr(path)]

def update_skipping_status(self):
def update_skipping_status(self) -> None:
"""
This method is used internally to check if the current
animation needs to be skipped or not. It also checks if
Expand Down
2 changes: 1 addition & 1 deletion manim/scene/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ def add(self, *mobjects: Mobject):
self.moving_mobjects += mobjects
return self

def add_mobjects_from_animations(self, animations):
def add_mobjects_from_animations(self, animations: list[Animation]) -> None:
curr_mobjects = self.get_mobject_family_members()
for animation in animations:
if animation.is_introducer():
Expand Down
18 changes: 13 additions & 5 deletions manim/scene/scene_file_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from pydub import AudioSegment

from manim import __version__
from manim.typing import PixelArray
from manim.typing import PixelArray, StrPath

from .. import config, logger
from .._config.logger_utils import set_file_logger
Expand All @@ -38,6 +38,7 @@
from .section import DefaultSectionType, Section

if TYPE_CHECKING:
from manim.renderer.cairo_renderer import CairoRenderer
Dismissed Show dismissed Hide dismissed
from manim.renderer.opengl_renderer import OpenGLRenderer


Expand Down Expand Up @@ -104,7 +105,12 @@ class SceneFileWriter:

force_output_as_scene_name = False

def __init__(self, renderer, scene_name, **kwargs):
def __init__(
self,
renderer: CairoRenderer | OpenGLRenderer,
scene_name: StrPath,
**kwargs: Any,
) -> None:
self.renderer = renderer
self.init_output_directories(scene_name)
self.init_audio()
Expand All @@ -118,7 +124,7 @@ def __init__(self, renderer, scene_name, **kwargs):
name="autocreated", type_=DefaultSectionType.NORMAL, skip_animations=False
)

def init_output_directories(self, scene_name):
def init_output_directories(self, scene_name: StrPath) -> None:
"""Initialise output directories.

Notes
Expand Down Expand Up @@ -378,7 +384,9 @@ def add_sound(
self.add_audio_segment(new_segment, time, **kwargs)

# Writers
def begin_animation(self, allow_write: bool = False, file_path=None):
def begin_animation(
self, allow_write: bool = False, file_path: StrPath | None = None
) -> None:
"""
Used internally by manim to stream the animation to FFMPEG for
displaying or writing to a file.
Expand All @@ -391,7 +399,7 @@ def begin_animation(self, allow_write: bool = False, file_path=None):
if write_to_movie() and allow_write:
self.open_partial_movie_stream(file_path=file_path)

def end_animation(self, allow_write: bool = False):
def end_animation(self, allow_write: bool = False) -> None:
"""
Internally used by Manim to stop streaming to
FFMPEG gracefully.
Expand Down
73 changes: 43 additions & 30 deletions manim/utils/bezier.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,12 @@
from manim.utils.simple_functions import choose

if TYPE_CHECKING:
import numpy.typing as npt

from manim.typing import (
BezierPoints,
BezierPoints_Array,
BezierPointsLike,
BezierPointsLike_Array,
ColVector,
ManimFloat,
MatrixMN,
Point3D,
Point3D_Array,
Expand All @@ -64,7 +61,9 @@ def bezier(
) -> Callable[[float | ColVector], Point3D_Array]: ...


def bezier(points):
def bezier(
points: Point3D_Array | Sequence[Point3D_Array],
) -> Callable[[float | ColVector], Point3D_Array]:
"""Classic implementation of a Bézier curve.

Parameters
Expand Down Expand Up @@ -118,21 +117,21 @@ def bezier(points):

if degree == 0:

def zero_bezier(t):
def zero_bezier(t: float | ColVector) -> Point3D | Point3D_Array:
return np.ones_like(t) * P[0]

return zero_bezier

if degree == 1:

def linear_bezier(t):
def linear_bezier(t: float | ColVector) -> Point3D | Point3D_Array:
return P[0] + t * (P[1] - P[0])

return linear_bezier

if degree == 2:

def quadratic_bezier(t):
def quadratic_bezier(t: float | ColVector) -> Point3D | Point3D_Array:
t2 = t * t
mt = 1 - t
mt2 = mt * mt
Expand All @@ -142,7 +141,7 @@ def quadratic_bezier(t):

if degree == 3:

def cubic_bezier(t):
def cubic_bezier(t: float | ColVector) -> Point3D | Point3D_Array:
t2 = t * t
t3 = t2 * t
mt = 1 - t
Expand All @@ -152,11 +151,12 @@ def cubic_bezier(t):

return cubic_bezier

def nth_grade_bezier(t):
def nth_grade_bezier(t: float | ColVector) -> Point3D | Point3D_Array:
is_scalar = not isinstance(t, np.ndarray)
if is_scalar:
B = np.empty((1, *P.shape))
else:
assert isinstance(t, np.ndarray)
t = t.reshape(-1, *[1 for dim in P.shape])
B = np.empty((t.shape[0], *P.shape))
B[:] = P
Expand All @@ -169,7 +169,8 @@ def nth_grade_bezier(t):
# In the end, there shall be the evaluation at t of a single Bezier curve of
# grade d, stored in the first slot of B
if is_scalar:
return B[0, 0]
val: Point3D = B[0, 0]
return val
return B[:, 0]

return nth_grade_bezier
Expand Down Expand Up @@ -1026,7 +1027,11 @@ def interpolate(start: Point3D, end: Point3D, alpha: float) -> Point3D: ...
def interpolate(start: Point3D, end: Point3D, alpha: ColVector) -> Point3D_Array: ...


def interpolate(start, end, alpha):
def interpolate(
start: float | Point3D,
end: float | Point3D,
alpha: float | ColVector,
) -> float | ColVector | Point3D | Point3D_Array:
"""Linearly interpolates between two values ``start`` and ``end``.

Parameters
Expand Down Expand Up @@ -1139,7 +1144,9 @@ def inverse_interpolate(start: Point3D, end: Point3D, value: Point3D) -> Point3D


def inverse_interpolate(
start: float | Point3D, end: float | Point3D, value: float | Point3D
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``
Expand Down Expand Up @@ -1234,7 +1241,7 @@ def match_interpolate(
return interpolate(
new_start,
new_end,
old_alpha, # type: ignore[arg-type]
old_alpha,
)


Expand Down Expand Up @@ -1270,7 +1277,8 @@ def get_smooth_cubic_bezier_handle_points(
# they can only be an interpolation of these two anchors with alphas
# 1/3 and 2/3, which will draw a straight line between the anchors.
if n_anchors == 2:
return interpolate(anchors[0], anchors[1], np.array([[1 / 3], [2 / 3]]))
val = interpolate(anchors[0], anchors[1], np.array([[1 / 3], [2 / 3]]))
return (val[0], val[1])

# Handle different cases depending on whether the points form a closed
# curve or not
Expand Down Expand Up @@ -1745,7 +1753,12 @@ def get_quadratic_approximation_of_cubic(
) -> QuadraticBezierPath: ...


def get_quadratic_approximation_of_cubic(a0, h0, h1, a1):
def get_quadratic_approximation_of_cubic(
a0: Point3D | Point3D_Array,
h0: Point3D | Point3D_Array,
h1: Point3D | Point3D_Array,
a1: Point3D | Point3D_Array,
) -> QuadraticSpline | QuadraticBezierPath:
r"""If ``a0``, ``h0``, ``h1`` and ``a1`` are the control points of a cubic
Bézier curve, approximate the curve with two quadratic Bézier curves and
return an array of 6 points, where the first 3 points represent the first
Expand Down Expand Up @@ -1849,33 +1862,33 @@ def get_quadratic_approximation_of_cubic(a0, h0, h1, a1):
If ``a0``, ``h0``, ``h1`` and ``a1`` have different dimensions, or
if their number of dimensions is not 1 or 2.
"""
a0 = np.asarray(a0)
h0 = np.asarray(h0)
h1 = np.asarray(h1)
a1 = np.asarray(a1)

if all(arr.ndim == 1 for arr in (a0, h0, h1, a1)):
num_curves, dim = 1, a0.shape[0]
elif all(arr.ndim == 2 for arr in (a0, h0, h1, a1)):
num_curves, dim = a0.shape
a0c = np.asarray(a0)
h0c = np.asarray(h0)
h1c = np.asarray(h1)
a1c = np.asarray(a1)

if all(arr.ndim == 1 for arr in (a0c, h0c, h1c, a1c)):
num_curves, dim = 1, a0c.shape[0]
elif all(arr.ndim == 2 for arr in (a0c, h0c, h1c, a1c)):
num_curves, dim = a0c.shape
else:
raise ValueError("All arguments must be Point3D or Point3D_Array.")

m0 = 0.25 * (3 * h0 + a0)
m1 = 0.25 * (3 * h1 + a1)
m0 = 0.25 * (3 * h0c + a0c)
m1 = 0.25 * (3 * h1c + a1c)
k = 0.5 * (m0 + m1)

result = np.empty((6 * num_curves, dim))
result[0::6] = a0
result[0::6] = a0c
result[1::6] = m0
result[2::6] = k
result[3::6] = k
result[4::6] = m1
result[5::6] = a1
result[5::6] = a1c
return result


def is_closed(points: Point3DLike_Array) -> bool:
def is_closed(points: Point3D_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.
Expand Down Expand Up @@ -1952,7 +1965,7 @@ def proportions_along_bezier_curve_for_point(
point: Point3DLike,
control_points: BezierPointsLike,
round_to: float = 1e-6,
) -> npt.NDArray[ManimFloat]:
) -> MatrixMN:
"""Obtains the proportion along the bezier curve corresponding to a given point
given the bezier curve's control points.

Expand Down
15 changes: 11 additions & 4 deletions manim/utils/caching.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
from __future__ import annotations

from typing import Callable
from typing import TYPE_CHECKING, Callable

from .. import config, logger
from ..utils.hashing import get_hash_from_play_call

__all__ = ["handle_caching_play"]

if TYPE_CHECKING:
from typing import Any

def handle_caching_play(func: Callable[..., None]):
from manim.renderer.opengl_renderer import OpenGLRenderer
Dismissed Show dismissed Hide dismissed
from manim.scene.scene import Scene
Dismissed Show dismissed Hide dismissed


def handle_caching_play(func: Callable[..., None]) -> Callable[..., None]:
henrikmidtiby marked this conversation as resolved.
Show resolved Hide resolved
"""Decorator that returns a wrapped version of func that will compute
the hash of the play invocation.

Expand All @@ -28,7 +34,7 @@ def handle_caching_play(func: Callable[..., None]):
# the play logic of the latter has to be refactored in the same way the cairo renderer has been, and thus this
# method has to be deleted.

def wrapper(self, scene, *args, **kwargs):
def wrapper(self: OpenGLRenderer, scene: Scene, *args: Any, **kwargs: Any) -> None:
self.skip_animations = self._original_skipping_status
self.update_skipping_status()
animations = scene.compile_animations(*args, **kwargs)
Expand All @@ -43,8 +49,9 @@ def wrapper(self, scene, *args, **kwargs):
return
if not config["disable_caching"]:
mobjects_on_scene = scene.mobjects
# TODO: the first argument seems wrong. Shouldn't it be scene instead?
hash_play = get_hash_from_play_call(
self,
self, # type: ignore[arg-type]
self.camera,
animations,
mobjects_on_scene,
Expand Down
Loading
Loading