1#![allow(dead_code)]
5
6use std::collections::HashMap;
7
8#[derive(Clone, Debug, PartialEq)]
10pub enum TrackInterp {
11 Step,
13 Linear,
15 SmoothStep,
17 Cubic,
19}
20
21#[derive(Clone, Debug)]
23pub struct Keyframe {
24 pub time: f32,
26 pub value: f32,
28}
29
30pub struct AnimTrack {
32 pub name: String,
34 pub interp: TrackInterp,
36 keyframes: Vec<Keyframe>,
38}
39
40impl AnimTrack {
41 pub fn new(name: impl Into<String>, interp: TrackInterp) -> Self {
43 Self {
44 name: name.into(),
45 interp,
46 keyframes: Vec::new(),
47 }
48 }
49
50 pub fn add_keyframe(&mut self, time: f32, value: f32) {
54 if let Some(kf) = self
56 .keyframes
57 .iter_mut()
58 .find(|k| (k.time - time).abs() < f32::EPSILON)
59 {
60 kf.value = value;
61 return;
62 }
63 let pos = self.keyframes.partition_point(|k| k.time < time);
64 self.keyframes.insert(pos, Keyframe { time, value });
65 }
66
67 pub fn remove_keyframe(&mut self, time: f32) -> bool {
70 if let Some(pos) = self
71 .keyframes
72 .iter()
73 .position(|k| (k.time - time).abs() < f32::EPSILON)
74 {
75 self.keyframes.remove(pos);
76 true
77 } else {
78 false
79 }
80 }
81
82 pub fn keyframe_count(&self) -> usize {
84 self.keyframes.len()
85 }
86
87 pub fn keyframes(&self) -> &[Keyframe] {
89 &self.keyframes
90 }
91
92 pub fn duration(&self) -> f32 {
94 self.keyframes.last().map(|k| k.time).unwrap_or(0.0)
95 }
96
97 pub fn evaluate(&self, time: f32) -> f32 {
99 if self.keyframes.is_empty() {
100 return 0.0;
101 }
102 let first = &self.keyframes[0];
103 let last = &self.keyframes[self.keyframes.len() - 1];
104
105 if time <= first.time {
106 return first.value;
107 }
108 if time >= last.time {
109 return last.value;
110 }
111
112 let idx_b = self.keyframes.partition_point(|k| k.time <= time);
114 let idx_a = idx_b - 1;
115
116 let ka = &self.keyframes[idx_a];
117 let kb = &self.keyframes[idx_b];
118
119 let span = kb.time - ka.time;
120 let t = if span > 0.0 {
121 (time - ka.time) / span
122 } else {
123 0.0
124 };
125
126 match self.interp {
127 TrackInterp::Step => ka.value,
128 TrackInterp::Linear => lerp(ka.value, kb.value, t),
129 TrackInterp::SmoothStep => {
130 let s = t * t * (3.0 - 2.0 * t);
131 lerp(ka.value, kb.value, s)
132 }
133 TrackInterp::Cubic => {
134 let n = self.keyframes.len();
136 let p0 = if idx_a == 0 {
137 ka.value
138 } else {
139 self.keyframes[idx_a - 1].value
140 };
141 let p3 = if idx_b + 1 >= n {
142 kb.value
143 } else {
144 self.keyframes[idx_b + 1].value
145 };
146 catmull_rom_scalar(p0, ka.value, kb.value, p3, t)
147 }
148 }
149 }
150
151 pub fn set_interp(&mut self, interp: TrackInterp) {
153 self.interp = interp;
154 }
155}
156
157#[inline]
162fn lerp(a: f32, b: f32, t: f32) -> f32 {
163 a + (b - a) * t
164}
165
166#[inline]
168fn catmull_rom_scalar(p0: f32, p1: f32, p2: f32, p3: f32, t: f32) -> f32 {
169 let t2 = t * t;
170 let t3 = t2 * t;
171 0.5 * ((2.0 * p1)
172 + (-p0 + p2) * t
173 + (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2
174 + (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3)
175}
176
177pub struct Timeline {
183 pub name: String,
185 pub fps: f32,
187 tracks: HashMap<String, AnimTrack>,
188}
189
190impl Timeline {
191 pub fn new(name: impl Into<String>) -> Self {
193 Self {
194 name: name.into(),
195 fps: 24.0,
196 tracks: HashMap::new(),
197 }
198 }
199
200 pub fn with_fps(name: impl Into<String>, fps: f32) -> Self {
202 Self {
203 name: name.into(),
204 fps,
205 tracks: HashMap::new(),
206 }
207 }
208
209 pub fn add_track(&mut self, track: AnimTrack) {
211 self.tracks.insert(track.name.clone(), track);
212 }
213
214 pub fn remove_track(&mut self, name: &str) -> bool {
216 self.tracks.remove(name).is_some()
217 }
218
219 pub fn get_track(&self, name: &str) -> Option<&AnimTrack> {
221 self.tracks.get(name)
222 }
223
224 pub fn get_track_mut(&mut self, name: &str) -> Option<&mut AnimTrack> {
226 self.tracks.get_mut(name)
227 }
228
229 pub fn track_names(&self) -> Vec<&str> {
231 let mut names: Vec<&str> = self.tracks.keys().map(String::as_str).collect();
232 names.sort_unstable();
233 names
234 }
235
236 pub fn track_count(&self) -> usize {
238 self.tracks.len()
239 }
240
241 pub fn duration(&self) -> f32 {
243 self.tracks
244 .values()
245 .map(|t| t.duration())
246 .fold(0.0_f32, f32::max)
247 }
248
249 pub fn frame_count(&self) -> usize {
251 (self.duration() * self.fps).ceil() as usize
252 }
253
254 pub fn evaluate(&self, time: f32) -> HashMap<String, f32> {
256 self.tracks
257 .iter()
258 .map(|(name, track)| (name.clone(), track.evaluate(time)))
259 .collect()
260 }
261
262 pub fn bake(&self, sample_fps: f32) -> Vec<HashMap<String, f32>> {
265 let duration = self.duration();
266 if duration <= 0.0 || sample_fps <= 0.0 {
267 return Vec::new();
268 }
269 let dt = 1.0 / sample_fps;
270 let mut frames = Vec::new();
271 let mut t = 0.0_f32;
272 while t <= duration + f32::EPSILON {
273 frames.push(self.evaluate(t));
274 t += dt;
275 }
276 frames
277 }
278
279 pub fn set_key(&mut self, track_name: &str, time: f32, value: f32) {
281 let track = self
282 .tracks
283 .entry(track_name.to_owned())
284 .or_insert_with(|| AnimTrack::new(track_name, TrackInterp::Linear));
285 track.add_keyframe(time, value);
286 }
287
288 pub fn time_offset(&mut self, dt: f32) {
290 for track in self.tracks.values_mut() {
291 for kf in track.keyframes.iter_mut() {
292 kf.time += dt;
293 }
294 }
295 }
296
297 pub fn time_scale(&mut self, factor: f32) {
299 for track in self.tracks.values_mut() {
300 for kf in track.keyframes.iter_mut() {
301 kf.time *= factor;
302 }
303 }
304 }
305}
306
307impl Default for Timeline {
308 fn default() -> Self {
309 Self::new("untitled")
310 }
311}
312
313#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[test]
324 fn test_track_new() {
325 let track = AnimTrack::new("jaw_open", TrackInterp::Linear);
326 assert_eq!(track.name, "jaw_open");
327 assert_eq!(track.interp, TrackInterp::Linear);
328 assert_eq!(track.keyframe_count(), 0);
329 }
330
331 #[test]
332 fn test_track_add_keyframe_sorted() {
333 let mut track = AnimTrack::new("t", TrackInterp::Linear);
334 track.add_keyframe(2.0, 0.8);
335 track.add_keyframe(0.0, 0.0);
336 track.add_keyframe(1.0, 0.5);
337 let kfs = track.keyframes();
338 assert_eq!(kfs.len(), 3);
339 assert!((kfs[0].time - 0.0).abs() < f32::EPSILON);
340 assert!((kfs[1].time - 1.0).abs() < f32::EPSILON);
341 assert!((kfs[2].time - 2.0).abs() < f32::EPSILON);
342 track.add_keyframe(1.0, 0.9);
344 assert_eq!(track.keyframe_count(), 3);
345 assert!((track.keyframes()[1].value - 0.9).abs() < f32::EPSILON);
346 }
347
348 #[test]
349 fn test_track_evaluate_empty() {
350 let track = AnimTrack::new("empty", TrackInterp::Linear);
351 assert!((track.evaluate(0.5) - 0.0).abs() < f32::EPSILON);
352 }
353
354 #[test]
355 fn test_track_evaluate_single() {
356 let mut track = AnimTrack::new("single", TrackInterp::Linear);
357 track.add_keyframe(1.0, 0.42);
358 assert!((track.evaluate(0.0) - 0.42).abs() < 1e-6);
359 assert!((track.evaluate(1.0) - 0.42).abs() < 1e-6);
360 assert!((track.evaluate(5.0) - 0.42).abs() < 1e-6);
361 }
362
363 #[test]
364 fn test_track_evaluate_linear() {
365 let mut track = AnimTrack::new("lin", TrackInterp::Linear);
366 track.add_keyframe(0.0, 0.0);
367 track.add_keyframe(1.0, 1.0);
368 assert!((track.evaluate(0.5) - 0.5).abs() < 1e-6);
369 assert!((track.evaluate(0.25) - 0.25).abs() < 1e-6);
370 assert!((track.evaluate(0.75) - 0.75).abs() < 1e-6);
371 }
372
373 #[test]
374 fn test_track_evaluate_step() {
375 let mut track = AnimTrack::new("step", TrackInterp::Step);
376 track.add_keyframe(0.0, 0.0);
377 track.add_keyframe(1.0, 1.0);
378 assert!((track.evaluate(0.5) - 0.0).abs() < 1e-6);
380 assert!((track.evaluate(1.0) - 1.0).abs() < 1e-6);
382 }
383
384 #[test]
385 fn test_track_evaluate_smoothstep() {
386 let mut track = AnimTrack::new("smooth", TrackInterp::SmoothStep);
387 track.add_keyframe(0.0, 0.0);
388 track.add_keyframe(1.0, 1.0);
389 let v = track.evaluate(0.5);
391 assert!((v - 0.5).abs() < 1e-5);
392 let v_low = track.evaluate(0.25);
394 assert!(v_low < 0.25 + 1e-5);
395 }
396
397 #[test]
398 fn test_track_duration() {
399 let track = AnimTrack::new("empty", TrackInterp::Linear);
400 assert!((track.duration() - 0.0).abs() < f32::EPSILON);
401
402 let mut track2 = AnimTrack::new("t2", TrackInterp::Linear);
403 track2.add_keyframe(0.0, 0.0);
404 track2.add_keyframe(3.5, 1.0);
405 assert!((track2.duration() - 3.5).abs() < 1e-6);
406 }
407
408 #[test]
409 fn test_track_remove_keyframe() {
410 let mut track = AnimTrack::new("rem", TrackInterp::Linear);
411 track.add_keyframe(0.0, 0.0);
412 track.add_keyframe(1.0, 1.0);
413 assert!(track.remove_keyframe(0.0));
414 assert_eq!(track.keyframe_count(), 1);
415 assert!(!track.remove_keyframe(99.0));
417 }
418
419 #[test]
422 fn test_timeline_new() {
423 let tl = Timeline::new("main");
424 assert_eq!(tl.name, "main");
425 assert!((tl.fps - 24.0).abs() < f32::EPSILON);
426 assert_eq!(tl.track_count(), 0);
427 }
428
429 #[test]
430 fn test_timeline_add_track() {
431 let mut tl = Timeline::new("main");
432 let track = AnimTrack::new("smile", TrackInterp::Linear);
433 tl.add_track(track);
434 assert_eq!(tl.track_count(), 1);
435 assert!(tl.get_track("smile").is_some());
436 assert!(tl.remove_track("smile"));
437 assert_eq!(tl.track_count(), 0);
438 assert!(!tl.remove_track("smile"));
439 }
440
441 #[test]
442 fn test_timeline_evaluate() {
443 let mut tl = Timeline::new("main");
444 let mut track = AnimTrack::new("brow_raise", TrackInterp::Linear);
445 track.add_keyframe(0.0, 0.0);
446 track.add_keyframe(1.0, 1.0);
447 tl.add_track(track);
448 let snapshot = tl.evaluate(0.5);
449 let v = snapshot["brow_raise"];
450 assert!((v - 0.5).abs() < 1e-5);
451 }
452
453 #[test]
454 fn test_timeline_bake() {
455 let mut tl = Timeline::new("bake_test");
456 let mut track = AnimTrack::new("p", TrackInterp::Linear);
457 track.add_keyframe(0.0, 0.0);
458 track.add_keyframe(1.0, 1.0);
459 tl.add_track(track);
460 let frames = tl.bake(10.0);
462 assert_eq!(frames.len(), 11);
463 assert!((frames[0]["p"] - 0.0).abs() < 1e-5);
465 assert!((frames[10]["p"] - 1.0).abs() < 1e-4);
467 }
468
469 #[test]
470 fn test_timeline_set_key() {
471 let mut tl = Timeline::new("sk");
472 tl.set_key("eye_blink", 0.0, 0.0);
473 tl.set_key("eye_blink", 0.5, 1.0);
474 let track = tl.get_track("eye_blink").expect("should succeed");
475 assert_eq!(track.keyframe_count(), 2);
476 assert_eq!(track.interp, TrackInterp::Linear);
477 }
478
479 #[test]
480 fn test_timeline_time_scale() {
481 let mut tl = Timeline::new("scale");
482 tl.set_key("p", 0.0, 0.0);
483 tl.set_key("p", 2.0, 1.0);
484 assert!((tl.duration() - 2.0).abs() < 1e-5);
485 tl.time_scale(0.5);
486 assert!((tl.duration() - 1.0).abs() < 1e-5);
487 }
488}