2022-02-15 18:39:45 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.animation.animation import Animation
|
2019-02-05 15:39:58 -08:00
|
|
|
from manimlib.utils.rate_functions import linear
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2022-02-15 18:39:45 +08:00
|
|
|
from typing import TYPE_CHECKING
|
2022-02-16 21:08:25 +08:00
|
|
|
|
2022-02-15 18:39:45 +08:00
|
|
|
if TYPE_CHECKING:
|
2022-04-12 19:19:59 +08:00
|
|
|
from typing import Callable, Sequence
|
|
|
|
|
2022-02-15 18:39:45 +08:00
|
|
|
import numpy as np
|
2022-04-12 19:19:59 +08:00
|
|
|
|
2022-02-15 18:39:45 +08:00
|
|
|
from manimlib.mobject.mobject import Mobject
|
2023-02-15 20:54:59 -08:00
|
|
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
2022-02-15 18:39:45 +08:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class Homotopy(Animation):
|
2022-12-14 14:58:25 -08:00
|
|
|
apply_function_config: dict = dict()
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2022-02-15 18:39:45 +08:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
homotopy: Callable[[float, float, float, float], Sequence[float]],
|
|
|
|
mobject: Mobject,
|
2022-12-14 14:58:25 -08:00
|
|
|
run_time: float = 3.0,
|
2022-02-15 18:39:45 +08:00
|
|
|
**kwargs
|
|
|
|
):
|
2018-03-31 15:11:35 -07:00
|
|
|
"""
|
2019-02-09 15:12:35 -08:00
|
|
|
Homotopy is a function from
|
|
|
|
(x, y, z, t) to (x', y', z')
|
2018-03-31 15:11:35 -07:00
|
|
|
"""
|
2019-02-09 15:12:35 -08:00
|
|
|
self.homotopy = homotopy
|
2022-12-14 14:58:25 -08:00
|
|
|
super().__init__(mobject, run_time=run_time, **kwargs)
|
2019-02-09 15:12:35 -08:00
|
|
|
|
2022-12-14 14:58:25 -08:00
|
|
|
def function_at_time_t(self, t: float) -> Callable[[np.ndarray], Sequence[float]]:
|
|
|
|
def result(p):
|
|
|
|
return self.homotopy(*p, t)
|
|
|
|
return result
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2022-02-15 18:39:45 +08:00
|
|
|
def interpolate_submobject(
|
|
|
|
self,
|
|
|
|
submob: Mobject,
|
|
|
|
start: Mobject,
|
|
|
|
alpha: float
|
|
|
|
) -> None:
|
2021-01-12 07:27:32 -10:00
|
|
|
submob.match_points(start)
|
2018-03-31 15:11:35 -07:00
|
|
|
submob.apply_function(
|
|
|
|
self.function_at_time_t(alpha),
|
2022-12-14 14:58:25 -08:00
|
|
|
**self.apply_function_config
|
2018-03-31 15:11:35 -07:00
|
|
|
)
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class SmoothedVectorizedHomotopy(Homotopy):
|
2022-12-14 14:58:25 -08:00
|
|
|
apply_function_config: dict = dict(make_smooth=True)
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 18:57:21 -07:00
|
|
|
class ComplexHomotopy(Homotopy):
|
2022-02-15 18:39:45 +08:00
|
|
|
def __init__(
|
|
|
|
self,
|
2022-12-14 14:58:25 -08:00
|
|
|
complex_homotopy: Callable[[complex, float], complex],
|
2022-02-15 18:39:45 +08:00
|
|
|
mobject: Mobject,
|
|
|
|
**kwargs
|
|
|
|
):
|
2018-03-31 18:57:21 -07:00
|
|
|
"""
|
2021-02-05 19:58:29 -08:00
|
|
|
Given a function form (z, t) -> w, where z and w
|
|
|
|
are complex numbers and t is time, this animates
|
|
|
|
the state over time
|
2018-03-31 18:57:21 -07:00
|
|
|
"""
|
2018-05-18 16:53:46 -07:00
|
|
|
def homotopy(x, y, z, t):
|
|
|
|
c = complex_homotopy(complex(x, y), t)
|
2018-03-31 18:57:21 -07:00
|
|
|
return (c.real, c.imag, z)
|
2022-12-14 14:58:25 -08:00
|
|
|
|
2021-02-05 19:58:29 -08:00
|
|
|
super().__init__(homotopy, mobject, **kwargs)
|
2018-03-31 18:57:21 -07:00
|
|
|
|
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class PhaseFlow(Animation):
|
2022-02-15 18:39:45 +08:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
function: Callable[[np.ndarray], np.ndarray],
|
|
|
|
mobject: Mobject,
|
2022-12-14 14:58:25 -08:00
|
|
|
virtual_time: float | None = None,
|
|
|
|
suspend_mobject_updating: bool = False,
|
|
|
|
rate_func: Callable[[float], float] = linear,
|
|
|
|
run_time: float =3.0,
|
2022-02-15 18:39:45 +08:00
|
|
|
**kwargs
|
|
|
|
):
|
2019-02-09 15:12:35 -08:00
|
|
|
self.function = function
|
2022-12-14 14:58:25 -08:00
|
|
|
self.virtual_time = virtual_time or run_time
|
|
|
|
super().__init__(
|
|
|
|
mobject,
|
|
|
|
rate_func=rate_func,
|
|
|
|
run_time=run_time,
|
|
|
|
suspend_mobject_updating=suspend_mobject_updating,
|
|
|
|
**kwargs
|
|
|
|
)
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2022-02-15 18:39:45 +08:00
|
|
|
def interpolate_mobject(self, alpha: float) -> None:
|
2018-03-31 15:11:35 -07:00
|
|
|
if hasattr(self, "last_alpha"):
|
2018-04-06 13:58:59 -07:00
|
|
|
dt = self.virtual_time * (alpha - self.last_alpha)
|
2018-03-31 15:11:35 -07:00
|
|
|
self.mobject.apply_function(
|
2018-04-06 13:58:59 -07:00
|
|
|
lambda p: p + dt * self.function(p)
|
2018-03-31 15:11:35 -07:00
|
|
|
)
|
|
|
|
self.last_alpha = alpha
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class MoveAlongPath(Animation):
|
2022-12-14 14:58:25 -08:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
mobject: Mobject,
|
2023-02-15 20:54:59 -08:00
|
|
|
path: VMobject,
|
2022-12-14 14:58:25 -08:00
|
|
|
suspend_mobject_updating: bool = False,
|
|
|
|
**kwargs
|
|
|
|
):
|
2019-02-09 15:12:35 -08:00
|
|
|
self.path = path
|
2022-12-14 14:58:25 -08:00
|
|
|
super().__init__(mobject, suspend_mobject_updating=suspend_mobject_updating, **kwargs)
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2022-02-15 18:39:45 +08:00
|
|
|
def interpolate_mobject(self, alpha: float) -> None:
|
2023-12-02 21:28:22 -06:00
|
|
|
point = self.path.quick_point_from_proportion(self.rate_func(alpha))
|
2018-03-31 15:11:35 -07:00
|
|
|
self.mobject.move_to(point)
|