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 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}