Skip to main content

inkling/
easing.rs

1//! Easing functions map linear time `0..=1` onto eased progress `0..=1`.
2//!
3//! Easing shapes *how progress moves over time*; it is independent of the
4//! [`RankMap`], which decides *where* a given progress value reveals.
5
6/// A timing curve. Input and output are both clamped to `0..=1`.
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
8pub enum Easing {
9    #[default]
10    Linear,
11    /// Decelerates toward the end, good for a confident finish.
12    EaseOutCubic,
13    /// Strong deceleration; the reveal "lands".
14    EaseOutQuint,
15    /// Slow start and end, fast middle, the classic UI curve.
16    EaseInOutCubic,
17}
18
19impl Easing {
20    /// Apply the curve to a normalized time `t`.
21    pub fn apply(self, t: f32) -> f32 {
22        let t = t.clamp(0.0, 1.0);
23        match self {
24            Easing::Linear => t,
25            Easing::EaseOutCubic => 1.0 - (1.0 - t).powi(3),
26            Easing::EaseOutQuint => 1.0 - (1.0 - t).powi(5),
27            Easing::EaseInOutCubic => {
28                if t < 0.5 {
29                    4.0 * t * t * t
30                } else {
31                    1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
32                }
33            }
34        }
35    }
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn endpoints_are_fixed() {
44        for e in [
45            Easing::Linear,
46            Easing::EaseOutCubic,
47            Easing::EaseOutQuint,
48            Easing::EaseInOutCubic,
49        ] {
50            assert!((e.apply(0.0) - 0.0).abs() < 1e-6);
51            assert!((e.apply(1.0) - 1.0).abs() < 1e-6);
52        }
53    }
54
55    #[test]
56    fn monotonic_non_decreasing() {
57        for e in [
58            Easing::EaseOutCubic,
59            Easing::EaseInOutCubic,
60            Easing::EaseOutQuint,
61        ] {
62            let mut prev = -1.0;
63            for i in 0..=100 {
64                let v = e.apply(i as f32 / 100.0);
65                assert!(v + 1e-6 >= prev, "{e:?} went backwards");
66                prev = v;
67            }
68        }
69    }
70}