1#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
9pub enum Easing {
10 #[default]
11 Linear,
12 EaseOutCubic,
14 EaseOutQuint,
16 EaseInOutCubic,
18}
19
20impl Easing {
21 pub fn apply(self, t: f32) -> f32 {
23 let t = t.clamp(0.0, 1.0);
24 match self {
25 Easing::Linear => t,
26 Easing::EaseOutCubic => 1.0 - (1.0 - t).powi(3),
27 Easing::EaseOutQuint => 1.0 - (1.0 - t).powi(5),
28 Easing::EaseInOutCubic => {
29 if t < 0.5 {
30 4.0 * t * t * t
31 } else {
32 1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
33 }
34 }
35 }
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn endpoints_are_fixed() {
45 for e in [
46 Easing::Linear,
47 Easing::EaseOutCubic,
48 Easing::EaseOutQuint,
49 Easing::EaseInOutCubic,
50 ] {
51 assert!((e.apply(0.0) - 0.0).abs() < 1e-6);
52 assert!((e.apply(1.0) - 1.0).abs() < 1e-6);
53 }
54 }
55
56 #[test]
57 fn monotonic_non_decreasing() {
58 for e in [
59 Easing::EaseOutCubic,
60 Easing::EaseInOutCubic,
61 Easing::EaseOutQuint,
62 ] {
63 let mut prev = -1.0;
64 for i in 0..=100 {
65 let v = e.apply(i as f32 / 100.0);
66 assert!(v + 1e-6 >= prev, "{e:?} went backwards");
67 prev = v;
68 }
69 }
70 }
71}