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> {
Before(&'a Frame<T>),
Between(&'a Frame<T>, &'a Frame<T>),
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,
}
}
pub fn surrounding(&self, time: Milliseconds) -> KeyframeRange<T> {
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;
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,
}