onedrop_engine/engine/
transition_state.rs1fn ease_in_out(t: f32) -> f32 {
15 let t = t.clamp(0.0, 1.0);
16 if t < 0.5 {
17 2.0 * t * t
18 } else {
19 1.0 - 2.0 * (1.0 - t) * (1.0 - t)
20 }
21}
22
23#[derive(Debug, Clone, Copy)]
26pub(crate) struct TransitionState {
27 pub(crate) elapsed_s: f32,
28 pub(crate) total_s: f32,
29}
30
31impl TransitionState {
32 pub(crate) fn new(total_s: f32) -> Self {
33 Self {
34 elapsed_s: 0.0,
35 total_s: total_s.max(1e-3),
36 }
37 }
38
39 pub(crate) fn tick(&mut self, delta_s: f32) {
40 self.elapsed_s = (self.elapsed_s + delta_s).max(0.0);
41 }
42
43 pub(crate) fn linear(&self) -> f32 {
45 (self.elapsed_s / self.total_s).clamp(0.0, 1.0)
46 }
47
48 pub(crate) fn eased(&self) -> f32 {
50 ease_in_out(self.linear())
51 }
52
53 pub(crate) fn is_complete(&self) -> bool {
57 self.elapsed_s >= self.total_s
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn linear_progresses_with_delta_time() {
67 let mut t = TransitionState::new(1.0);
68 assert_eq!(t.linear(), 0.0);
69 t.tick(0.25);
70 assert!((t.linear() - 0.25).abs() < 1e-6);
71 t.tick(0.25);
72 assert!((t.linear() - 0.5).abs() < 1e-6);
73 t.tick(1.0);
74 assert_eq!(t.linear(), 1.0);
75 assert!(t.is_complete());
76 }
77
78 #[test]
79 fn ease_in_out_endpoints_and_midpoint() {
80 assert_eq!(ease_in_out(0.0), 0.0);
81 assert_eq!(ease_in_out(1.0), 1.0);
82 let mid = ease_in_out(0.5);
83 assert!((mid - 0.5).abs() < 1e-6, "midpoint should still be 0.5");
84 assert!(ease_in_out(0.25) < 0.25);
86 assert!(ease_in_out(0.75) > 0.75);
87 }
88
89 #[test]
90 fn clamp_at_one_after_overshoot() {
91 let mut t = TransitionState::new(0.5);
92 t.tick(10.0);
93 assert_eq!(t.linear(), 1.0);
94 assert_eq!(t.eased(), 1.0);
95 }
96}