oxihuman_export/
keyframe_set_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone, Copy)]
10pub struct KeyframeValue {
11 pub time: f32,
12 pub value: f32,
13}
14
15#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct KeyframeChannel {
19 pub name: String,
20 pub keys: Vec<KeyframeValue>,
21}
22
23#[allow(dead_code)]
25#[derive(Debug, Clone)]
26pub struct KeyframeSetExport {
27 pub channels: Vec<KeyframeChannel>,
28}
29
30#[allow(dead_code)]
32pub fn new_keyframe_set_export() -> KeyframeSetExport {
33 KeyframeSetExport {
34 channels: Vec::new(),
35 }
36}
37
38#[allow(dead_code)]
40pub fn add_channel(export: &mut KeyframeSetExport, channel: KeyframeChannel) {
41 export.channels.push(channel);
42}
43
44#[allow(dead_code)]
46pub fn channel_count_ks(export: &KeyframeSetExport) -> usize {
47 export.channels.len()
48}
49
50#[allow(dead_code)]
52pub fn total_keyframe_count(export: &KeyframeSetExport) -> usize {
53 export.channels.iter().map(|c| c.keys.len()).sum()
54}
55
56#[allow(dead_code)]
58pub fn find_channel_ks<'a>(
59 export: &'a KeyframeSetExport,
60 name: &str,
61) -> Option<&'a KeyframeChannel> {
62 export.channels.iter().find(|c| c.name == name)
63}
64
65#[allow(dead_code)]
67pub fn keyframe_set_duration(export: &KeyframeSetExport) -> f32 {
68 export
69 .channels
70 .iter()
71 .flat_map(|c| c.keys.iter().map(|k| k.time))
72 .fold(0.0f32, f32::max)
73}
74
75#[allow(dead_code)]
77pub fn sample_channel_ks(channel: &KeyframeChannel, t: f32) -> f32 {
78 if channel.keys.is_empty() {
79 return 0.0;
80 }
81 if channel.keys.len() == 1 {
82 return channel.keys[0].value;
83 }
84 let idx = channel
85 .keys
86 .partition_point(|k| k.time <= t)
87 .saturating_sub(1);
88 let i0 = idx.min(channel.keys.len() - 1);
89 let i1 = (idx + 1).min(channel.keys.len() - 1);
90 if i0 == i1 {
91 return channel.keys[i0].value;
92 }
93 let k0 = channel.keys[i0];
94 let k1 = channel.keys[i1];
95 let dt = k1.time - k0.time;
96 if dt <= 0.0 {
97 return k0.value;
98 }
99 let f = ((t - k0.time) / dt).clamp(0.0, 1.0);
100 k0.value + f * (k1.value - k0.value)
101}
102
103#[allow(dead_code)]
105pub fn keyframe_set_to_json(export: &KeyframeSetExport) -> String {
106 format!(
107 "{{\"channels\":{},\"total_keys\":{}}}",
108 export.channels.len(),
109 total_keyframe_count(export)
110 )
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 fn linear_channel() -> KeyframeChannel {
118 KeyframeChannel {
119 name: "tx".to_string(),
120 keys: vec![
121 KeyframeValue {
122 time: 0.0,
123 value: 0.0,
124 },
125 KeyframeValue {
126 time: 1.0,
127 value: 1.0,
128 },
129 ],
130 }
131 }
132
133 #[test]
134 fn test_add_and_count() {
135 let mut e = new_keyframe_set_export();
136 add_channel(&mut e, linear_channel());
137 assert_eq!(channel_count_ks(&e), 1);
138 }
139
140 #[test]
141 fn test_total_keyframe_count() {
142 let mut e = new_keyframe_set_export();
143 add_channel(&mut e, linear_channel());
144 assert_eq!(total_keyframe_count(&e), 2);
145 }
146
147 #[test]
148 fn test_find_channel() {
149 let mut e = new_keyframe_set_export();
150 add_channel(&mut e, linear_channel());
151 assert!(find_channel_ks(&e, "tx").is_some());
152 }
153
154 #[test]
155 fn test_duration() {
156 let mut e = new_keyframe_set_export();
157 add_channel(&mut e, linear_channel());
158 assert!((keyframe_set_duration(&e) - 1.0).abs() < 1e-5);
159 }
160
161 #[test]
162 fn test_sample_at_start() {
163 let c = linear_channel();
164 assert!(sample_channel_ks(&c, 0.0).abs() < 1e-5);
165 }
166
167 #[test]
168 fn test_sample_at_end() {
169 let c = linear_channel();
170 assert!((sample_channel_ks(&c, 1.0) - 1.0).abs() < 1e-5);
171 }
172
173 #[test]
174 fn test_sample_at_mid() {
175 let c = linear_channel();
176 assert!((sample_channel_ks(&c, 0.5) - 0.5).abs() < 1e-5);
177 }
178
179 #[test]
180 fn test_keyframe_set_to_json() {
181 let e = new_keyframe_set_export();
182 let j = keyframe_set_to_json(&e);
183 assert!(j.contains("channels"));
184 }
185
186 #[test]
187 fn test_sample_empty_channel() {
188 let c = KeyframeChannel {
189 name: "x".to_string(),
190 keys: vec![],
191 };
192 assert!(sample_channel_ks(&c, 0.5).abs() < 1e-6);
193 }
194
195 #[test]
196 fn test_empty_duration_zero() {
197 let e = new_keyframe_set_export();
198 assert!(keyframe_set_duration(&e).abs() < 1e-6);
199 }
200}