uianimator/
default_animator_f64_quadratic.rs

1use std::time::Instant;
2
3use crate::Animator;
4
5pub struct DefaultAnimatorF64Quadratic {
6    /// this value squared * 2 = difference between starting point (@ time - elapsed_duration) and target
7    target_reach_half_duration: f64,
8    elapsed_duration: f64,
9    decreasing: bool,
10    start: f64,
11    time: Instant,
12    target: f64,
13    /// in units per second per second
14    speed: f64,
15}
16impl DefaultAnimatorF64Quadratic {
17    pub fn new(value: f64, speed: f64) -> Self {
18        Self {
19            target_reach_half_duration: 0.0,
20            elapsed_duration: 0.0,
21            decreasing: false,
22            start: value,
23            time: Instant::now(),
24            target: value,
25            speed,
26        }
27    }
28    pub fn target(&self) -> f64 {
29        self.target
30    }
31}
32impl Animator for DefaultAnimatorF64Quadratic {
33    type Value = f64;
34    type Time = Instant;
35
36    fn get_value(&self, time: Self::Time) -> Self::Value {
37        let elapsed = self.elapsed_duration
38            + self.speed * time.saturating_duration_since(self.time).as_secs_f64();
39        if elapsed < self.target_reach_half_duration {
40            // speed up
41            if self.decreasing {
42                self.start - elapsed * elapsed
43            } else {
44                self.start + elapsed * elapsed
45            }
46        } else {
47            // slow down
48            let remaining = (self.target_reach_half_duration * 2.0) - elapsed;
49            if remaining > 0.0 {
50                if self.decreasing {
51                    self.target + remaining * remaining
52                } else {
53                    self.target - remaining * remaining
54                }
55            } else {
56                self.target
57            }
58        }
59    }
60    fn set_target(&mut self, target: Self::Value, time: Self::Time) {
61        if self.target == target {
62            return;
63        }
64        let elapsed = self.elapsed_duration
65            + self.speed * time.saturating_duration_since(self.time).as_secs_f64();
66        let mut set_time = true;
67        // dbg!(elapsed, self.target_reach_half_duration);
68        self.start = if elapsed < self.target_reach_half_duration {
69            let slow_down_now_end_value = if self.decreasing {
70                self.start - elapsed * elapsed * 2.0
71            } else {
72                self.start + elapsed * elapsed * 2.0
73            };
74            let new_decreasing = if self.decreasing {
75                target <= slow_down_now_end_value
76            } else {
77                target < slow_down_now_end_value
78            };
79            // speeding up
80            if new_decreasing == self.decreasing {
81                // we should speed up
82                set_time = false;
83                self.start
84            } else {
85                // should slow down, then go reverse
86                self.decreasing = new_decreasing;
87                // so that the new curve has the same slope, but changes in the opposite direction
88                self.elapsed_duration = -elapsed;
89                slow_down_now_end_value
90            }
91        } else {
92            let remaining = (self.target_reach_half_duration * 2.0) - elapsed;
93            if remaining > 0.0 {
94                // slowing down
95                let new_decreasing = if self.decreasing {
96                    target <= self.target
97                } else {
98                    target < self.target
99                };
100                if new_decreasing != self.decreasing {
101                    // should slow down, then go reverse
102                    self.decreasing = new_decreasing;
103                    // so that the new curve has the same slope, but changes in the opposite direction
104                    self.elapsed_duration = -remaining;
105                    self.target
106                } else {
107                    // should speed up, then slow down
108                    self.decreasing = new_decreasing;
109                    // so that the new curve has the same slope, but changes in the opposite direction
110                    self.elapsed_duration = remaining;
111                    if self.decreasing {
112                        self.target + remaining * remaining * 2.0
113                    } else {
114                        self.target - remaining * remaining * 2.0
115                    }
116                }
117            } else {
118                // target reached
119                self.elapsed_duration = 0.0;
120                self.decreasing = target < self.target;
121                self.target
122            }
123        };
124        self.target = target;
125        self.target_reach_half_duration = (0.5 * (self.start - self.target).abs()).sqrt();
126        if set_time {
127            self.time = time;
128        }
129    }
130}