1#[allow(dead_code)]
4pub struct WrinkleConfig {
5 pub frequency: f32,
6 pub amplitude: f32,
7 pub threshold: f32,
8 pub blend_sharpness: f32,
9}
10
11#[allow(dead_code)]
12pub struct WrinkleRegion {
13 pub name: String,
14 pub vertex_indices: Vec<usize>,
15 pub direction: [f32; 3],
16 pub intensity: f32,
17}
18
19#[allow(dead_code)]
20pub struct WrinkleMap {
21 pub values: Vec<f32>,
22 pub vertex_count: usize,
23 pub config: WrinkleConfig,
24}
25
26#[allow(dead_code)]
27pub fn default_wrinkle_config() -> WrinkleConfig {
28 WrinkleConfig {
29 frequency: 1.0,
30 amplitude: 1.0,
31 threshold: 0.01,
32 blend_sharpness: 2.0,
33 }
34}
35
36#[allow(dead_code)]
37pub fn new_wrinkle_map(vertex_count: usize, cfg: WrinkleConfig) -> WrinkleMap {
38 WrinkleMap {
39 values: vec![0.0; vertex_count],
40 vertex_count,
41 config: cfg,
42 }
43}
44
45#[allow(dead_code)]
46pub fn compute_wrinkle_from_deformation(
47 original: &[[f32; 3]],
48 deformed: &[[f32; 3]],
49 cfg: &WrinkleConfig,
50) -> WrinkleMap {
51 let n = original.len().min(deformed.len());
52 let mut values = vec![0.0f32; n];
53 for (i, (orig, def)) in original.iter().zip(deformed.iter()).enumerate() {
54 let dx = def[0] - orig[0];
55 let dy = def[1] - orig[1];
56 let dz = def[2] - orig[2];
57 let mag = (dx * dx + dy * dy + dz * dz).sqrt();
58 if mag > cfg.threshold {
59 values[i] = (mag * cfg.amplitude).min(1.0);
60 }
61 }
62 WrinkleMap {
63 values,
64 vertex_count: n,
65 config: WrinkleConfig {
66 frequency: cfg.frequency,
67 amplitude: cfg.amplitude,
68 threshold: cfg.threshold,
69 blend_sharpness: cfg.blend_sharpness,
70 },
71 }
72}
73
74#[allow(dead_code)]
75pub fn add_procedural_wrinkles(
76 map: &mut WrinkleMap,
77 region: &WrinkleRegion,
78 positions: &[[f32; 3]],
79) {
80 let dir = region.direction;
81 let dir_len = (dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]).sqrt();
82 let dir_norm = if dir_len > 1e-6 {
83 [dir[0] / dir_len, dir[1] / dir_len, dir[2] / dir_len]
84 } else {
85 [1.0, 0.0, 0.0]
86 };
87
88 for &idx in ®ion.vertex_indices {
89 if idx >= map.vertex_count || idx >= positions.len() {
90 continue;
91 }
92 let pos = positions[idx];
93 let proj = pos[0] * dir_norm[0] + pos[1] * dir_norm[1] + pos[2] * dir_norm[2];
94 let wave = ((proj * map.config.frequency * std::f32::consts::TAU).sin() * 0.5 + 0.5)
95 * region.intensity;
96 map.values[idx] = (map.values[idx] + wave).min(1.0);
97 }
98}
99
100#[allow(dead_code)]
101pub fn smooth_wrinkle_map(map: &mut WrinkleMap, adjacency: &[Vec<usize>], iterations: u32) {
102 for _ in 0..iterations {
103 let old = map.values.clone();
104 for (i, neighbors) in adjacency.iter().enumerate() {
105 if i >= map.vertex_count {
106 continue;
107 }
108 if neighbors.is_empty() {
109 continue;
110 }
111 let sum: f32 = neighbors
112 .iter()
113 .filter(|&&n| n < map.vertex_count)
114 .map(|&n| old[n])
115 .sum();
116 let valid_count = neighbors.iter().filter(|&&n| n < map.vertex_count).count();
117 if valid_count > 0 {
118 map.values[i] = (old[i] + sum / valid_count as f32) * 0.5;
119 }
120 }
121 }
122}
123
124#[allow(dead_code)]
125pub fn wrinkle_map_min(map: &WrinkleMap) -> f32 {
126 map.values.iter().cloned().fold(f32::INFINITY, f32::min)
127}
128
129#[allow(dead_code)]
130pub fn wrinkle_map_max(map: &WrinkleMap) -> f32 {
131 map.values.iter().cloned().fold(f32::NEG_INFINITY, f32::max)
132}
133
134#[allow(dead_code)]
135pub fn normalize_wrinkle_map(map: &mut WrinkleMap) {
136 let mn = wrinkle_map_min(map);
137 let mx = wrinkle_map_max(map);
138 let range = mx - mn;
139 if range > 1e-6 {
140 for v in &mut map.values {
141 *v = (*v - mn) / range;
142 }
143 }
144}
145
146#[allow(dead_code)]
147pub fn blend_wrinkle_maps(a: &WrinkleMap, b: &WrinkleMap, t: f32) -> WrinkleMap {
148 let t = t.clamp(0.0, 1.0);
149 let n = a.vertex_count.min(b.vertex_count);
150 let values: Vec<f32> = a.values[..n]
151 .iter()
152 .zip(b.values[..n].iter())
153 .map(|(av, bv)| av * (1.0 - t) + bv * t)
154 .collect();
155 WrinkleMap {
156 vertex_count: n,
157 values,
158 config: WrinkleConfig {
159 frequency: a.config.frequency,
160 amplitude: a.config.amplitude,
161 threshold: a.config.threshold,
162 blend_sharpness: a.config.blend_sharpness,
163 },
164 }
165}
166
167#[allow(dead_code)]
168pub fn wrinkle_to_normal_delta(
169 map: &WrinkleMap,
170 positions: &[[f32; 3]],
171 normals: &[[f32; 3]],
172) -> Vec<[f32; 3]> {
173 let n = map.vertex_count.min(positions.len()).min(normals.len());
174 (0..n)
175 .map(|i| {
176 let w = map.values[i] * map.config.amplitude;
177 let nrm = normals[i];
178 [nrm[0] * w, nrm[1] * w, nrm[2] * w]
179 })
180 .collect()
181}
182
183#[allow(dead_code)]
184pub fn threshold_wrinkle_map(map: &WrinkleMap, threshold: f32) -> Vec<bool> {
185 map.values.iter().map(|&v| v >= threshold).collect()
186}
187
188#[allow(dead_code)]
189pub fn wrinkle_region_average(map: &WrinkleMap, indices: &[usize]) -> f32 {
190 if indices.is_empty() {
191 return 0.0;
192 }
193 let valid: Vec<f32> = indices
194 .iter()
195 .filter(|&&i| i < map.vertex_count)
196 .map(|&i| map.values[i])
197 .collect();
198 if valid.is_empty() {
199 return 0.0;
200 }
201 valid.iter().sum::<f32>() / valid.len() as f32
202}
203
204#[allow(dead_code)]
205pub fn apply_wrinkle_weight(map: &mut WrinkleMap, weight: f32) {
206 for v in &mut map.values {
207 *v = (*v * weight).clamp(0.0, 1.0);
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn test_new_wrinkle_map() {
217 let cfg = default_wrinkle_config();
218 let map = new_wrinkle_map(10, cfg);
219 assert_eq!(map.vertex_count, 10);
220 assert_eq!(map.values.len(), 10);
221 assert!(map.values.iter().all(|&v| v == 0.0));
222 }
223
224 #[test]
225 fn test_default_wrinkle_config() {
226 let cfg = default_wrinkle_config();
227 assert!(cfg.frequency > 0.0);
228 assert!(cfg.amplitude > 0.0);
229 assert!(cfg.threshold >= 0.0);
230 assert!(cfg.blend_sharpness > 0.0);
231 }
232
233 #[test]
234 fn test_compute_wrinkle_from_deformation_no_deform() {
235 let positions = vec![[0.0f32; 3]; 5];
236 let cfg = default_wrinkle_config();
237 let map = compute_wrinkle_from_deformation(&positions, &positions, &cfg);
238 assert_eq!(map.vertex_count, 5);
239 assert!(map.values.iter().all(|&v| v == 0.0));
240 }
241
242 #[test]
243 fn test_compute_wrinkle_from_deformation_with_deform() {
244 let original = vec![[0.0f32; 3]; 3];
245 let deformed = vec![[1.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.5, 0.5, 0.0]];
246 let cfg = default_wrinkle_config();
247 let map = compute_wrinkle_from_deformation(&original, &deformed, &cfg);
248 assert!(map.values[0] > 0.0);
249 assert_eq!(map.values[1], 0.0);
250 assert!(map.values[2] > 0.0);
251 }
252
253 #[test]
254 fn test_normalize_flat() {
255 let cfg = default_wrinkle_config();
256 let mut map = new_wrinkle_map(3, cfg);
257 map.values = vec![0.2, 0.6, 1.0];
258 normalize_wrinkle_map(&mut map);
259 assert!((map.values[0]).abs() < 1e-5);
260 assert!((map.values[2] - 1.0).abs() < 1e-5);
261 }
262
263 #[test]
264 fn test_normalize_uniform() {
265 let cfg = default_wrinkle_config();
266 let mut map = new_wrinkle_map(3, cfg);
267 map.values = vec![0.5, 0.5, 0.5];
268 normalize_wrinkle_map(&mut map);
270 assert!(map.values.iter().all(|&v| (v - 0.5).abs() < 1e-5));
271 }
272
273 #[test]
274 fn test_blend_wrinkle_maps() {
275 let cfg_a = default_wrinkle_config();
276 let cfg_b = default_wrinkle_config();
277 let mut a = new_wrinkle_map(4, cfg_a);
278 let mut b = new_wrinkle_map(4, cfg_b);
279 a.values = vec![0.0; 4];
280 b.values = vec![1.0; 4];
281 let blended = blend_wrinkle_maps(&a, &b, 0.5);
282 assert!(blended.values.iter().all(|&v| (v - 0.5).abs() < 1e-5));
283 }
284
285 #[test]
286 fn test_blend_wrinkle_maps_clamp() {
287 let a = new_wrinkle_map(2, default_wrinkle_config());
288 let b = new_wrinkle_map(2, default_wrinkle_config());
289 let blended = blend_wrinkle_maps(&a, &b, 2.0);
290 assert_eq!(blended.vertex_count, 2);
291 }
292
293 #[test]
294 fn test_threshold_wrinkle_map() {
295 let cfg = default_wrinkle_config();
296 let mut map = new_wrinkle_map(4, cfg);
297 map.values = vec![0.1, 0.5, 0.3, 0.8];
298 let mask = threshold_wrinkle_map(&map, 0.4);
299 assert_eq!(mask, vec![false, true, false, true]);
300 }
301
302 #[test]
303 fn test_smooth_wrinkle_map_no_panic() {
304 let cfg = default_wrinkle_config();
305 let mut map = new_wrinkle_map(3, cfg);
306 map.values = vec![1.0, 0.0, 0.5];
307 let adjacency = vec![vec![1usize], vec![0usize, 2usize], vec![1usize]];
308 smooth_wrinkle_map(&mut map, &adjacency, 2);
309 assert_eq!(map.values.len(), 3);
310 }
311
312 #[test]
313 fn test_wrinkle_region_average() {
314 let cfg = default_wrinkle_config();
315 let mut map = new_wrinkle_map(5, cfg);
316 map.values = vec![0.2, 0.4, 0.6, 0.8, 1.0];
317 let avg = wrinkle_region_average(&map, &[0, 1, 2]);
318 assert!((avg - 0.4).abs() < 1e-5);
319 }
320
321 #[test]
322 fn test_wrinkle_region_average_empty() {
323 let map = new_wrinkle_map(3, default_wrinkle_config());
324 let avg = wrinkle_region_average(&map, &[]);
325 assert_eq!(avg, 0.0);
326 }
327
328 #[test]
329 fn test_wrinkle_map_min_max() {
330 let cfg = default_wrinkle_config();
331 let mut map = new_wrinkle_map(4, cfg);
332 map.values = vec![0.1, 0.5, 0.9, 0.3];
333 assert!((wrinkle_map_min(&map) - 0.1).abs() < 1e-5);
334 assert!((wrinkle_map_max(&map) - 0.9).abs() < 1e-5);
335 }
336
337 #[test]
338 fn test_apply_wrinkle_weight() {
339 let cfg = default_wrinkle_config();
340 let mut map = new_wrinkle_map(3, cfg);
341 map.values = vec![0.5, 1.0, 0.25];
342 apply_wrinkle_weight(&mut map, 0.5);
343 assert!((map.values[0] - 0.25).abs() < 1e-5);
344 assert!((map.values[1] - 0.5).abs() < 1e-5);
345 }
346
347 #[test]
348 fn test_wrinkle_to_normal_delta() {
349 let cfg = default_wrinkle_config();
350 let mut map = new_wrinkle_map(2, cfg);
351 map.values = vec![0.5, 1.0];
352 let positions = vec![[0.0f32; 3]; 2];
353 let normals = vec![[0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
354 let deltas = wrinkle_to_normal_delta(&map, &positions, &normals);
355 assert_eq!(deltas.len(), 2);
356 assert!((deltas[0][1] - 0.5).abs() < 1e-5);
357 assert!((deltas[1][2] - 1.0).abs() < 1e-5);
358 }
359
360 #[test]
361 fn test_add_procedural_wrinkles() {
362 let cfg = default_wrinkle_config();
363 let mut map = new_wrinkle_map(3, cfg);
364 let region = WrinkleRegion {
365 name: "test".to_string(),
366 vertex_indices: vec![0, 1, 2],
367 direction: [1.0, 0.0, 0.0],
368 intensity: 0.5,
369 };
370 let positions = vec![[0.0f32; 3]; 3];
371 add_procedural_wrinkles(&mut map, ®ion, &positions);
372 assert_eq!(map.values.len(), 3);
373 }
374}