-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
movement.py
142 lines (118 loc) · 4.15 KB
/
movement.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""Animations related to movement."""
__all__ = [
"Homotopy",
"SmoothedVectorizedHomotopy",
"ComplexHomotopy",
"PhaseFlow",
"MoveAlongPath",
]
import typing
import numpy as np
from ..animation.animation import Animation
from ..utils.rate_functions import linear
if typing.TYPE_CHECKING:
from ..mobject.mobject import Mobject
class Homotopy(Animation):
def __init__(
self,
homotopy: typing.Callable[
[float, float, float, float], typing.Tuple[float, float, float]
],
mobject: "Mobject",
run_time: float = 3,
apply_function_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
**kwargs
) -> None:
"""
Homotopy is a function from
(x, y, z, t) to (x', y', z')
"""
self.homotopy = homotopy
self.apply_function_kwargs = (
apply_function_kwargs if apply_function_kwargs is not None else {}
)
super().__init__(mobject, run_time=run_time, **kwargs)
def function_at_time_t(self, t: float) -> typing.Tuple[float, float, float]:
return lambda p: self.homotopy(*p, t)
def interpolate_submobject(
self, submobject: "Mobject", starting_submobject: "Mobject", alpha: float
) -> None:
submobject.points = starting_submobject.points
submobject.apply_function(
self.function_at_time_t(alpha), **self.apply_function_kwargs
)
class SmoothedVectorizedHomotopy(Homotopy):
def interpolate_submobject(
self, submobject: "Mobject", starting_submobject: "Mobject", alpha: float
) -> None:
Homotopy.interpolate_submobject(self, submobject, starting_submobject, alpha)
submobject.make_smooth()
class ComplexHomotopy(Homotopy):
def __init__(
self,
complex_homotopy: typing.Callable[[complex], float],
mobject: "Mobject",
**kwargs
) -> None:
"""
Complex Homotopy a function Cx[0, 1] to C
"""
def homotopy(
x: float, y: float, z: float, t: float
) -> typing.Tuple[float, float, float]:
c = complex_homotopy(complex(x, y), t)
return (c.real, c.imag, z)
Homotopy.__init__(self, homotopy, mobject, **kwargs)
class PhaseFlow(Animation):
def __init__(
self,
function: typing.Callable[[np.ndarray], np.ndarray],
mobject: "Mobject",
virtual_time: float = 1,
suspend_mobject_updating: bool = False,
rate_func: typing.Callable[
[typing.Union[np.ndarray, float]], typing.Union[np.ndarray, float]
] = linear,
**kwargs
) -> None:
self.virtual_time = virtual_time
self.function = function
super().__init__(
mobject,
suspend_mobject_updating=suspend_mobject_updating,
rate_func=rate_func,
**kwargs
)
def interpolate_mobject(self, alpha: float) -> None:
if hasattr(self, "last_alpha"):
dt = self.virtual_time * (alpha - self.last_alpha)
self.mobject.apply_function(lambda p: p + dt * self.function(p))
self.last_alpha = alpha
class MoveAlongPath(Animation):
"""Make one mobject move along the path of another mobject.
Example
--------
.. manim:: MoveAlongPathExample
class MoveAlongPathExample(Scene):
def construct(self):
d1 = Dot().set_color(ORANGE)
l1 = Line(LEFT, RIGHT)
l2 = VMobject()
self.add(d1, l1, l2)
l2.add_updater(lambda x: x.become(Line(LEFT, d1.get_center()).set_color(ORANGE)))
self.play(MoveAlongPath(d1, l1), rate_func=linear)
"""
def __init__(
self,
mobject: "Mobject",
path: np.ndarray,
suspend_mobject_updating: typing.Optional[bool] = False,
**kwargs
) -> None:
self.path = path
super().__init__(
mobject, suspend_mobject_updating=suspend_mobject_updating, **kwargs
)
def interpolate_mobject(self, alpha: float) -> None:
point = self.path.point_from_proportion(alpha)
self.mobject.move_to(point)