Skip to content
Open
52 changes: 27 additions & 25 deletions manim/animation/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@
import inspect
import types
from collections.abc import Callable, Iterable, Sequence
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, Self

import numpy as np

from manim.data_structures import MethodWithArgs
from manim.mobject.opengl.opengl_mobject import OpenGLGroup, OpenGLMobject
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
from manim.mobject.types.vectorized_mobject import VMobject

from .. import config
from ..animation.animation import Animation
Expand Down Expand Up @@ -66,6 +68,8 @@ class Transform(Animation):
path_func
A function defining the path that the points of the ``mobject`` are being moved
along until they match the points of the ``target_mobject``, see :mod:`.utils.paths`.
If path_func is None, it defaults to a straight line,
which is set up inside the path_arc setter.
path_arc
The arc angle (in radians) that the points of ``mobject`` will follow to reach
the points of the target if using a circular path arc, see ``path_arc_centers``.
Expand Down Expand Up @@ -142,26 +146,24 @@ def __init__(
replace_mobject_with_target_in_scene: bool = False,
**kwargs,
) -> None:
self.path_arc_axis: np.ndarray = path_arc_axis
self.path_arc_centers: Point3DLike | Point3DLike_Array | None = path_arc_centers
self.path_arc: float = path_arc

# path_func is a property a few lines below so it doesn't need to be set in any case
self.path_arc_axis = path_arc_axis
self.path_arc_centers = path_arc_centers
self.path_arc = path_arc
if path_func is not None:
self.path_func: Callable = path_func
self.path_func = path_func
elif self.path_arc_centers is not None:
self.path_func = path_along_circles(
path_arc,
self.path_arc_centers,
self.path_arc_axis,
)

self.replace_mobject_with_target_in_scene: bool = (
replace_mobject_with_target_in_scene
)
self.target_mobject: Mobject = (
self.replace_mobject_with_target_in_scene = replace_mobject_with_target_in_scene

self.target_mobject = (
target_mobject if target_mobject is not None else Mobject()
)

super().__init__(mobject, **kwargs)

@property
Expand Down Expand Up @@ -240,11 +242,11 @@ def get_all_families_zipped(self) -> Iterable[tuple]: # more precise typing?

def interpolate_submobject(
self,
submobject: Mobject,
starting_submobject: Mobject,
target_copy: Mobject,
submobject: VMobject | OpenGLVMobject,
starting_submobject: VMobject | OpenGLVMobject,
target_copy: VMobject | OpenGLVMobject,
alpha: float,
) -> Transform:
) -> Self:
submobject.interpolate(starting_submobject, target_copy, alpha, self.path_func)
return self

Expand Down Expand Up @@ -461,7 +463,9 @@ class ApplyMethod(Transform):
Parameters
----------
method
The method that will be applied in the animation.
The bound method of a Mobject that will be applied in the animation.
Pass the method itself, not its result.
For example, pass ``sq.shift`` not ``sq.shift(UP)``.
args
Any positional arguments to be passed when applying the method.
kwargs
Expand All @@ -482,6 +486,7 @@ def check_validity_of_input(self, method: Callable) -> None:
raise ValueError(
"Whoops, looks like you accidentally invoked "
"the method you want to animate",
"Pass the method itself, e.g. ``sq.shift`` instead of ``sq.shift(UP)``.",
)
assert isinstance(method.__self__, (Mobject, OpenGLMobject))

Expand Down Expand Up @@ -681,24 +686,21 @@ def initialize_matrix(self, matrix: np.ndarray) -> np.ndarray:
new_matrix[:2, :2] = matrix
matrix = new_matrix
elif matrix.shape != (3, 3):
raise ValueError("Matrix has bad dimensions")
raise ValueError(
"Only a 2D Matrix having shape either (2,2) or (3,3) is accepted."
)
return matrix


class ApplyComplexFunction(ApplyMethod):
def __init__(self, function: types.MethodType, mobject: Mobject, **kwargs) -> None:
self.function = function
method = mobject.apply_complex_function
rotational_component = function(complex(1)) - function(complex(0))
arc = np.log(rotational_component).imag if rotational_component != 0 else 0
kwargs["path_arc"] = arc if np.isfinite(arc) else 0
super().__init__(method, function, **kwargs)

def _init_path_func(self) -> None:
func1 = self.function(complex(1))
self.path_arc = np.log(func1).imag
super()._init_path_func()


###


class CyclicReplace(Transform):
"""An animation moving mobjects cyclically.
Expand Down
Loading