motion_core/animation/
interval.rs1use crate::{Duration, Easing, Time};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub struct Interval {
21 start: Time,
22 duration: Duration,
23}
24
25impl Interval {
26 pub const fn new(start: Time, duration: Duration) -> Self {
28 Self { start, duration }
29 }
30
31 pub const fn start(&self) -> Time {
33 self.start
34 }
35
36 pub const fn duration(&self) -> Duration {
38 self.duration
39 }
40
41 pub fn contains(&self, time: Time) -> bool {
45 let elapsed = self.elapsed(time);
46 elapsed >= 0 && elapsed < i128::from(self.duration.ticks())
47 }
48
49 pub fn progress(&self, time: Time) -> f64 {
52 let elapsed = self.elapsed(time);
53 if elapsed < 0 {
54 0.0
55 } else if elapsed >= i128::from(self.duration.ticks()) {
56 1.0
57 } else {
58 elapsed as f64 / self.duration.ticks() as f64
59 }
60 }
61
62 pub fn ease(&self, time: Time, easing: Easing) -> f64 {
64 easing.apply(self.progress(time))
65 }
66
67 fn elapsed(&self, time: Time) -> i128 {
68 i128::from(time.ticks()) - i128::from(self.start.ticks())
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use crate::TimeLiteral;
76
77 #[test]
78 fn progress_clamps_and_zero_duration_jumps() {
79 let interval = Interval::new(Time::ZERO + 1.seconds(), 2.seconds());
80 assert!(!interval.contains(Time::ZERO));
81 assert!(interval.contains(Time::ZERO + 1.seconds()));
82 assert!(!interval.contains(Time::ZERO + 3.seconds()));
83 assert_eq!(interval.progress(Time::ZERO + 1.seconds()), 0.0);
84 assert_eq!(interval.progress(Time::ZERO + 3.seconds()), 1.0);
85
86 let jump = Interval::new(Time::ZERO, Duration::ZERO);
87 assert_eq!(jump.progress(Time::from_ticks(-1)), 0.0);
88 assert_eq!(jump.progress(Time::ZERO), 1.0);
89 assert!(!jump.contains(Time::ZERO));
90 }
91
92 #[test]
93 fn extreme_ticks_do_not_overflow() {
94 let interval = Interval::new(Time::from_ticks(i64::MIN), Duration::ZERO);
95 assert_eq!(interval.progress(Time::from_ticks(i64::MAX)), 1.0);
96 }
97}