oxihuman_morph/
delta_mush.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct DeltaMushConfig {
10 pub iterations: usize,
12 pub smoothing: f32,
14 pub delta_scale: f32,
16}
17
18impl Default for DeltaMushConfig {
19 fn default() -> Self {
20 DeltaMushConfig {
21 iterations: 10,
22 smoothing: 0.5,
23 delta_scale: 1.0,
24 }
25 }
26}
27
28#[derive(Debug, Clone)]
30pub struct DeltaMush {
31 pub config: DeltaMushConfig,
32 pub smoothed: Vec<[f32; 3]>,
34}
35
36impl DeltaMush {
37 pub fn new(vertex_count: usize) -> Self {
38 DeltaMush {
39 config: DeltaMushConfig::default(),
40 smoothed: vec![[0.0; 3]; vertex_count],
41 }
42 }
43}
44
45pub fn new_delta_mush(vertex_count: usize) -> DeltaMush {
47 DeltaMush::new(vertex_count)
48}
49
50#[allow(clippy::needless_range_loop)]
52pub fn delta_mush_smooth(dm: &mut DeltaMush, positions: &[[f32; 3]]) {
53 let n = dm.smoothed.len().min(positions.len());
54 for i in 0..n {
55 let p = positions[i];
56 let s = dm.smoothed[i];
57 let t = dm.config.smoothing;
58 dm.smoothed[i] = [
59 s[0] + t * (p[0] - s[0]),
60 s[1] + t * (p[1] - s[1]),
61 s[2] + t * (p[2] - s[2]),
62 ];
63 }
64}
65
66pub fn delta_mush_vertex_count(dm: &DeltaMush) -> usize {
68 dm.smoothed.len()
69}
70
71pub fn delta_mush_to_json(dm: &DeltaMush) -> String {
73 format!(
74 r#"{{"iterations":{},"smoothing":{:.4},"delta_scale":{:.4},"vertices":{}}}"#,
75 dm.config.iterations,
76 dm.config.smoothing,
77 dm.config.delta_scale,
78 dm.smoothed.len()
79 )
80}
81
82pub fn delta_mush_reset(dm: &mut DeltaMush) {
84 for s in &mut dm.smoothed {
85 *s = [0.0; 3];
86 }
87}
88
89pub fn delta_mush_set_smoothing(dm: &mut DeltaMush, smoothing: f32) {
91 dm.config.smoothing = smoothing.clamp(0.0, 1.0);
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn test_new_delta_mush_vertex_count() {
100 let dm = new_delta_mush(20);
101 assert_eq!(
102 delta_mush_vertex_count(&dm),
103 20, );
105 }
106
107 #[test]
108 fn test_default_iterations() {
109 let dm = new_delta_mush(5);
110 assert_eq!(dm.config.iterations, 10 ,);
111 }
112
113 #[test]
114 fn test_smooth_moves_toward_target() {
115 let mut dm = new_delta_mush(1);
116 delta_mush_smooth(&mut dm, &[[2.0, 0.0, 0.0]]);
117 assert!(dm.smoothed[0][0] > 0.0, );
118 }
119
120 #[test]
121 fn test_reset_zeroes_positions() {
122 let mut dm = new_delta_mush(3);
123 delta_mush_smooth(
124 &mut dm,
125 &[[1.0, 1.0, 1.0], [2.0, 2.0, 2.0], [3.0, 3.0, 3.0]],
126 );
127 delta_mush_reset(&mut dm);
128 for s in &dm.smoothed {
129 assert!((s[0]).abs() < 1e-6, );
130 }
131 }
132
133 #[test]
134 fn test_set_smoothing_clamps() {
135 let mut dm = new_delta_mush(2);
136 delta_mush_set_smoothing(&mut dm, 2.0);
137 assert!((dm.config.smoothing - 1.0).abs() < 1e-5, );
138 }
139
140 #[test]
141 fn test_set_smoothing_negative_clamps() {
142 let mut dm = new_delta_mush(2);
143 delta_mush_set_smoothing(&mut dm, -1.0);
144 assert!((dm.config.smoothing).abs() < 1e-6, );
145 }
146
147 #[test]
148 fn test_to_json_contains_iterations() {
149 let dm = new_delta_mush(4);
150 let j = delta_mush_to_json(&dm);
151 assert!(j.contains("iterations"), );
152 }
153
154 #[test]
155 fn test_smoothed_initialized_zero() {
156 let dm = new_delta_mush(5);
157 for s in &dm.smoothed {
158 assert!((s[0]).abs() < 1e-6, );
159 }
160 }
161
162 #[test]
163 fn test_smooth_ignores_extra_positions() {
164 let mut dm = new_delta_mush(2);
165 delta_mush_smooth(&mut dm, &[[1.0, 0.0, 0.0]; 10]);
166 assert_eq!(
167 delta_mush_vertex_count(&dm),
168 2, );
170 }
171
172 #[test]
173 fn test_delta_scale_default_one() {
174 let dm = new_delta_mush(1);
175 assert!((dm.config.delta_scale - 1.0).abs() < 1e-5, );
176 }
177
178 #[test]
179 fn test_to_json_contains_vertices() {
180 let dm = new_delta_mush(7);
181 let j = delta_mush_to_json(&dm);
182 assert!(j.contains("7") ,);
183 }
184}