Skip to content

Commit 3333b6a

Browse files
authored
Merge pull request #512 from nilaybhatia/easing-funcs
Implement standard easing functions
2 parents 1924ec0 + 90d75b5 commit 3333b6a

File tree

4 files changed

+228
-1
lines changed

4 files changed

+228
-1
lines changed
127 KB
Loading

docs/source/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Mobjects, Scenes, and Animations
6464
#. Add a :code:`Variable` class for displaying text that continuously updates to reflect the value of a python variable.
6565
#. The ``Tex`` and ``MathTex`` objects allow you to specify a custom TexTemplate using the ``template`` keyword argument.
6666
#. :code:`VGroup` now supports printing the class names of contained mobjects and :code:`VDict` supports printing the internal dict of mobjects
67+
#. Add all the standard easing functions
6768
#. :code:`Scene` now renders when :code:`Scene.render()` is called rather than upon instantiation.
6869
#. :code:`ValueTracker` now supports increment using the `+=` operator (in addition to the already existing `increment_value` method)
6970
#. Add :class:`PangoText` for rendering texts using Pango.

manim/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
from .utils.file_ops import *
7777
from .utils.paths import *
7878
from .utils.rate_functions import *
79+
from .utils import rate_functions
7980
from .utils.simple_functions import *
8081
from .utils.sounds import *
8182
from .utils.space_ops import *

manim/utils/rate_functions.py

Lines changed: 226 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,50 @@
1-
"""A selection of rate functions, i.e., *speed curves* for animations."""
1+
"""A selection of rate functions, i.e., *speed curves* for animations.
2+
Please find a standard list at https://easings.net/. Here is a picture for the non-standard ones
3+
4+
.. image:: /_static/non_standard_rate_funcs.png
5+
:alt: Non-standard rate functions
6+
7+
8+
There are primarily 3 kinds of standard easing functions:
9+
10+
#. Ease In - The animation has a smooth start.
11+
#. Ease Out - The animation has a smooth end.
12+
#. Ease In Out - The animation has a smooth start as well as smooth end.
13+
14+
.. note:: The standard functions are not exported, so to use them you do something like this:
15+
rate_func=rate_functions.ease_in_sine
16+
On the other hand, the non-standard functions, which are used more commonly, are exported and can be used directly.
17+
18+
.. manim:: RateFunctions1Example
19+
20+
class RateFunctions1Example(Scene):
21+
def construct(self):
22+
line1 = Line(3*LEFT, 3*RIGHT).shift(UP).set_color(RED)
23+
line2 = Line(3*LEFT, 3*RIGHT).set_color(GREEN)
24+
line3 = Line(3*LEFT, 3*RIGHT).shift(DOWN).set_color(BLUE)
25+
26+
dot1 = Dot().move_to(line1.get_left())
27+
dot2 = Dot().move_to(line2.get_left())
28+
dot3 = Dot().move_to(line3.get_left())
29+
30+
label1 = Tex("Ease In").next_to(line1, RIGHT)
31+
label2 = Tex("Ease out").next_to(line2, RIGHT)
32+
label3 = Tex("Ease In Out").next_to(line3, RIGHT)
33+
34+
self.play(
35+
FadeIn(VGroup(line1, line2, line3),
36+
FadeIn(VGroup(dot1, dot2, dot3))),
37+
Write(VGroup(label1, label2, label3)),
38+
)
39+
self.play(
40+
MoveAlongPath(dot1, line1, rate_func=rate_functions.ease_in_sine),
41+
MoveAlongPath(dot2, line2, rate_func=rate_functions.ease_out_sine),
42+
MoveAlongPath(dot3, line3, rate_func=rate_functions.ease_in_out_sine),
43+
run_time=7
44+
)
45+
self.wait()
46+
"""
47+
248

349
__all__ = [
450
"linear",
@@ -18,6 +64,8 @@
1864
]
1965

2066

67+
from math import sqrt
68+
2169
import numpy as np
2270

2371
from ..utils.bezier import bezier
@@ -115,3 +163,180 @@ def exponential_decay(t, half_life=0.1):
115163
# The half-life should be rather small to minimize
116164
# the cut-off error at the end
117165
return 1 - np.exp(-t / half_life)
166+
167+
168+
def ease_in_sine(t):
169+
return 1 - np.cos((t * np.pi) / 2)
170+
171+
172+
def ease_out_sine(t):
173+
return np.sin((t * np.pi) / 2)
174+
175+
176+
def ease_in_out_sine(t):
177+
return -(np.cos(np.pi * t) - 1) / 2
178+
179+
180+
def ease_in_quad(t):
181+
return t * t
182+
183+
184+
def ease_out_quad(t):
185+
return 1 - (1 - t) * (1 - t)
186+
187+
188+
def ease_in_out_quad(t):
189+
return 2 * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 2) / 2
190+
191+
192+
def ease_in_cubic(t):
193+
return t * t * t
194+
195+
196+
def ease_out_cubic(t):
197+
return 1 - pow(1 - t, 3)
198+
199+
200+
def ease_in_out_cubic(t):
201+
return 4 * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 3) / 2
202+
203+
204+
def ease_in_quart(t):
205+
return t * t * t * t
206+
207+
208+
def ease_out_quart(t):
209+
return 1 - pow(1 - t, 4)
210+
211+
212+
def ease_in_out_quart(t):
213+
return 8 * t * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 4) / 2
214+
215+
216+
def ease_in_quint(t):
217+
return t * t * t * t * t
218+
219+
220+
def ease_out_quint(t):
221+
return 1 - pow(1 - t, 5)
222+
223+
224+
def ease_in_out_quint(t):
225+
return 16 * t * t * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 5) / 2
226+
227+
228+
def ease_in_expo(t):
229+
return 0 if t == 0 else pow(2, 10 * t - 10)
230+
231+
232+
def ease_out_expo(t):
233+
return 1 if t == 1 else 1 - pow(2, -10 * t)
234+
235+
236+
def ease_in_out_expo(t):
237+
if t == 0:
238+
return 0
239+
elif t == 1:
240+
return 1
241+
elif t < 0.5:
242+
return pow(2, 20 * t - 10) / 2
243+
else:
244+
return 2 - pow(2, -20 * t + 10) / 2
245+
246+
247+
def ease_in_circ(t):
248+
return 1 - sqrt(1 - pow(t, 2))
249+
250+
251+
def ease_out_circ(t):
252+
return sqrt(1 - pow(t - 1, 2))
253+
254+
255+
def ease_in_out_circ(t):
256+
return (
257+
(1 - sqrt(1 - pow(2 * t, 2))) / 2
258+
if t < 0.5
259+
else (sqrt(1 - pow(-2 * t + 2, 2)) + 1) / 2
260+
)
261+
262+
263+
def ease_in_back(t):
264+
c1 = 1.70158
265+
c3 = c1 + 1
266+
return c3 * t * t * t - c1 * t * t
267+
268+
269+
def ease_out_back(t):
270+
c1 = 1.70158
271+
c3 = c1 + 1
272+
return 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2)
273+
274+
275+
def ease_in_out_back(t):
276+
c1 = 1.70158
277+
c2 = c1 * 1.525
278+
return (
279+
(pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
280+
if t < 0.5
281+
else (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2
282+
)
283+
284+
285+
def ease_in_elastic(t):
286+
c4 = (2 * np.pi) / 3
287+
if t == 0:
288+
return 0
289+
elif t == 1:
290+
return 1
291+
else:
292+
return -pow(2, 10 * t - 10) * np.sin((t * 10 - 10.75) * c4)
293+
294+
295+
def ease_out_elastic(t):
296+
c4 = (2 * np.pi) / 3
297+
if t == 0:
298+
return 0
299+
elif t == 1:
300+
return 1
301+
else:
302+
return pow(2, -10 * t) * np.sin((t * 10 - 0.75) * c4) + 1
303+
304+
305+
def ease_in_out_elastic(t):
306+
c5 = (2 * np.pi) / 4.5
307+
if t == 0:
308+
return 0
309+
elif t == 1:
310+
return 1
311+
elif t < 0.5:
312+
return -(pow(2, 20 * t - 10) * np.sin((20 * t - 11.125) * c5)) / 2
313+
else:
314+
return (pow(2, -20 * t + 10) * np.sin((20 * t - 11.125) * c5)) / 2 + 1
315+
316+
317+
def ease_in_bounce(t):
318+
return 1 - ease_out_bounce(1 - t)
319+
320+
321+
def ease_out_bounce(t):
322+
n1 = 7.5625
323+
d1 = 2.75
324+
325+
if t < 1 / d1:
326+
return n1 * t * t
327+
elif t < 2 / d1:
328+
return n1 * (t - 1.5 / d1) * t + 0.75
329+
elif t < 2.5 / d1:
330+
return n1 * (t - 2.25 / d1) * t + 0.9375
331+
else:
332+
return n1 * (t - 2.625 / d1) * t + 0.984375
333+
334+
335+
def ease_in_out_bounce(t):
336+
c1 = 1.70158
337+
c2 = c1 * 1.525
338+
return (
339+
(pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
340+
if t < 0.5
341+
else (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2
342+
)

0 commit comments

Comments
 (0)