atagen

monolith

public
Star0
Watch0
7de49c2aa70b317e35783f54619e08eff5326c7f
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git

Blame: crates/motion/src/timeline.rs

b563d2da1c80atagen2026-10-16 08:25 UTC1
/// A plan's place in time. Every phase lasts `phase_ns`.
b563d2da1c80atagen2026-10-16 08:25 UTC2
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
b563d2da1c80atagen2026-10-16 08:25 UTC3
pub struct Timeline {
b563d2da1c80atagen2026-10-16 08:25 UTC4
    pub start_ns: u64,
b563d2da1c80atagen2026-10-16 08:25 UTC5
    pub phase_ns: u64,
b563d2da1c80atagen2026-10-16 08:25 UTC6
    pub phases: usize,
b563d2da1c80atagen2026-10-16 08:25 UTC7
}
b563d2da1c80atagen2026-10-16 08:25 UTC8
b563d2da1c80atagen2026-10-16 08:25 UTC9
#[derive(Clone, Copy, Debug, PartialEq)]
b563d2da1c80atagen2026-10-16 08:25 UTC10
pub enum Position {
b563d2da1c80atagen2026-10-16 08:25 UTC11
    NotStarted,
b563d2da1c80atagen2026-10-16 08:25 UTC12
    /// Inside `phase`, with linear time `t` in [0, 1) before easing.
b563d2da1c80atagen2026-10-16 08:25 UTC13
    Phase {
b563d2da1c80atagen2026-10-16 08:25 UTC14
        phase: usize,
b563d2da1c80atagen2026-10-16 08:25 UTC15
        t: f64,
b563d2da1c80atagen2026-10-16 08:25 UTC16
    },
b563d2da1c80atagen2026-10-16 08:25 UTC17
    Finished,
b563d2da1c80atagen2026-10-16 08:25 UTC18
}
b563d2da1c80atagen2026-10-16 08:25 UTC19
b563d2da1c80atagen2026-10-16 08:25 UTC20
impl Timeline {
b563d2da1c80atagen2026-10-16 08:25 UTC21
    pub fn end_ns(&self) -> u64 {
b563d2da1c80atagen2026-10-16 08:25 UTC22
        self.start_ns + self.phase_ns * self.phases as u64
b563d2da1c80atagen2026-10-16 08:25 UTC23
    }
b563d2da1c80atagen2026-10-16 08:25 UTC24
b563d2da1c80atagen2026-10-16 08:25 UTC25
    pub fn locate(&self, now_ns: u64) -> Position {
b563d2da1c80atagen2026-10-16 08:25 UTC26
        if now_ns < self.start_ns {
b563d2da1c80atagen2026-10-16 08:25 UTC27
            return Position::NotStarted;
b563d2da1c80atagen2026-10-16 08:25 UTC28
        }
b563d2da1c80atagen2026-10-16 08:25 UTC29
        if self.phase_ns == 0 || now_ns >= self.end_ns() {
b563d2da1c80atagen2026-10-16 08:25 UTC30
            return Position::Finished;
b563d2da1c80atagen2026-10-16 08:25 UTC31
        }
b563d2da1c80atagen2026-10-16 08:25 UTC32
        let elapsed = now_ns - self.start_ns;
b563d2da1c80atagen2026-10-16 08:25 UTC33
        let phase = (elapsed / self.phase_ns) as usize;
b563d2da1c80atagen2026-10-16 08:25 UTC34
        let t = (elapsed % self.phase_ns) as f64 / self.phase_ns as f64;
b563d2da1c80atagen2026-10-16 08:25 UTC35
        Position::Phase { phase, t }
b563d2da1c80atagen2026-10-16 08:25 UTC36
    }
b563d2da1c80atagen2026-10-16 08:25 UTC37
}
b563d2da1c80atagen2026-10-16 08:25 UTC38
b563d2da1c80atagen2026-10-16 08:25 UTC39
#[cfg(test)]
b563d2da1c80atagen2026-10-16 08:25 UTC40
mod tests {
b563d2da1c80atagen2026-10-16 08:25 UTC41
    use super::*;
b563d2da1c80atagen2026-10-16 08:25 UTC42
b563d2da1c80atagen2026-10-16 08:25 UTC43
    #[test]
b563d2da1c80atagen2026-10-16 08:25 UTC44
    fn locates_phases() {
b563d2da1c80atagen2026-10-16 08:25 UTC45
        let tl = Timeline {
b563d2da1c80atagen2026-10-16 08:25 UTC46
            start_ns: 1_000,
b563d2da1c80atagen2026-10-16 08:25 UTC47
            phase_ns: 100,
b563d2da1c80atagen2026-10-16 08:25 UTC48
            phases: 3,
b563d2da1c80atagen2026-10-16 08:25 UTC49
        };
b563d2da1c80atagen2026-10-16 08:25 UTC50
        assert_eq!(tl.locate(999), Position::NotStarted);
b563d2da1c80atagen2026-10-16 08:25 UTC51
        assert_eq!(tl.locate(1_000), Position::Phase { phase: 0, t: 0.0 });
b563d2da1c80atagen2026-10-16 08:25 UTC52
        assert_eq!(tl.locate(1_150), Position::Phase { phase: 1, t: 0.5 });
b563d2da1c80atagen2026-10-16 08:25 UTC53
        assert_eq!(tl.locate(1_300), Position::Finished);
b563d2da1c80atagen2026-10-16 08:25 UTC54
    }
b563d2da1c80atagen2026-10-16 08:25 UTC55
b563d2da1c80atagen2026-10-16 08:25 UTC56
    #[test]
b563d2da1c80atagen2026-10-16 08:25 UTC57
    fn total_time_is_phases_times_duration() {
b563d2da1c80atagen2026-10-16 08:25 UTC58
        let tl = Timeline {
b563d2da1c80atagen2026-10-16 08:25 UTC59
            start_ns: 0,
b563d2da1c80atagen2026-10-16 08:25 UTC60
            phase_ns: 160_000_000,
b563d2da1c80atagen2026-10-16 08:25 UTC61
            phases: 4,
b563d2da1c80atagen2026-10-16 08:25 UTC62
        };
b563d2da1c80atagen2026-10-16 08:25 UTC63
        assert_eq!(tl.end_ns(), 640_000_000);
b563d2da1c80atagen2026-10-16 08:25 UTC64
    }
b563d2da1c80atagen2026-10-16 08:25 UTC65
}