1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::cmp::min;

use interpolation::lerp;

use crate::math::Milliseconds;
use crate::query3d::AnimationPosition;

use super::interpolate::{Interpolation, Interpolate};

pub enum KeyframeRange<'a, T> {
    /// The keyframe before the specified time
    Before(&'a Frame<T>),
    /// The keyframes that immediately surround the specified time
    Between(&'a Frame<T>, &'a Frame<T>),
    /// The keyframe after the specified time
    After(&'a Frame<T>),
}

#[derive(Debug, Clone)]
pub struct Keyframes<T> {
    pub frames: Vec<Frame<T>>,
    pub interpolation: Interpolation,
}

impl<T> Keyframes<T> {
    pub fn new(
        times: impl Iterator<Item=Milliseconds>,
        values: impl Iterator<Item=T>,
        interpolation: Interpolation,
    ) -> Self {
        let frames = times.zip(values).map(|(time, value)| Frame {time, value}).collect();

        Self {
            frames,
            interpolation,
        }
    }

    /// Retrieves the keyframes immediately surrounding the given time
    /// A time smaller than that of all keyframes will get back the first keyframe twice
    /// A time larger than all keyframes gets the last keyframe twice
    pub fn surrounding(&self, time: Milliseconds) -> KeyframeRange<T> {
        // This unwrap is safe for partial_cmp as long as NaN is not one of the comparison values
        let index = match self.frames.binary_search_by(|frame| frame.time.partial_cmp(&time).unwrap()) {
            Ok(i) | Err(i) => i,
        };

        if index == 0 {
            KeyframeRange::After(&self.frames[index])
        } else if index == self.frames.len() {
            KeyframeRange::Before(&self.frames[index - 1])
        } else {
            let left_index = index - 1;
            let right_index = min(index, self.frames.len() - 1);
            KeyframeRange::Between(&self.frames[left_index], &self.frames[right_index])
        }
    }

    pub fn end_time(&self) -> Milliseconds {
        let last_index = self.frames.len() - 1;
        self.frames[last_index].time
    }

    pub fn value_at(&self, pos: &AnimationPosition) -> T
        where T: Interpolate + Copy,
    {
        let time = match pos {
            &AnimationPosition::Time(t) => t,
            &AnimationPosition::RelativeTime{start_time, weight} => {
                Milliseconds::from_msec(lerp(&start_time.to_msec(), &self.end_time().to_msec(), &weight))
            },
        };

        let new_value = match self.surrounding(time) {
            KeyframeRange::Before(kf) => kf.value,
            KeyframeRange::After(kf) => kf.value,
            KeyframeRange::Between(kf1, kf2) => {
                let start = kf1.time;
                let end = kf2.time;
                // The time factor that gives weight to the start or end frame during interpolation
                let weight = (time.to_msec() - start.to_msec()) / (end.to_msec() - start.to_msec());
                T::interpolate(self.interpolation, weight, &kf1.value, &kf2.value)
            },
        };

        new_value
    }
}

#[derive(Debug, Clone)]
pub struct Frame<T> {
    pub time: Milliseconds,
    pub value: T,
}