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