1use crate::keyframe::Keyframe;
7
8use eazy_core::Curve;
9use eazy_core::Easing;
10use eazy_tweener::Tweenable;
11
12#[derive(Debug, Clone)]
32pub struct KeyframeTrack<T: Tweenable> {
33 keyframes: Vec<Keyframe<T>>,
35 default_easing: Easing,
37}
38
39impl<T: Tweenable> KeyframeTrack<T> {
40 pub fn new() -> Self {
42 Self {
43 keyframes: Vec::new(),
44 default_easing: Easing::Linear,
45 }
46 }
47
48 pub fn with_default_easing(easing: Easing) -> Self {
50 Self {
51 keyframes: Vec::new(),
52 default_easing: easing,
53 }
54 }
55
56 pub fn add(&mut self, keyframe: Keyframe<T>) {
58 self.keyframes.push(keyframe);
59 self.sort();
60 }
61
62 pub fn keyframe(mut self, time: f32, value: T) -> Self {
64 self.keyframes.push(Keyframe::new(time, value));
65 self.sort();
66 self
67 }
68
69 pub fn keyframe_eased(mut self, time: f32, value: T, easing: Easing) -> Self {
71 self
72 .keyframes
73 .push(Keyframe::new(time, value).with_easing(easing));
74 self.sort();
75 self
76 }
77
78 fn sort(&mut self) {
80 self
81 .keyframes
82 .sort_by(|a, b| a.time().partial_cmp(&b.time()).unwrap());
83 }
84
85 pub fn len(&self) -> usize {
87 self.keyframes.len()
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.keyframes.is_empty()
93 }
94
95 pub fn duration(&self) -> f32 {
97 self.keyframes.last().map(|kf| kf.time()).unwrap_or(0.0)
98 }
99
100 pub fn start_time(&self) -> f32 {
102 self.keyframes.first().map(|kf| kf.time()).unwrap_or(0.0)
103 }
104
105 pub fn get(&self, index: usize) -> Option<&Keyframe<T>> {
107 self.keyframes.get(index)
108 }
109
110 pub fn sample(&self, time: f32) -> T {
116 match self.keyframes.len() {
117 0 => panic!("Cannot sample empty keyframe track"),
118 1 => self.keyframes[0].value(),
119 _ => self.sample_between(time),
120 }
121 }
122
123 pub fn sample_clamped(&self, time: f32) -> T {
125 let clamped = time.clamp(self.start_time(), self.duration());
126
127 self.sample(clamped)
128 }
129
130 fn find_keyframe_pair(&self, time: f32) -> (usize, usize) {
132 let next_idx = self
134 .keyframes
135 .iter()
136 .position(|kf| kf.time() > time)
137 .unwrap_or(self.keyframes.len());
138
139 if next_idx == 0 {
140 (0, 0)
141 } else if next_idx >= self.keyframes.len() {
142 let last = self.keyframes.len() - 1;
143
144 (last, last)
145 } else {
146 (next_idx - 1, next_idx)
147 }
148 }
149
150 fn sample_between(&self, time: f32) -> T {
152 let (prev_idx, next_idx) = self.find_keyframe_pair(time);
153
154 if prev_idx == next_idx {
156 return self.keyframes[prev_idx].value();
157 }
158
159 let prev = &self.keyframes[prev_idx];
160 let next = &self.keyframes[next_idx];
161
162 let segment_duration = next.time() - prev.time();
164
165 if segment_duration == 0.0 {
166 return next.value();
167 }
168
169 let local_progress = (time - prev.time()) / segment_duration;
170
171 let easing = next.easing().unwrap_or(&self.default_easing);
173 let eased_progress = easing.y(local_progress);
174
175 prev.value().lerp(next.value(), eased_progress)
177 }
178
179 pub fn iter(&self) -> impl Iterator<Item = &Keyframe<T>> {
181 self.keyframes.iter()
182 }
183}
184
185impl<T: Tweenable> Default for KeyframeTrack<T> {
186 fn default() -> Self {
187 Self::new()
188 }
189}
190
191impl<T: Tweenable> FromIterator<Keyframe<T>> for KeyframeTrack<T> {
192 fn from_iter<I: IntoIterator<Item = Keyframe<T>>>(iter: I) -> Self {
193 let mut track = Self::new();
194
195 for kf in iter {
196 track.keyframes.push(kf);
197 }
198
199 track.sort();
200 track
201 }
202}
203
204impl<T: Tweenable> From<Vec<Keyframe<T>>> for KeyframeTrack<T> {
205 fn from(keyframes: Vec<Keyframe<T>>) -> Self {
206 let mut track = Self {
207 keyframes,
208 default_easing: Easing::Linear,
209 };
210
211 track.sort();
212 track
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 #[test]
221 fn test_sample_linear() {
222 let track = KeyframeTrack::new()
223 .keyframe(0.0, 0.0_f32)
224 .keyframe(1.0, 100.0);
225
226 assert_eq!(track.sample(0.0), 0.0);
227 assert!((track.sample(0.5) - 50.0).abs() < 0.001);
228 assert_eq!(track.sample(1.0), 100.0);
229 }
230
231 #[test]
232 fn test_sample_multiple_keyframes() {
233 let track = KeyframeTrack::new()
234 .keyframe(0.0, 0.0_f32)
235 .keyframe(0.5, 100.0)
236 .keyframe(1.0, 50.0);
237
238 assert_eq!(track.sample(0.0), 0.0);
239 assert!((track.sample(0.25) - 50.0).abs() < 0.001);
240 assert_eq!(track.sample(0.5), 100.0);
241 assert!((track.sample(0.75) - 75.0).abs() < 0.001);
242 assert_eq!(track.sample(1.0), 50.0);
243 }
244
245 #[test]
246 fn test_sample_before_first() {
247 let track = KeyframeTrack::new()
248 .keyframe(0.5, 100.0_f32)
249 .keyframe(1.0, 200.0);
250
251 assert_eq!(track.sample(0.0), 100.0);
253 assert_eq!(track.sample(0.25), 100.0);
254 }
255
256 #[test]
257 fn test_sample_after_last() {
258 let track = KeyframeTrack::new()
259 .keyframe(0.0, 0.0_f32)
260 .keyframe(0.5, 100.0);
261
262 assert_eq!(track.sample(0.75), 100.0);
264 assert_eq!(track.sample(1.0), 100.0);
265 }
266
267 #[test]
268 fn test_sample_single_keyframe() {
269 let track = KeyframeTrack::new().keyframe(0.5, 100.0_f32);
270
271 assert_eq!(track.sample(0.0), 100.0);
272 assert_eq!(track.sample(0.5), 100.0);
273 assert_eq!(track.sample(1.0), 100.0);
274 }
275
276 #[test]
277 fn test_sample_array() {
278 let track = KeyframeTrack::new()
279 .keyframe(0.0, [0.0_f32, 0.0, 0.0])
280 .keyframe(1.0, [100.0, 200.0, 300.0]);
281
282 let mid = track.sample(0.5);
283
284 assert!((mid[0] - 50.0).abs() < 0.001);
285 assert!((mid[1] - 100.0).abs() < 0.001);
286 assert!((mid[2] - 150.0).abs() < 0.001);
287 }
288
289 #[test]
290 fn test_duration() {
291 let track = KeyframeTrack::new()
292 .keyframe(0.0, 0.0_f32)
293 .keyframe(2.5, 100.0);
294
295 assert_eq!(track.duration(), 2.5);
296 }
297
298 #[test]
299 fn test_keyframe_ordering() {
300 let track = KeyframeTrack::new()
302 .keyframe(1.0, 100.0_f32)
303 .keyframe(0.0, 0.0)
304 .keyframe(0.5, 50.0);
305
306 assert_eq!(track.get(0).unwrap().time(), 0.0);
308 assert_eq!(track.get(1).unwrap().time(), 0.5);
309 assert_eq!(track.get(2).unwrap().time(), 1.0);
310 }
311
312 #[test]
313 fn test_from_iterator() {
314 let keyframes =
315 vec![Keyframe::new(1.0, 100.0_f32), Keyframe::new(0.0, 0.0)];
316
317 let track: KeyframeTrack<f32> = keyframes.into_iter().collect();
318
319 assert_eq!(track.len(), 2);
320 assert_eq!(track.get(0).unwrap().time(), 0.0);
321 }
322
323 #[test]
324 fn test_from_vec() {
325 let keyframes =
326 vec![Keyframe::new(1.0, 100.0_f32), Keyframe::new(0.0, 0.0)];
327
328 let track = KeyframeTrack::from(keyframes);
329
330 assert_eq!(track.len(), 2);
331 assert_eq!(track.get(0).unwrap().time(), 0.0);
332 }
333
334 #[test]
335 fn test_keyframes_macro() {
336 use crate::keyframes;
337
338 let track = keyframes![
339 (0.0, 0.0_f32),
340 (0.5, 100.0, Easing::InQuadratic),
341 (1.0, 50.0)
342 ];
343
344 assert_eq!(track.len(), 3);
345 assert_eq!(track.sample(0.0), 0.0);
346 assert_eq!(track.sample(1.0), 50.0);
347
348 let val = track.sample(0.25);
351
352 assert_eq!(val, 25.0);
353 }
354
355 #[test]
356 fn test_keyframes_macro_array() {
357 use crate::keyframes;
358
359 let track = keyframes![(0.0, [0.0_f32, 0.0]), (1.0, [100.0, 200.0])];
360
361 let mid = track.sample(0.5);
362
363 assert_eq!(mid, [50.0, 100.0]);
364 }
365}