Skip to main content

emath/
easing.rs

1//! Easing functions for animations.
2//!
3//! Contains most easing functions from <https://easings.net/>.
4//!
5//! All functions take a value in `[0, 1]` and return a value in `[0, 1]`.
6//!
7//! Derived from <https://github.com/warrenm/AHEasing/blob/master/AHEasing/easing.c>.
8use core::f32::consts::PI;
9
10use crate::fast_midpoint;
11
12#[inline]
13fn powf(base: f32, exp: f32) -> f32 {
14    base.powf(exp)
15}
16
17/// No easing, just `y = x`
18#[inline]
19pub fn linear(t: f32) -> f32 {
20    t
21}
22
23/// <https://easings.net/#easeInQuad>
24///
25/// Modeled after the parabola `y = x^2`
26#[inline]
27pub fn quadratic_in(t: f32) -> f32 {
28    t * t
29}
30
31/// <https://easings.net/#easeOutQuad>
32///
33/// Same as `1.0 - quadratic_in(1.0 - t)`.
34#[inline]
35pub fn quadratic_out(t: f32) -> f32 {
36    -(t * (t - 2.))
37}
38
39/// <https://easings.net/#easeInOutQuad>
40#[inline]
41pub fn quadratic_in_out(t: f32) -> f32 {
42    if t < 0.5 {
43        2. * t * t
44    } else {
45        (-2. * t * t) + (4. * t) - 1.
46    }
47}
48
49/// <https://easings.net/#easeInCubic>
50///
51/// Modeled after the parabola `y = x^3`
52#[inline]
53pub fn cubic_in(t: f32) -> f32 {
54    t * t * t
55}
56
57/// <https://easings.net/#easeOutCubic>
58#[inline]
59pub fn cubic_out(t: f32) -> f32 {
60    let f = t - 1.;
61    f * f * f + 1.
62}
63
64/// <https://easings.net/#easeInOutCubic>
65#[inline]
66pub fn cubic_in_out(t: f32) -> f32 {
67    if t < 0.5 {
68        4. * t * t * t
69    } else {
70        let f = (2. * t) - 2.;
71        0.5 * f * f * f + 1.
72    }
73}
74
75/// <https://easings.net/#easeInSine>
76///
77/// Modeled after quarter-cycle of sine wave
78#[inline]
79pub fn sin_in(t: f32) -> f32 {
80    ((t - 1.) * 2. * PI).sin() + 1.
81}
82
83/// <https://easings.net/#easeOuSine>
84///
85/// Modeled after quarter-cycle of sine wave (different phase)
86#[inline]
87pub fn sin_out(t: f32) -> f32 {
88    (t * 2. * PI).sin()
89}
90
91/// <https://easings.net/#easeInOutSine>
92///
93/// Modeled after half sine wave
94#[inline]
95pub fn sin_in_out(t: f32) -> f32 {
96    0.5 * (1. - (t * PI).cos())
97}
98
99/// <https://easings.net/#easeInCirc>
100///
101/// Modeled after shifted quadrant IV of unit circle
102#[inline]
103pub fn circular_in(t: f32) -> f32 {
104    1. - (1. - t * t).sqrt()
105}
106
107/// <https://easings.net/#easeOutCirc>
108///
109/// Modeled after shifted quadrant II of unit circle
110#[inline]
111pub fn circular_out(t: f32) -> f32 {
112    (2. - t).sqrt() * t
113}
114
115/// <https://easings.net/#easeInOutCirc>
116#[inline]
117pub fn circular_in_out(t: f32) -> f32 {
118    if t < 0.5 {
119        0.5 * (1. - (1. - 4. * t * t).sqrt())
120    } else {
121        fast_midpoint((-(2. * t - 3.) * (2. * t - 1.)).sqrt(), 1.)
122    }
123}
124
125/// <https://easings.net/#easeInExpo>
126///
127/// There is a small discontinuity at 0.
128#[inline]
129pub fn exponential_in(t: f32) -> f32 {
130    if t == 0. {
131        t
132    } else {
133        powf(2.0, 10. * (t - 1.))
134    }
135}
136
137/// <https://easings.net/#easeOutExpo>
138///
139/// There is a small discontinuity at 1.
140#[inline]
141pub fn exponential_out(t: f32) -> f32 {
142    if t == 1. { t } else { 1. - powf(2.0, -10. * t) }
143}
144
145/// <https://easings.net/#easeInOutExpo>
146///
147/// There is a small discontinuity at 0 and 1.
148#[inline]
149pub fn exponential_in_out(t: f32) -> f32 {
150    if t == 0. || t == 1. {
151        t
152    } else if t < 0.5 {
153        0.5 * powf(2.0, 20. * t - 10.)
154    } else {
155        0.5 * powf(2.0, -20. * t + 10.) + 1.
156    }
157}
158
159/// <https://easings.net/#easeInBack>
160#[inline]
161pub fn back_in(t: f32) -> f32 {
162    t * t * t - t * (t * PI).sin()
163}
164
165/// <https://easings.net/#easeOutBack>
166#[inline]
167pub fn back_out(t: f32) -> f32 {
168    let f = 1. - t;
169    1. - (f * f * f - f * (f * PI).sin())
170}
171
172/// <https://easings.net/#easeInOutBack>
173#[inline]
174pub fn back_in_out(t: f32) -> f32 {
175    if t < 0.5 {
176        let f = 2. * t;
177        0.5 * (f * f * f - f * (f * PI).sin())
178    } else {
179        let f = 1. - (2. * t - 1.);
180        0.5 * (1. - (f * f * f - f * (f * PI).sin())) + 0.5
181    }
182}
183
184/// <https://easings.net/#easeInBounce>
185///
186/// Each bounce is modelled as a parabola.
187#[inline]
188pub fn bounce_in(t: f32) -> f32 {
189    1. - bounce_out(1. - t)
190}
191
192/// <https://easings.net/#easeOutBounce>
193///
194/// Each bounce is modelled as a parabola.
195#[inline]
196pub fn bounce_out(t: f32) -> f32 {
197    if t < 4. / 11. {
198        const T2: f32 = 121. / 16.;
199        T2 * t * t
200    } else if t < 8. / 11. {
201        const T2: f32 = 363. / 40.;
202        const T1: f32 = -99. / 10.;
203        const T0: f32 = 17. / 5.;
204        T2 * t * t + T1 * t + T0
205    } else if t < 9. / 10. {
206        const T2: f32 = 4356. / 361.;
207        const T1: f32 = -35442. / 1805.;
208        const T0: f32 = 16061. / 1805.;
209        T2 * t * t + T1 * t + T0
210    } else {
211        const T2: f32 = 54. / 5.;
212        const T1: f32 = -513. / 25.;
213        const T0: f32 = 268. / 25.;
214        T2 * t * t + T1 * t + T0
215    }
216}
217
218/// <https://easings.net/#easeInOutBounce>
219///
220/// Each bounce is modelled as a parabola.
221#[inline]
222pub fn bounce_in_out(t: f32) -> f32 {
223    if t < 0.5 {
224        0.5 * bounce_in(t * 2.)
225    } else {
226        0.5 * bounce_out(t * 2. - 1.) + 0.5
227    }
228}