1#[allow(dead_code)]
7#[derive(Clone)]
8pub struct ExpressionSnapshot {
9 pub time: f32,
10 pub weights: Vec<(String, f32)>,
11 pub metadata: String,
12}
13
14#[allow(dead_code)]
15pub struct ExpressionRecording {
16 pub name: String,
17 pub snapshots: Vec<ExpressionSnapshot>,
18 pub fps: f32,
19 pub looping: bool,
20}
21
22#[allow(dead_code)]
23pub struct RecorderState {
24 pub recording: bool,
25 pub playing: bool,
26 pub current_time: f32,
27 pub playback_speed: f32,
28}
29
30#[allow(dead_code)]
31pub fn new_recording(name: &str, fps: f32) -> ExpressionRecording {
32 ExpressionRecording {
33 name: name.to_string(),
34 snapshots: Vec::new(),
35 fps,
36 looping: false,
37 }
38}
39
40#[allow(dead_code)]
41pub fn record_snapshot(rec: &mut ExpressionRecording, time: f32, weights: Vec<(String, f32)>) {
42 rec.snapshots.push(ExpressionSnapshot {
43 time,
44 weights,
45 metadata: String::new(),
46 });
47 rec.snapshots.sort_by(|a, b| {
48 a.time
49 .partial_cmp(&b.time)
50 .unwrap_or(std::cmp::Ordering::Equal)
51 });
52}
53
54#[allow(dead_code)]
55pub fn playback_at(rec: &ExpressionRecording, time: f32) -> Vec<(String, f32)> {
56 if rec.snapshots.is_empty() {
57 return Vec::new();
58 }
59 if rec.snapshots.len() == 1 {
60 return rec.snapshots[0].weights.clone();
61 }
62 let before = rec.snapshots.iter().rfind(|s| s.time <= time);
64 let after = rec.snapshots.iter().find(|s| s.time > time);
65 match (before, after) {
66 (Some(b), Some(a)) => {
67 let span = a.time - b.time;
68 let t = if span > 1e-8 {
69 (time - b.time) / span
70 } else {
71 0.0
72 };
73 interpolate_weights(&b.weights, &a.weights, t)
74 }
75 (Some(b), None) => b.weights.clone(),
76 (None, Some(a)) => a.weights.clone(),
77 (None, None) => Vec::new(),
78 }
79}
80
81fn interpolate_weights(a: &[(String, f32)], b: &[(String, f32)], t: f32) -> Vec<(String, f32)> {
82 let mut result: Vec<(String, f32)> = Vec::new();
83 for (name, wa) in a {
84 let wb = b
85 .iter()
86 .find(|(n, _)| n == name)
87 .map(|(_, w)| *w)
88 .unwrap_or(0.0);
89 result.push((name.clone(), wa + (wb - wa) * t));
90 }
91 for (name, wb) in b {
92 if !result.iter().any(|(n, _)| n == name) {
93 result.push((name.clone(), *wb * t));
94 }
95 }
96 result
97}
98
99#[allow(dead_code)]
100pub fn recording_duration(rec: &ExpressionRecording) -> f32 {
101 if rec.snapshots.is_empty() {
102 return 0.0;
103 }
104 let first = rec.snapshots.first().map_or(0.0, |s| s.time);
105 let last = rec.snapshots.last().map_or(0.0, |s| s.time);
106 last - first
107}
108
109#[allow(dead_code)]
110pub fn snapshot_count(rec: &ExpressionRecording) -> usize {
111 rec.snapshots.len()
112}
113
114#[allow(dead_code)]
115pub fn trim_recording(rec: &mut ExpressionRecording, start: f32, end: f32) {
116 rec.snapshots.retain(|s| s.time >= start && s.time <= end);
117}
118
119#[allow(dead_code)]
120pub fn reverse_recording(rec: &mut ExpressionRecording) {
121 if rec.snapshots.is_empty() {
122 return;
123 }
124 let max_t = rec.snapshots[rec.snapshots.len() - 1].time;
125 for snap in &mut rec.snapshots {
126 snap.time = max_t - snap.time;
127 }
128 rec.snapshots.reverse();
129}
130
131#[allow(dead_code)]
132pub fn scale_recording_time(rec: &mut ExpressionRecording, factor: f32) {
133 for snap in &mut rec.snapshots {
134 snap.time *= factor;
135 }
136}
137
138#[allow(dead_code)]
139pub fn new_recorder_state() -> RecorderState {
140 RecorderState {
141 recording: false,
142 playing: false,
143 current_time: 0.0,
144 playback_speed: 1.0,
145 }
146}
147
148#[allow(dead_code)]
149pub fn start_recording(state: &mut RecorderState) {
150 state.recording = true;
151 state.playing = false;
152}
153
154#[allow(dead_code)]
155pub fn stop_recording(state: &mut RecorderState) {
156 state.recording = false;
157}
158
159#[allow(dead_code)]
160pub fn start_playback(state: &mut RecorderState) {
161 state.playing = true;
162 state.recording = false;
163}
164
165#[allow(dead_code)]
166pub fn stop_playback(state: &mut RecorderState) {
167 state.playing = false;
168}
169
170#[allow(dead_code)]
171pub fn advance_playback(
172 state: &mut RecorderState,
173 rec: &ExpressionRecording,
174 dt: f32,
175) -> Vec<(String, f32)> {
176 if !state.playing {
177 return Vec::new();
178 }
179 state.current_time += dt * state.playback_speed;
180 let duration = recording_duration(rec);
181 if rec.looping && duration > 0.0 {
182 state.current_time %= duration;
183 } else if state.current_time > duration {
184 state.current_time = duration;
185 state.playing = false;
186 }
187 playback_at(rec, state.current_time)
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn test_new_recording() {
196 let rec = new_recording("test", 30.0);
197 assert_eq!(rec.name, "test");
198 assert_eq!(rec.fps, 30.0);
199 assert!(!rec.looping);
200 assert!(rec.snapshots.is_empty());
201 }
202
203 #[test]
204 fn test_record_snapshot() {
205 let mut rec = new_recording("r", 24.0);
206 record_snapshot(&mut rec, 0.0, vec![("smile".to_string(), 0.5)]);
207 record_snapshot(&mut rec, 1.0, vec![("smile".to_string(), 1.0)]);
208 assert_eq!(snapshot_count(&rec), 2);
209 }
210
211 #[test]
212 fn test_snapshot_count() {
213 let mut rec = new_recording("r", 24.0);
214 assert_eq!(snapshot_count(&rec), 0);
215 record_snapshot(&mut rec, 0.0, vec![]);
216 assert_eq!(snapshot_count(&rec), 1);
217 }
218
219 #[test]
220 fn test_recording_duration() {
221 let mut rec = new_recording("r", 24.0);
222 assert!((recording_duration(&rec)).abs() < 1e-5);
223 record_snapshot(&mut rec, 0.0, vec![]);
224 record_snapshot(&mut rec, 2.0, vec![]);
225 assert!((recording_duration(&rec) - 2.0).abs() < 1e-5);
226 }
227
228 #[test]
229 fn test_playback_at_empty() {
230 let rec = new_recording("r", 24.0);
231 let result = playback_at(&rec, 0.5);
232 assert!(result.is_empty());
233 }
234
235 #[test]
236 fn test_playback_at_interpolates() {
237 let mut rec = new_recording("r", 24.0);
238 record_snapshot(&mut rec, 0.0, vec![("brow".to_string(), 0.0)]);
239 record_snapshot(&mut rec, 1.0, vec![("brow".to_string(), 1.0)]);
240 let weights = playback_at(&rec, 0.5);
241 let brow = weights
242 .iter()
243 .find(|(n, _)| n == "brow")
244 .expect("should succeed");
245 assert!((brow.1 - 0.5).abs() < 1e-5);
246 }
247
248 #[test]
249 fn test_playback_at_before_start() {
250 let mut rec = new_recording("r", 24.0);
251 record_snapshot(&mut rec, 1.0, vec![("x".to_string(), 0.8)]);
252 let weights = playback_at(&rec, 0.0);
253 assert_eq!(weights.len(), 1);
254 }
255
256 #[test]
257 fn test_trim_recording() {
258 let mut rec = new_recording("r", 24.0);
259 for i in 0..5 {
260 record_snapshot(&mut rec, i as f32, vec![]);
261 }
262 trim_recording(&mut rec, 1.0, 3.0);
263 assert_eq!(snapshot_count(&rec), 3);
264 }
265
266 #[test]
267 fn test_reverse_recording() {
268 let mut rec = new_recording("r", 24.0);
269 record_snapshot(&mut rec, 0.0, vec![("a".to_string(), 0.0)]);
270 record_snapshot(&mut rec, 1.0, vec![("a".to_string(), 1.0)]);
271 reverse_recording(&mut rec);
272 assert!((rec.snapshots[0].time).abs() < 1e-5);
273 assert!((rec.snapshots[1].time - 1.0).abs() < 1e-5);
274 assert!((rec.snapshots[0].weights[0].1 - 1.0).abs() < 1e-5);
275 }
276
277 #[test]
278 fn test_scale_recording_time() {
279 let mut rec = new_recording("r", 24.0);
280 record_snapshot(&mut rec, 1.0, vec![]);
281 record_snapshot(&mut rec, 2.0, vec![]);
282 scale_recording_time(&mut rec, 2.0);
283 assert!((rec.snapshots[0].time - 2.0).abs() < 1e-5);
284 assert!((rec.snapshots[1].time - 4.0).abs() < 1e-5);
285 }
286
287 #[test]
288 fn test_new_recorder_state() {
289 let state = new_recorder_state();
290 assert!(!state.recording);
291 assert!(!state.playing);
292 assert!((state.current_time).abs() < 1e-5);
293 assert!((state.playback_speed - 1.0).abs() < 1e-5);
294 }
295
296 #[test]
297 fn test_recorder_state_transitions() {
298 let mut state = new_recorder_state();
299 start_recording(&mut state);
300 assert!(state.recording);
301 stop_recording(&mut state);
302 assert!(!state.recording);
303 start_playback(&mut state);
304 assert!(state.playing);
305 stop_playback(&mut state);
306 assert!(!state.playing);
307 }
308
309 #[test]
310 fn test_start_recording_stops_playback() {
311 let mut state = new_recorder_state();
312 start_playback(&mut state);
313 start_recording(&mut state);
314 assert!(!state.playing);
315 assert!(state.recording);
316 }
317
318 #[test]
319 fn test_advance_playback() {
320 let mut rec = new_recording("r", 24.0);
321 record_snapshot(&mut rec, 0.0, vec![("x".to_string(), 0.0)]);
322 record_snapshot(&mut rec, 2.0, vec![("x".to_string(), 1.0)]);
323 let mut state = new_recorder_state();
324 start_playback(&mut state);
325 let weights = advance_playback(&mut state, &rec, 1.0);
326 assert!(!weights.is_empty());
327 assert!((state.current_time - 1.0).abs() < 1e-5);
328 }
329
330 #[test]
331 fn test_advance_playback_not_playing() {
332 let rec = new_recording("r", 24.0);
333 let mut state = new_recorder_state();
334 let weights = advance_playback(&mut state, &rec, 1.0);
335 assert!(weights.is_empty());
336 }
337}