1#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
8pub enum Easing {
9 #[default]
10 Linear,
11 EaseOutCubic,
13 EaseOutQuint,
15 EaseInOutCubic,
17}
18
19impl Easing {
20 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}