oxihuman_export/
keyshape_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
7#[derive(Clone)]
8pub struct KeyShape {
9 pub name: String,
10 pub weight: f32,
11 pub deltas: Vec<[f32; 3]>,
12}
13
14#[allow(dead_code)]
16#[derive(Default)]
17pub struct KeyShapeExport {
18 pub shapes: Vec<KeyShape>,
19}
20
21#[allow(dead_code)]
23pub fn new_keyshape_export() -> KeyShapeExport {
24 KeyShapeExport::default()
25}
26
27#[allow(dead_code)]
29pub fn add_keyshape(export: &mut KeyShapeExport, name: &str, weight: f32, deltas: Vec<[f32; 3]>) {
30 export.shapes.push(KeyShape {
31 name: name.to_string(),
32 weight,
33 deltas,
34 });
35}
36
37#[allow(dead_code)]
39pub fn keyshape_count(export: &KeyShapeExport) -> usize {
40 export.shapes.len()
41}
42
43#[allow(dead_code)]
45pub fn find_keyshape<'a>(export: &'a KeyShapeExport, name: &str) -> Option<&'a KeyShape> {
46 export.shapes.iter().find(|s| s.name == name)
47}
48
49#[allow(dead_code)]
51pub fn total_keyshape_deltas(export: &KeyShapeExport) -> usize {
52 export.shapes.iter().map(|s| s.deltas.len()).sum()
53}
54
55#[allow(dead_code)]
57pub fn blend_keyshapes(a: &KeyShape, b: &KeyShape, t: f32) -> Vec<[f32; 3]> {
58 let n = a.deltas.len().min(b.deltas.len());
59 (0..n)
60 .map(|i| {
61 let da = a.deltas[i];
62 let db = b.deltas[i];
63 [
64 da[0] + t * (db[0] - da[0]),
65 da[1] + t * (db[1] - da[1]),
66 da[2] + t * (db[2] - da[2]),
67 ]
68 })
69 .collect()
70}
71
72#[allow(dead_code)]
74pub fn validate_keyshape_weights(export: &KeyShapeExport) -> bool {
75 export
76 .shapes
77 .iter()
78 .all(|s| (0.0..=1.0).contains(&s.weight))
79}
80
81#[allow(dead_code)]
83pub fn max_keyshape_delta(shape: &KeyShape) -> f32 {
84 shape
85 .deltas
86 .iter()
87 .map(|d| (d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt())
88 .fold(0.0_f32, f32::max)
89}
90
91#[allow(dead_code)]
93pub fn keyshape_to_json(export: &KeyShapeExport) -> String {
94 format!(
95 r#"{{"keyshapes":{},"total_deltas":{}}}"#,
96 export.shapes.len(),
97 total_keyshape_deltas(export)
98 )
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn add_and_count() {
107 let mut e = new_keyshape_export();
108 add_keyshape(&mut e, "smile", 0.5, vec![[0.1, 0.0, 0.0]; 5]);
109 assert_eq!(keyshape_count(&e), 1);
110 }
111
112 #[test]
113 fn find_shape() {
114 let mut e = new_keyshape_export();
115 add_keyshape(&mut e, "blink", 1.0, vec![]);
116 assert!(find_keyshape(&e, "blink").is_some());
117 }
118
119 #[test]
120 fn find_missing() {
121 let e = new_keyshape_export();
122 assert!(find_keyshape(&e, "x").is_none());
123 }
124
125 #[test]
126 fn total_deltas() {
127 let mut e = new_keyshape_export();
128 add_keyshape(&mut e, "a", 0.5, vec![[0.0; 3]; 3]);
129 add_keyshape(&mut e, "b", 0.5, vec![[0.0; 3]; 4]);
130 assert_eq!(total_keyshape_deltas(&e), 7);
131 }
132
133 #[test]
134 fn blend_midpoint() {
135 let a = KeyShape {
136 name: "a".to_string(),
137 weight: 0.0,
138 deltas: vec![[0.0, 0.0, 0.0]],
139 };
140 let b = KeyShape {
141 name: "b".to_string(),
142 weight: 1.0,
143 deltas: vec![[2.0, 0.0, 0.0]],
144 };
145 let blended = blend_keyshapes(&a, &b, 0.5);
146 assert!((blended[0][0] - 1.0).abs() < 1e-5);
147 }
148
149 #[test]
150 fn validate_valid() {
151 let mut e = new_keyshape_export();
152 add_keyshape(&mut e, "x", 0.7, vec![]);
153 assert!(validate_keyshape_weights(&e));
154 }
155
156 #[test]
157 fn validate_invalid() {
158 let mut e = new_keyshape_export();
159 add_keyshape(&mut e, "x", 1.5, vec![]);
160 assert!(!validate_keyshape_weights(&e));
161 }
162
163 #[test]
164 fn max_delta() {
165 let s = KeyShape {
166 name: "x".to_string(),
167 weight: 1.0,
168 deltas: vec![[3.0, 4.0, 0.0]],
169 };
170 assert!((max_keyshape_delta(&s) - 5.0).abs() < 1e-5);
171 }
172
173 #[test]
174 fn json_has_count() {
175 let e = new_keyshape_export();
176 let j = keyshape_to_json(&e);
177 assert!(j.contains("\"keyshapes\":0"));
178 }
179
180 #[test]
181 fn empty_total_deltas() {
182 let e = new_keyshape_export();
183 assert_eq!(total_keyshape_deltas(&e), 0);
184 }
185}