vizia_core/animation/
timing_function.rs

1#[derive(Debug, Clone, Copy)]
2pub(crate) struct TimingFunction {
3    x1: f32,
4    x2: f32,
5    y1: f32,
6    y2: f32,
7}
8
9impl Default for TimingFunction {
10    fn default() -> Self {
11        Self::ease_in_out()
12    }
13}
14
15impl TimingFunction {
16    pub fn linear() -> Self {
17        Self::new(0., 0., 1., 1.)
18    }
19    pub fn ease() -> Self {
20        Self::new(0.25, 0.1, 0.25, 1.)
21    }
22    pub fn ease_in() -> Self {
23        Self::new(0.42, 0., 1., 1.)
24    }
25    pub fn ease_out() -> Self {
26        Self::new(0., 0., 0.58, 1.)
27    }
28    pub fn ease_in_out() -> Self {
29        Self::new(0.42, 0., 0.58, 1.)
30    }
31}
32
33impl TimingFunction {
34    pub fn new(x1: f32, y1: f32, x2: f32, y2: f32) -> Self {
35        Self { x1, y1, x2, y2 }
36    }
37
38    pub fn value(&self, x: f32) -> f32 {
39        // Linear
40        if self.x1 == self.y1 && self.x2 == self.y2 {
41            return x;
42        }
43
44        Self::calc_bezier(self.find_t_for_x(x), self.y1, self.y2)
45    }
46
47    fn calc_bezier(t: f32, a1: f32, a2: f32) -> f32 {
48        let a = |a1: f32, a2: f32| 1.0 - 3.0 * a2 + 3.0 * a1;
49        let b = |a1: f32, a2: f32| 3.0 * a2 - 6.0 * a1;
50        let c = |a1: f32| 3.0 * a1;
51
52        ((a(a1, a2) * t + b(a1, a2)) * t + c(a1)) * t
53    }
54
55    fn calc_bezier_slope(t: f32, a1: f32, a2: f32) -> f32 {
56        let a = |a1: f32, a2: f32| 1.0 - 3.0 * a2 + 3.0 * a1;
57        let b = |a1: f32, a2: f32| 3.0 * a2 - 6.0 * a1;
58        let c = |a1: f32| 3.0 * a1;
59
60        3.0 * a(a1, a2) * t * t + 2.0 * b(a1, a2) * t + c(a1)
61    }
62
63    fn find_t_for_x(&self, x: f32) -> f32 {
64        let mut guess = x;
65        let mut error = f32::MAX;
66        for _ in 0..8 {
67            let pos = Self::calc_bezier(guess, self.x1, self.x2);
68            error = pos - x;
69            if error.abs() <= 0.0000001 {
70                return guess;
71            }
72            let slope = Self::calc_bezier_slope(guess, self.x1, self.x2);
73            guess -= error / slope;
74        }
75        if error.abs() <= 0.0000001 {
76            guess
77        } else {
78            x
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::TimingFunction;
86
87    #[test]
88    fn linear() {
89        let timing_func = TimingFunction::linear();
90        assert_eq!(timing_func.value(0.5), 0.5);
91    }
92
93    #[test]
94    fn ease() {
95        let timing_func = TimingFunction::ease();
96        assert_eq!(timing_func.value(0.25), 0.4085106);
97    }
98}