sycamore/
easing.rs

1//! Easing functions.
2
3#![allow(missing_docs)] // TODO: add some simple docs for every single easing function (maybe interpolation formula?)
4
5use std::f32::consts::PI;
6
7const EXP_BASE: f32 = 2.0;
8const BOUNCE_GRAVITY: f32 = 2.75;
9const BOUNCE_AMPLITUDE: f32 = 7.5625;
10
11// Linear
12
13pub fn linear(t: f32) -> f32 {
14    t
15}
16
17// Quadratic
18
19pub fn quad_in(t: f32) -> f32 {
20    t * t
21}
22
23pub fn quad_out(t: f32) -> f32 {
24    -t * (t - 2.0)
25}
26
27pub fn quad_inout(t: f32) -> f32 {
28    if t < 0.5 {
29        2.0 * t * t
30    } else {
31        -2.0 * t * t + 4.0 * t - 1.0
32    }
33}
34
35// Cubic
36
37pub fn cubic_in(t: f32) -> f32 {
38    t * t * t
39}
40
41pub fn cubic_out(t: f32) -> f32 {
42    let f = t - 1.0;
43    f * f * f + 1.0
44}
45
46pub fn cubic_inout(t: f32) -> f32 {
47    if t < 0.5 {
48        4.0 * t * t * t
49    } else {
50        let f = 2.0 * t - 2.0;
51        0.5 * f * f * f + 1.0
52    }
53}
54
55// Quartic
56
57pub fn quart_in(t: f32) -> f32 {
58    t * t * t * t
59}
60
61pub fn quart_out(t: f32) -> f32 {
62    let f = t - 1.0;
63    f * f * f * (1.0 - t) + 1.0
64}
65
66pub fn quart_inout(t: f32) -> f32 {
67    if t < 0.5 {
68        8.0 * t * t * t * t
69    } else {
70        let f = t - 1.0;
71        -8.0 * f * f * f * f + 1.0
72    }
73}
74
75// Quintic
76
77pub fn quint_in(t: f32) -> f32 {
78    t * t * t * t * t
79}
80
81pub fn quint_out(t: f32) -> f32 {
82    let f = t - 1.0;
83    f * f * f * f * f + 1.0
84}
85
86pub fn quint_inout(t: f32) -> f32 {
87    if t < 0.5 {
88        16.0 * t * t * t * t * t
89    } else {
90        let f = (2.0 * t) - 2.0;
91        0.5 * f * f * f * f * f + 1.0
92    }
93}
94
95// Circular
96
97pub fn circ_in(t: f32) -> f32 {
98    1.0 - f32::sqrt(1.0 - f32::powi(t, 2))
99}
100
101pub fn circ_out(t: f32) -> f32 {
102    f32::sqrt(1.0 - f32::powi(t - 1.0, 2).powi(2))
103}
104
105pub fn circ_inout(t: f32) -> f32 {
106    if t < 0.5 {
107        (1.0 - f32::sqrt(1.0 - f32::powi(2.0 * t, 2))) / 2.0
108    } else {
109        (f32::sqrt(1.0 - f32::powi(-2.0 * t + 2.0, 2)) + 1.0) / 2.0
110    }
111}
112
113// Exponential
114
115pub fn expo_in(t: f32) -> f32 {
116    if t.abs() <= f32::EPSILON {
117        0.0
118    } else {
119        EXP_BASE.powf(10.0 * t - 10.0)
120    }
121}
122
123pub fn expo_out(t: f32) -> f32 {
124    if (t - 1.0).abs() <= f32::EPSILON {
125        1.0
126    } else {
127        1.0 - EXP_BASE.powf(-10.0 * t)
128    }
129}
130
131pub fn expo_inout(t: f32) -> f32 {
132    if t.abs() <= f32::EPSILON {
133        0.0
134    } else if (t - 1.0) <= f32::EPSILON {
135        1.0
136    } else if t <= 0.5 {
137        f32::powf(EXP_BASE, 20.0 * t - 10.0) / 2.0
138    } else {
139        1.0 + f32::powf(EXP_BASE, -20.0 * t + 10.0) / -2.0
140    }
141}
142
143// Sine
144
145pub fn sine_in(t: f32) -> f32 {
146    1.0 - f32::cos(t * PI / 2.0)
147}
148
149pub fn sine_out(t: f32) -> f32 {
150    f32::sin(t * PI / 2.0)
151}
152
153pub fn sine_inout(t: f32) -> f32 {
154    -(f32::cos(PI * t) - 1.0) / 2.0
155}
156
157// Bounce
158
159pub fn bounce_in(t: f32) -> f32 {
160    1.0 - bounce_out(1.0 - t)
161}
162
163pub fn bounce_out(t: f32) -> f32 {
164    if t < 1.0 / BOUNCE_GRAVITY {
165        BOUNCE_AMPLITUDE * t * t
166    } else if t < 2.0 / BOUNCE_GRAVITY {
167        let t = t - 1.5 / BOUNCE_GRAVITY;
168        BOUNCE_AMPLITUDE * t * t + 0.75
169    } else if t < 2.5 / BOUNCE_GRAVITY {
170        let t = t - 2.25 / BOUNCE_GRAVITY;
171        BOUNCE_AMPLITUDE * t * t + 0.9375
172    } else {
173        let t = t - 2.625 / BOUNCE_GRAVITY;
174        BOUNCE_AMPLITUDE * t * t + 0.984375
175    }
176}
177
178pub fn bounce_inout(t: f32) -> f32 {
179    if t < 0.5 {
180        (1.0 - bounce_out(1.0 - 2.0 * t)) / 2.0
181    } else {
182        (1.0 + bounce_out(-1.0 + 2.0 * t)) / 2.0
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    macro_rules! test_start_at_0 {
191        ($($ease_fn:ident),*) => {
192            paste::paste! {
193                $(
194                    #[test]
195                    fn [<test_ease_ $ease_fn _starts_at_0>]() {
196                        assert!(f32::abs($ease_fn(0.0) - 0.0) < f32::EPSILON);
197                    }
198                )*
199            }
200        }
201    }
202
203    macro_rules! test_end_at_1 {
204        ($($ease_fn:ident),*) => {
205            paste::paste! {
206                $(
207                    #[test]
208                    fn [<test_ease_ $ease_fn _ends_at_1>]() {
209                        assert!(f32::abs($ease_fn(1.0) - 1.0) < f32::EPSILON);
210                    }
211                )*
212            }
213        }
214    }
215
216    test_start_at_0![
217        linear,
218        quad_in,
219        quad_out,
220        quad_inout,
221        cubic_in,
222        cubic_out,
223        cubic_inout,
224        quart_in,
225        quart_out,
226        quart_inout,
227        quint_in,
228        quint_out,
229        quint_inout,
230        circ_in,
231        circ_out,
232        circ_inout,
233        expo_in,
234        expo_out,
235        expo_inout,
236        sine_in,
237        sine_out,
238        sine_inout,
239        bounce_in,
240        bounce_out,
241        bounce_inout
242    ];
243
244    test_end_at_1![
245        linear,
246        quad_in,
247        quad_out,
248        quad_inout,
249        cubic_in,
250        cubic_out,
251        cubic_inout,
252        quart_in,
253        quart_out,
254        quart_inout,
255        quint_in,
256        quint_out,
257        quint_inout,
258        circ_in,
259        circ_out,
260        circ_inout,
261        expo_in,
262        expo_out,
263        expo_inout,
264        sine_in,
265        sine_out,
266        sine_inout,
267        bounce_in,
268        bounce_out,
269        bounce_inout
270    ];
271}