oxihuman_export/
morph_target_export.rs1#![allow(dead_code)]
2#[allow(dead_code)]
7#[derive(Debug, Clone)]
8pub struct MorphTarget {
9 pub name: String,
10 pub deltas: Vec<f32>,
12}
13
14#[allow(dead_code)]
16#[derive(Debug, Clone)]
17pub struct MorphTargetExport {
18 pub targets: Vec<MorphTarget>,
19}
20
21#[allow(dead_code)]
23pub fn export_morph_targets(names: &[&str], deltas: &[Vec<f32>]) -> MorphTargetExport {
24 let mut targets = Vec::new();
25 for (i, &name) in names.iter().enumerate() {
26 targets.push(MorphTarget {
27 name: name.to_string(),
28 deltas: deltas.get(i).cloned().unwrap_or_default(),
29 });
30 }
31 MorphTargetExport { targets }
32}
33
34#[allow(dead_code)]
36pub fn morph_target_count_export(exp: &MorphTargetExport) -> usize {
37 exp.targets.len()
38}
39
40#[allow(dead_code)]
42pub fn morph_target_name(exp: &MorphTargetExport, index: usize) -> Option<&str> {
43 exp.targets.get(index).map(|t| t.name.as_str())
44}
45
46#[allow(dead_code)]
48pub fn morph_target_delta_count(exp: &MorphTargetExport, index: usize) -> usize {
49 exp.targets.get(index).map_or(0, |t| t.deltas.len() / 3)
50}
51
52#[allow(dead_code)]
54pub fn morph_target_to_json(exp: &MorphTargetExport) -> String {
55 let mut s = String::from("{\"targets\":[");
56 for (i, t) in exp.targets.iter().enumerate() {
57 if i > 0 {
58 s.push(',');
59 }
60 s.push_str(&format!(
61 "{{\"name\":\"{}\",\"delta_verts\":{}}}",
62 t.name,
63 t.deltas.len() / 3
64 ));
65 }
66 s.push_str("]}");
67 s
68}
69
70#[allow(dead_code)]
72pub fn morph_target_to_bytes(exp: &MorphTargetExport) -> Vec<u8> {
73 let mut buf = Vec::new();
74 buf.extend_from_slice(&(exp.targets.len() as u32).to_le_bytes());
75 for t in &exp.targets {
76 let name_bytes = t.name.as_bytes();
77 buf.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
78 buf.extend_from_slice(name_bytes);
79 buf.extend_from_slice(&(t.deltas.len() as u32).to_le_bytes());
80 for &d in &t.deltas {
81 buf.extend_from_slice(&d.to_le_bytes());
82 }
83 }
84 buf
85}
86
87#[allow(dead_code)]
89pub fn morph_target_export_size(exp: &MorphTargetExport) -> usize {
90 morph_target_to_bytes(exp).len()
91}
92
93#[allow(dead_code)]
95pub fn validate_morph_target_export(exp: &MorphTargetExport) -> bool {
96 if exp.targets.is_empty() {
97 return true;
98 }
99 let first_len = exp.targets[0].deltas.len();
100 exp.targets.iter().all(|t| t.deltas.len() == first_len)
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 fn sample() -> MorphTargetExport {
108 export_morph_targets(
109 &["smile", "blink"],
110 &[
111 vec![0.1, 0.0, 0.0, 0.0, 0.1, 0.0],
112 vec![0.0, -0.1, 0.0, 0.0, 0.0, 0.1],
113 ],
114 )
115 }
116
117 #[test]
118 fn test_count() {
119 assert_eq!(morph_target_count_export(&sample()), 2);
120 }
121
122 #[test]
123 fn test_name() {
124 assert_eq!(morph_target_name(&sample(), 0), Some("smile"));
125 assert_eq!(morph_target_name(&sample(), 1), Some("blink"));
126 assert_eq!(morph_target_name(&sample(), 5), None);
127 }
128
129 #[test]
130 fn test_delta_count() {
131 assert_eq!(morph_target_delta_count(&sample(), 0), 2);
132 }
133
134 #[test]
135 fn test_to_json() {
136 let j = morph_target_to_json(&sample());
137 assert!(j.contains("\"smile\""));
138 assert!(j.contains("\"blink\""));
139 }
140
141 #[test]
142 fn test_to_bytes() {
143 let b = morph_target_to_bytes(&sample());
144 let tc = u32::from_le_bytes([b[0], b[1], b[2], b[3]]);
145 assert_eq!(tc, 2);
146 }
147
148 #[test]
149 fn test_export_size() {
150 assert!(morph_target_export_size(&sample()) > 0);
151 }
152
153 #[test]
154 fn test_validate_ok() {
155 assert!(validate_morph_target_export(&sample()));
156 }
157
158 #[test]
159 fn test_validate_mismatch() {
160 let e = export_morph_targets(&["a", "b"], &[vec![1.0, 2.0, 3.0], vec![1.0]]);
161 assert!(!validate_morph_target_export(&e));
162 }
163
164 #[test]
165 fn test_empty() {
166 let e = export_morph_targets(&[], &[]);
167 assert_eq!(morph_target_count_export(&e), 0);
168 assert!(validate_morph_target_export(&e));
169 }
170
171 #[test]
172 fn test_delta_count_oob() {
173 assert_eq!(morph_target_delta_count(&sample(), 99), 0);
174 }
175}