use crate::{Duration, Easing, Time};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Interval {
start: Time,
duration: Duration,
}
impl Interval {
pub const fn new(start: Time, duration: Duration) -> Self {
Self { start, duration }
}
pub const fn start(&self) -> Time {
self.start
}
pub const fn duration(&self) -> Duration {
self.duration
}
pub fn contains(&self, time: Time) -> bool {
let elapsed = self.elapsed(time);
elapsed >= 0 && elapsed < i128::from(self.duration.ticks())
}
pub fn progress(&self, time: Time) -> f64 {
let elapsed = self.elapsed(time);
if elapsed < 0 {
0.0
} else if elapsed >= i128::from(self.duration.ticks()) {
1.0
} else {
elapsed as f64 / self.duration.ticks() as f64
}
}
pub fn ease(&self, time: Time, easing: Easing) -> f64 {
easing.apply(self.progress(time))
}
fn elapsed(&self, time: Time) -> i128 {
i128::from(time.ticks()) - i128::from(self.start.ticks())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TimeLiteral;
#[test]
fn progress_clamps_and_zero_duration_jumps() {
let interval = Interval::new(Time::ZERO + 1.seconds(), 2.seconds());
assert!(!interval.contains(Time::ZERO));
assert!(interval.contains(Time::ZERO + 1.seconds()));
assert!(!interval.contains(Time::ZERO + 3.seconds()));
assert_eq!(interval.progress(Time::ZERO + 1.seconds()), 0.0);
assert_eq!(interval.progress(Time::ZERO + 3.seconds()), 1.0);
let jump = Interval::new(Time::ZERO, Duration::ZERO);
assert_eq!(jump.progress(Time::from_ticks(-1)), 0.0);
assert_eq!(jump.progress(Time::ZERO), 1.0);
assert!(!jump.contains(Time::ZERO));
}
#[test]
fn extreme_ticks_do_not_overflow() {
let interval = Interval::new(Time::from_ticks(i64::MIN), Duration::ZERO);
assert_eq!(interval.progress(Time::from_ticks(i64::MAX)), 1.0);
}
}