1use std::collections::{HashMap, HashSet, VecDeque};
6
7use super::types::{
8 AtlasPatch, BooleanResult, FeatureEdge, MeshQualityStats, ProcessMesh, Quadric,
9 TriangleQuality, UvParameterization,
10};
11
12pub type Vertex = [f64; 3];
14pub type Face = [usize; 3];
16pub type Uv = [f64; 2];
18#[inline]
19pub(super) fn vec3_add(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
20 [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
21}
22#[inline]
23pub(super) fn vec3_sub(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
24 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
25}
26#[inline]
27pub(super) fn vec3_scale(a: [f64; 3], s: f64) -> [f64; 3] {
28 [a[0] * s, a[1] * s, a[2] * s]
29}
30#[inline]
31pub(super) fn vec3_dot(a: [f64; 3], b: [f64; 3]) -> f64 {
32 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
33}
34#[inline]
35pub(super) fn vec3_cross(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
36 [
37 a[1] * b[2] - a[2] * b[1],
38 a[2] * b[0] - a[0] * b[2],
39 a[0] * b[1] - a[1] * b[0],
40 ]
41}
42#[inline]
43pub(super) fn vec3_len(a: [f64; 3]) -> f64 {
44 (a[0] * a[0] + a[1] * a[1] + a[2] * a[2]).sqrt()
45}
46#[inline]
47pub(super) fn vec3_normalize(a: [f64; 3]) -> [f64; 3] {
48 let l = vec3_len(a);
49 if l < 1e-15 {
50 [0.0, 0.0, 0.0]
51 } else {
52 vec3_scale(a, 1.0 / l)
53 }
54}
55#[inline]
56pub(super) fn vec3_lerp(a: [f64; 3], b: [f64; 3], t: f64) -> [f64; 3] {
57 vec3_add(vec3_scale(a, 1.0 - t), vec3_scale(b, t))
58}
59pub fn laplacian_smooth(mesh: &ProcessMesh, lambda: f64, iters: usize) -> ProcessMesh {
63 let mut out = mesh.clone();
64 let adj = mesh.build_adjacency();
65 for _ in 0..iters {
66 let old = out.verts.clone();
67 for (i, nbrs) in adj.iter().enumerate() {
68 if nbrs.is_empty() {
69 continue;
70 }
71 let mut avg = [0.0_f64; 3];
72 for &j in nbrs {
73 avg = vec3_add(avg, old[j]);
74 }
75 avg = vec3_scale(avg, 1.0 / nbrs.len() as f64);
76 let delta = vec3_sub(avg, old[i]);
77 out.verts[i] = vec3_add(old[i], vec3_scale(delta, lambda));
78 }
79 }
80 out
81}
82pub fn taubin_smooth(mesh: &ProcessMesh, lambda: f64, mu: f64, iters: usize) -> ProcessMesh {
87 let mut out = mesh.clone();
88 let adj = mesh.build_adjacency();
89 let one_step = |verts: &[Vertex], step: f64, a: &[Vec<usize>]| -> Vec<Vertex> {
90 let mut next = verts.to_vec();
91 for (i, nbrs) in a.iter().enumerate() {
92 if nbrs.is_empty() {
93 continue;
94 }
95 let mut avg = [0.0_f64; 3];
96 for &j in nbrs {
97 avg = vec3_add(avg, verts[j]);
98 }
99 avg = vec3_scale(avg, 1.0 / nbrs.len() as f64);
100 let delta = vec3_sub(avg, verts[i]);
101 next[i] = vec3_add(verts[i], vec3_scale(delta, step));
102 }
103 next
104 };
105 for _ in 0..iters {
106 out.verts = one_step(&out.verts, lambda, &adj);
107 out.verts = one_step(&out.verts, mu, &adj);
108 }
109 out
110}
111pub fn midpoint_subdivide(mesh: &ProcessMesh) -> ProcessMesh {
114 let mut verts = mesh.verts.clone();
115 let mut faces = Vec::new();
116 let mut edge_mid: HashMap<(usize, usize), usize> = HashMap::new();
117 let mut get_mid = |a: usize, b: usize, v: &mut Vec<Vertex>| -> usize {
118 let key = if a < b { (a, b) } else { (b, a) };
119 *edge_mid.entry(key).or_insert_with(|| {
120 let mid = vec3_scale(vec3_add(v[a], v[b]), 0.5);
121 let idx = v.len();
122 v.push(mid);
123 idx
124 })
125 };
126 for &[a, b, c] in &mesh.faces {
127 let ab = get_mid(a, b, &mut verts);
128 let bc = get_mid(b, c, &mut verts);
129 let ca = get_mid(c, a, &mut verts);
130 faces.push([a, ab, ca]);
131 faces.push([ab, b, bc]);
132 faces.push([ca, bc, c]);
133 faces.push([ab, bc, ca]);
134 }
135 ProcessMesh::new(verts, faces)
136}
137pub fn loop_subdivide(mesh: &ProcessMesh) -> ProcessMesh {
141 let mut verts = mesh.verts.clone();
142 let mut edge_mid: HashMap<(usize, usize), usize> = HashMap::new();
143 let mut edge_opp: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
144 for &[a, b, c] in &mesh.faces {
145 let edges = [(a, b, c), (b, c, a), (c, a, b)];
146 for (i, j, k) in edges {
147 let key = if i < j { (i, j) } else { (j, i) };
148 edge_opp.entry(key).or_default().push(k);
149 }
150 }
151 let mut get_mid = |a: usize, b: usize, v: &mut Vec<Vertex>| -> usize {
152 let key = if a < b { (a, b) } else { (b, a) };
153 *edge_mid.entry(key).or_insert_with(|| {
154 let opps = edge_opp.get(&key).cloned().unwrap_or_default();
155 let pos = if opps.len() == 2 {
156 let sum_ab = vec3_scale(vec3_add(v[a], v[b]), 3.0 / 8.0);
157 let sum_op = vec3_scale(vec3_add(v[opps[0]], v[opps[1]]), 1.0 / 8.0);
158 vec3_add(sum_ab, sum_op)
159 } else {
160 vec3_scale(vec3_add(v[a], v[b]), 0.5)
161 };
162 let idx = v.len();
163 v.push(pos);
164 idx
165 })
166 };
167 let adj = mesh.build_adjacency();
168 let orig = mesh.verts.clone();
169 let n = orig.len();
170 let mut new_pos = orig.clone();
171 for i in 0..n {
172 let nbrs = &adj[i];
173 let k = nbrs.len() as f64;
174 let beta = if k <= 3.0 {
175 3.0 / 16.0
176 } else {
177 3.0 / (8.0 * k)
178 };
179 let mut sum = [0.0_f64; 3];
180 for &j in nbrs {
181 sum = vec3_add(sum, orig[j]);
182 }
183 new_pos[i] = vec3_add(vec3_scale(orig[i], 1.0 - k * beta), vec3_scale(sum, beta));
184 }
185 let mut faces = Vec::new();
186 for &[a, b, c] in &mesh.faces {
187 let ab = get_mid(a, b, &mut verts);
188 let bc = get_mid(b, c, &mut verts);
189 let ca = get_mid(c, a, &mut verts);
190 faces.push([a, ab, ca]);
191 faces.push([ab, b, bc]);
192 faces.push([ca, bc, c]);
193 faces.push([ab, bc, ca]);
194 }
195 verts[..n].copy_from_slice(&new_pos[..n]);
196 ProcessMesh::new(verts, faces)
197}
198pub fn catmull_clark_subdivide(mesh: &ProcessMesh) -> ProcessMesh {
203 let nv = mesh.verts.len();
204 let mut new_verts = mesh.verts.clone();
205 let mut face_pts: Vec<Vertex> = Vec::with_capacity(mesh.faces.len());
206 for &[a, b, c] in &mesh.faces {
207 let fp = vec3_scale(
208 vec3_add(vec3_add(mesh.verts[a], mesh.verts[b]), mesh.verts[c]),
209 1.0 / 3.0,
210 );
211 face_pts.push(fp);
212 }
213 let mut edge_map: HashMap<(usize, usize), usize> = HashMap::new();
214 let mut edge_pts: Vec<Vertex> = Vec::new();
215 let mut vert_face_avg = vec![[0.0_f64; 3]; nv];
216 let mut vert_face_cnt = vec![0usize; nv];
217 for (fi, &[a, b, c]) in mesh.faces.iter().enumerate() {
218 let fp = face_pts[fi];
219 for &v in &[a, b, c] {
220 vert_face_avg[v] = vec3_add(vert_face_avg[v], fp);
221 vert_face_cnt[v] += 1;
222 }
223 for (i, j) in [(a, b), (b, c), (c, a)] {
224 let key = if i < j { (i, j) } else { (j, i) };
225 edge_map.entry(key).or_insert_with(|| {
226 let ep = vec3_scale(vec3_add(mesh.verts[i], mesh.verts[j]), 0.5);
227 let idx = edge_pts.len();
228 edge_pts.push(ep);
229 idx
230 });
231 }
232 }
233 let adj = mesh.build_adjacency();
234 for i in 0..nv {
235 let n = vert_face_cnt[i] as f64;
236 if n == 0.0 {
237 continue;
238 }
239 let f_avg = vec3_scale(vert_face_avg[i], 1.0 / n);
240 let nbrs = &adj[i];
241 let mut e_avg = [0.0_f64; 3];
242 for &j in nbrs {
243 e_avg = vec3_add(e_avg, mesh.verts[j]);
244 }
245 let m = nbrs.len() as f64;
246 e_avg = vec3_scale(e_avg, 1.0 / m.max(1.0));
247 new_verts[i] = vec3_scale(
248 vec3_add(
249 vec3_add(f_avg, vec3_scale(e_avg, 2.0)),
250 vec3_scale(mesh.verts[i], (n - 3.0).max(0.0)),
251 ),
252 1.0 / n,
253 );
254 }
255 let ep_offset = nv;
256 let fp_offset = ep_offset + edge_pts.len();
257 for ep in &edge_pts {
258 new_verts.push(*ep);
259 }
260 for fp in &face_pts {
261 new_verts.push(*fp);
262 }
263 let mut faces = Vec::new();
264 for (fi, &[a, b, c]) in mesh.faces.iter().enumerate() {
265 let fp_idx = fp_offset + fi;
266 let ab_idx = ep_offset
267 + *edge_map
268 .get(&if a < b { (a, b) } else { (b, a) })
269 .expect("key must exist");
270 let bc_idx = ep_offset
271 + *edge_map
272 .get(&if b < c { (b, c) } else { (c, b) })
273 .expect("key must exist");
274 let ca_idx = ep_offset
275 + *edge_map
276 .get(&if c < a { (c, a) } else { (a, c) })
277 .expect("key must exist");
278 faces.push([a, ab_idx, fp_idx]);
279 faces.push([ab_idx, b, fp_idx]);
280 faces.push([b, bc_idx, fp_idx]);
281 faces.push([bc_idx, c, fp_idx]);
282 faces.push([c, ca_idx, fp_idx]);
283 faces.push([ca_idx, a, fp_idx]);
284 }
285 ProcessMesh::new(new_verts, faces)
286}
287pub(super) fn face_quadrics(mesh: &ProcessMesh) -> Vec<Quadric> {
289 mesh.faces
290 .iter()
291 .map(|&[a, b, c]| {
292 let va = mesh.verts[a];
293 let vb = mesh.verts[b];
294 let vc = mesh.verts[c];
295 let ab = vec3_sub(vb, va);
296 let ac = vec3_sub(vc, va);
297 let n = vec3_normalize(vec3_cross(ab, ac));
298 let d = -vec3_dot(n, va);
299 Quadric::from_plane(n[0], n[1], n[2], d)
300 })
301 .collect()
302}
303pub(super) fn vertex_quadrics(mesh: &ProcessMesh) -> Vec<Quadric> {
305 let nv = mesh.verts.len();
306 let fq = face_quadrics(mesh);
307 let mut vq = vec![Quadric::zero(); nv];
308 for (fi, &[a, b, c]) in mesh.faces.iter().enumerate() {
309 for &v in &[a, b, c] {
310 vq[v] = vq[v].add(&fq[fi]);
311 }
312 }
313 vq
314}
315pub fn qem_decimate(mesh: &ProcessMesh, target_faces: usize) -> ProcessMesh {
318 if mesh.faces.len() <= target_faces {
319 return mesh.clone();
320 }
321 let nv = mesh.verts.len();
322 let mut verts = mesh.verts.clone();
323 let mut faces = mesh.faces.clone();
324 let mut vq = vertex_quadrics(mesh);
325 let mut removed_verts = vec![false; nv];
326 let mut removed_faces = vec![false; faces.len()];
327 while faces.iter().filter(|&&_f| true).count() - removed_faces.iter().filter(|&&r| r).count()
328 > target_faces
329 {
330 let mut best_cost = f64::INFINITY;
331 let mut best_edge: Option<(usize, usize)> = None;
332 let mut best_pos = [0.0_f64; 3];
333 let mut seen = HashSet::new();
334 for &[a, b, c] in &faces {
335 for (i, j) in [(a, b), (b, c), (c, a)] {
336 let key = if i < j { (i, j) } else { (j, i) };
337 if seen.contains(&key) {
338 continue;
339 }
340 seen.insert(key);
341 if removed_verts[i] || removed_verts[j] {
342 continue;
343 }
344 let q = vq[i].add(&vq[j]);
345 let mid = vec3_scale(vec3_add(verts[i], verts[j]), 0.5);
346 let cost = q.evaluate(mid);
347 if cost < best_cost {
348 best_cost = cost;
349 best_edge = Some((i, j));
350 best_pos = mid;
351 }
352 }
353 }
354 if let Some((vi, vj)) = best_edge {
355 verts[vi] = best_pos;
356 vq[vi] = vq[vi].add(&vq[vj]);
357 removed_verts[vj] = true;
358 for (fi, f) in faces.iter_mut().enumerate() {
359 if removed_faces[fi] {
360 continue;
361 }
362 for idx in f.iter_mut() {
363 if *idx == vj {
364 *idx = vi;
365 }
366 }
367 if f[0] == f[1] || f[1] == f[2] || f[0] == f[2] {
368 removed_faces[fi] = true;
369 }
370 }
371 } else {
372 break;
373 }
374 }
375 let remap: Vec<usize> = {
376 let mut m = vec![0usize; nv];
377 let mut cnt = 0;
378 for i in 0..nv {
379 if !removed_verts[i] {
380 m[i] = cnt;
381 cnt += 1;
382 }
383 }
384 m
385 };
386 let new_verts: Vec<Vertex> = (0..nv)
387 .filter(|&i| !removed_verts[i])
388 .map(|i| verts[i])
389 .collect();
390 let new_faces: Vec<Face> = faces
391 .iter()
392 .enumerate()
393 .filter(|&(fi, _)| !removed_faces[fi])
394 .map(|(_, &[a, b, c])| [remap[a], remap[b], remap[c]])
395 .collect();
396 ProcessMesh::new(new_verts, new_faces)
397}
398pub fn triangle_quality(va: Vertex, vb: Vertex, vc: Vertex) -> TriangleQuality {
400 let ab = vec3_sub(vb, va);
401 let ac = vec3_sub(vc, va);
402 let bc = vec3_sub(vc, vb);
403 let la = vec3_len(bc);
404 let lb = vec3_len(ac);
405 let lc = vec3_len(ab);
406 let s = (la + lb + lc) / 2.0;
407 let area2 = vec3_len(vec3_cross(ab, ac));
408 let area = area2 / 2.0;
409 let inradius = if s > 1e-15 { area / s } else { 0.0 };
410 let circumradius = if area > 1e-15 {
411 la * lb * lc / (4.0 * area)
412 } else {
413 f64::INFINITY
414 };
415 let aspect_ratio = if inradius > 1e-15 {
416 circumradius / (2.0 * inradius)
417 } else {
418 f64::INFINITY
419 };
420 let angle_a = if la > 1e-15 && lb > 1e-15 {
421 let cos_a = (lb * lb + lc * lc - la * la) / (2.0 * lb * lc);
422 cos_a.clamp(-1.0, 1.0).acos()
423 } else {
424 0.0
425 };
426 let angle_b = if la > 1e-15 && lc > 1e-15 {
427 let cos_b = (la * la + lc * lc - lb * lb) / (2.0 * la * lc);
428 cos_b.clamp(-1.0, 1.0).acos()
429 } else {
430 0.0
431 };
432 let angle_c = std::f64::consts::PI - angle_a - angle_b;
433 let perim = 2.0 * s;
434 let ideal_area = perim * perim * (3.0_f64.sqrt()) / 36.0;
435 let area_quality = if ideal_area > 1e-15 {
436 (area / ideal_area).min(1.0)
437 } else {
438 0.0
439 };
440 TriangleQuality {
441 aspect_ratio,
442 min_angle: angle_a.min(angle_b).min(angle_c),
443 max_angle: angle_a.max(angle_b).max(angle_c),
444 area_quality,
445 }
446}
447pub fn mesh_quality_stats(mesh: &ProcessMesh) -> MeshQualityStats {
449 if mesh.faces.is_empty() {
450 return MeshQualityStats {
451 mean_aspect_ratio: 0.0,
452 max_aspect_ratio: 0.0,
453 min_angle_deg: 0.0,
454 mean_area_quality: 0.0,
455 };
456 }
457 let n = mesh.faces.len() as f64;
458 let mut sum_ar = 0.0;
459 let mut max_ar = 0.0_f64;
460 let mut min_ang = f64::INFINITY;
461 let mut sum_aq = 0.0;
462 for &[a, b, c] in &mesh.faces {
463 let q = triangle_quality(mesh.verts[a], mesh.verts[b], mesh.verts[c]);
464 sum_ar += q.aspect_ratio;
465 max_ar = max_ar.max(q.aspect_ratio);
466 min_ang = min_ang.min(q.min_angle);
467 sum_aq += q.area_quality;
468 }
469 MeshQualityStats {
470 mean_aspect_ratio: sum_ar / n,
471 max_aspect_ratio: max_ar,
472 min_angle_deg: min_ang.to_degrees(),
473 mean_area_quality: sum_aq / n,
474 }
475}
476pub fn detect_boundary_loops(mesh: &ProcessMesh) -> Vec<Vec<usize>> {
481 let mut edge_count: HashMap<(usize, usize), usize> = HashMap::new();
482 for &[a, b, c] in &mesh.faces {
483 for (i, j) in [(a, b), (b, c), (c, a)] {
484 *edge_count.entry((i, j)).or_insert(0) += 1;
485 }
486 }
487 let mut boundary_adj: HashMap<usize, usize> = HashMap::new();
488 for &(i, j) in edge_count.keys() {
489 if edge_count.get(&(j, i)).copied().unwrap_or(0) == 0 {
490 boundary_adj.insert(i, j);
491 }
492 }
493 let mut visited = HashSet::new();
494 let mut loops = Vec::new();
495 for &start in boundary_adj.keys() {
496 if visited.contains(&start) {
497 continue;
498 }
499 let mut loop_ = Vec::new();
500 let mut cur = start;
501 loop {
502 if visited.contains(&cur) {
503 break;
504 }
505 visited.insert(cur);
506 loop_.push(cur);
507 match boundary_adj.get(&cur) {
508 Some(&next) => cur = next,
509 None => break,
510 }
511 }
512 if !loop_.is_empty() {
513 loops.push(loop_);
514 }
515 }
516 loops
517}
518pub fn fill_hole_fan(verts: &mut Vec<Vertex>, loop_: &[usize]) -> Vec<Face> {
522 if loop_.len() < 3 {
523 return Vec::new();
524 }
525 let mut centroid = [0.0_f64; 3];
526 for &vi in loop_ {
527 centroid = vec3_add(centroid, verts[vi]);
528 }
529 centroid = vec3_scale(centroid, 1.0 / loop_.len() as f64);
530 let center_idx = verts.len();
531 verts.push(centroid);
532 let mut new_faces = Vec::new();
533 let n = loop_.len();
534 for i in 0..n {
535 new_faces.push([loop_[(i + 1) % n], loop_[i], center_idx]);
536 }
537 new_faces
538}
539pub fn fill_all_holes(mesh: &ProcessMesh) -> ProcessMesh {
541 let mut out = mesh.clone();
542 loop {
543 let loops = detect_boundary_loops(&out);
544 if loops.is_empty() {
545 break;
546 }
547 for lp in &loops {
548 let new_faces = fill_hole_fan(&mut out.verts, lp);
549 out.faces.extend(new_faces);
550 }
551 if detect_boundary_loops(&out).len() >= loops.len() {
552 break;
553 }
554 }
555 out
556}
557pub fn fix_non_manifold_vertices(mesh: &ProcessMesh) -> ProcessMesh {
560 let nv = mesh.verts.len();
561 let mut vert_faces: Vec<Vec<usize>> = vec![Vec::new(); nv];
562 for (fi, &[a, b, c]) in mesh.faces.iter().enumerate() {
563 vert_faces[a].push(fi);
564 vert_faces[b].push(fi);
565 vert_faces[c].push(fi);
566 }
567 let mut new_verts = mesh.verts.clone();
568 let mut new_faces = mesh.faces.clone();
569 for vi in 0..nv {
570 let flist = &vert_faces[vi];
571 if flist.len() < 2 {
572 continue;
573 }
574 let face_set: HashSet<usize> = flist.iter().cloned().collect();
575 let mut visited = HashSet::new();
576 let mut queue = VecDeque::new();
577 queue.push_back(flist[0]);
578 while let Some(fi) = queue.pop_front() {
579 if visited.contains(&fi) {
580 continue;
581 }
582 visited.insert(fi);
583 let [a, b, c] = new_faces[fi];
584 for &fj in &vert_faces[a] {
585 if face_set.contains(&fj) && !visited.contains(&fj) {
586 queue.push_back(fj);
587 }
588 }
589 for &fj in &vert_faces[b] {
590 if face_set.contains(&fj) && !visited.contains(&fj) {
591 queue.push_back(fj);
592 }
593 }
594 for &fj in &vert_faces[c] {
595 if face_set.contains(&fj) && !visited.contains(&fj) {
596 queue.push_back(fj);
597 }
598 }
599 }
600 let extra: Vec<usize> = flist
601 .iter()
602 .filter(|&&f| !visited.contains(&f))
603 .cloned()
604 .collect();
605 if !extra.is_empty() {
606 let new_idx = new_verts.len();
607 new_verts.push(new_verts[vi]);
608 for fi in extra {
609 for idx in new_faces[fi].iter_mut() {
610 if *idx == vi {
611 *idx = new_idx;
612 }
613 }
614 }
615 }
616 }
617 ProcessMesh::new(new_verts, new_faces)
618}
619pub fn detect_feature_edges(mesh: &ProcessMesh, threshold_deg: f64) -> Vec<FeatureEdge> {
622 let thresh = threshold_deg.to_radians();
623 let face_normals: Vec<[f64; 3]> = mesh
624 .faces
625 .iter()
626 .map(|&[a, b, c]| {
627 let ab = vec3_sub(mesh.verts[b], mesh.verts[a]);
628 let ac = vec3_sub(mesh.verts[c], mesh.verts[a]);
629 vec3_normalize(vec3_cross(ab, ac))
630 })
631 .collect();
632 let mut edge_faces: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
633 for (fi, &[a, b, c]) in mesh.faces.iter().enumerate() {
634 for (i, j) in [(a, b), (b, c), (c, a)] {
635 let key = if i < j { (i, j) } else { (j, i) };
636 edge_faces.entry(key).or_default().push(fi);
637 }
638 }
639 let mut features = Vec::new();
640 for ((v0, v1), flist) in &edge_faces {
641 if flist.len() != 2 {
642 continue;
643 }
644 let n0 = face_normals[flist[0]];
645 let n1 = face_normals[flist[1]];
646 let cos_a = vec3_dot(n0, n1).clamp(-1.0, 1.0);
647 let angle = cos_a.acos();
648 if angle > thresh {
649 features.push(FeatureEdge {
650 v0: *v0,
651 v1: *v1,
652 dihedral_angle: angle,
653 });
654 }
655 }
656 features
657}
658pub fn isotropic_remesh(mesh: &ProcessMesh, target_len: f64, iters: usize) -> ProcessMesh {
663 let mut out = mesh.clone();
664 for _ in 0..iters {
665 out = split_long_edges(&out, target_len * 4.0 / 3.0);
666 out = collapse_short_edges(&out, target_len * 4.0 / 5.0);
667 out = laplacian_smooth(&out, 0.5, 1);
668 }
669 out
670}
671pub fn split_long_edges(mesh: &ProcessMesh, max_len: f64) -> ProcessMesh {
673 let mut verts = mesh.verts.clone();
674 let mut faces = Vec::new();
675 let mut edge_mid: HashMap<(usize, usize), usize> = HashMap::new();
676 let mut get_mid = |a: usize, b: usize, v: &mut Vec<Vertex>| -> Option<usize> {
677 let d = vec3_len(vec3_sub(v[b], v[a]));
678 if d > max_len {
679 let key = if a < b { (a, b) } else { (b, a) };
680 let idx = *edge_mid.entry(key).or_insert_with(|| {
681 let mid = vec3_scale(vec3_add(v[a], v[b]), 0.5);
682 let i = v.len();
683 v.push(mid);
684 i
685 });
686 Some(idx)
687 } else {
688 None
689 }
690 };
691 for &[a, b, c] in &mesh.faces {
692 let ab = get_mid(a, b, &mut verts);
693 let bc = get_mid(b, c, &mut verts);
694 let ca = get_mid(c, a, &mut verts);
695 match (ab, bc, ca) {
696 (None, None, None) => faces.push([a, b, c]),
697 (Some(m), None, None) => {
698 faces.push([a, m, c]);
699 faces.push([m, b, c]);
700 }
701 (None, Some(m), None) => {
702 faces.push([a, b, m]);
703 faces.push([a, m, c]);
704 }
705 (None, None, Some(m)) => {
706 faces.push([a, b, m]);
707 faces.push([m, b, c]);
708 }
709 (Some(ab), Some(bc), None) => {
710 faces.push([a, ab, c]);
711 faces.push([ab, b, bc]);
712 faces.push([ab, bc, c]);
713 }
714 (Some(ab), None, Some(ca)) => {
715 faces.push([a, ab, ca]);
716 faces.push([ab, b, c]);
717 faces.push([ab, c, ca]);
718 }
719 (None, Some(bc), Some(ca)) => {
720 faces.push([a, b, bc]);
721 faces.push([a, bc, ca]);
722 faces.push([ca, bc, c]);
723 }
724 (Some(ab), Some(bc), Some(ca)) => {
725 faces.push([a, ab, ca]);
726 faces.push([ab, b, bc]);
727 faces.push([ca, bc, c]);
728 faces.push([ab, bc, ca]);
729 }
730 }
731 }
732 ProcessMesh::new(verts, faces)
733}
734pub fn collapse_short_edges(mesh: &ProcessMesh, min_len: f64) -> ProcessMesh {
736 let nv = mesh.verts.len();
737 let mut verts = mesh.verts.clone();
738 let mut faces = mesh.faces.clone();
739 let mut remap: Vec<usize> = (0..nv).collect();
740 fn root(r: &mut Vec<usize>, i: usize) -> usize {
741 if r[i] == i {
742 i
743 } else {
744 let p = root(r, r[i]);
745 r[i] = p;
746 p
747 }
748 }
749 let mut collapsed = true;
750 while collapsed {
751 collapsed = false;
752 let mut seen = HashSet::new();
753 for &[a, b, c] in &faces {
754 for (i, j) in [(a, b), (b, c), (c, a)] {
755 let ri = root(&mut remap, i);
756 let rj = root(&mut remap, j);
757 if ri == rj {
758 continue;
759 }
760 let key = if ri < rj { (ri, rj) } else { (rj, ri) };
761 if seen.contains(&key) {
762 continue;
763 }
764 seen.insert(key);
765 let d = vec3_len(vec3_sub(verts[rj], verts[ri]));
766 if d < min_len {
767 verts[ri] = vec3_scale(vec3_add(verts[ri], verts[rj]), 0.5);
768 remap[rj] = ri;
769 collapsed = true;
770 }
771 }
772 }
773 for i in 0..nv {
774 let r = root(&mut remap, i);
775 remap[i] = r;
776 }
777 for f in &mut faces {
778 for idx in f.iter_mut() {
779 *idx = remap[*idx];
780 }
781 }
782 faces.retain(|&[a, b, c]| a != b && b != c && a != c);
783 }
784 let active: Vec<bool> = (0..nv).map(|i| remap[i] == i).collect();
785 let new_idx: Vec<usize> = {
786 let mut m = vec![0usize; nv];
787 let mut cnt = 0;
788 for i in 0..nv {
789 if active[i] {
790 m[i] = cnt;
791 cnt += 1;
792 }
793 }
794 m
795 };
796 let new_verts: Vec<Vertex> = (0..nv).filter(|&i| active[i]).map(|i| verts[i]).collect();
797 let new_faces: Vec<Face> = faces
798 .iter()
799 .map(|&[a, b, c]| {
800 let ra = root(&mut remap, a);
801 let rb = root(&mut remap, b);
802 let rc = root(&mut remap, c);
803 [new_idx[ra], new_idx[rb], new_idx[rc]]
804 })
805 .collect();
806 ProcessMesh::new(new_verts, new_faces)
807}
808pub fn tutte_parameterize(mesh: &ProcessMesh, iters: usize) -> UvParameterization {
812 let nv = mesh.verts.len();
813 let loops = detect_boundary_loops(mesh);
814 let boundary: Vec<usize> = loops.into_iter().next().unwrap_or_default();
815 let nb = boundary.len();
816 let mut uv = vec![[0.0_f64; 2]; nv];
817 for (k, &vi) in boundary.iter().enumerate() {
818 let angle = 2.0 * std::f64::consts::PI * k as f64 / nb as f64;
819 uv[vi] = [0.5 + 0.5 * angle.cos(), 0.5 + 0.5 * angle.sin()];
820 }
821 let boundary_set: HashSet<usize> = boundary.iter().cloned().collect();
822 let adj = mesh.build_adjacency();
823 for _ in 0..iters {
824 let old = uv.clone();
825 for i in 0..nv {
826 if boundary_set.contains(&i) {
827 continue;
828 }
829 let nbrs = &adj[i];
830 if nbrs.is_empty() {
831 continue;
832 }
833 let mut sum = [0.0_f64; 2];
834 for &j in nbrs {
835 sum[0] += old[j][0];
836 sum[1] += old[j][1];
837 }
838 let n = nbrs.len() as f64;
839 uv[i] = [sum[0] / n, sum[1] / n];
840 }
841 }
842 let mut out = mesh.clone();
843 out.uvs = Some(uv.clone());
844 UvParameterization { mesh: out, uvs: uv }
845}
846pub fn lscm_parameterize(mesh: &ProcessMesh) -> UvParameterization {
851 tutte_parameterize(mesh, 200)
852}
853pub fn generate_texture_atlas(mesh: &ProcessMesh, atlas_size: usize) -> Vec<AtlasPatch> {
856 let _ = atlas_size;
857 let param = tutte_parameterize(mesh, 100);
858 let uvs = param.uvs.clone();
859 let bounds = uvs.iter().fold(
860 [
861 f64::INFINITY,
862 f64::INFINITY,
863 f64::NEG_INFINITY,
864 f64::NEG_INFINITY,
865 ],
866 |mut b, &[u, v]| {
867 if u < b[0] {
868 b[0] = u;
869 }
870 if v < b[1] {
871 b[1] = v;
872 }
873 if u > b[2] {
874 b[2] = u;
875 }
876 if v > b[3] {
877 b[3] = v;
878 }
879 b
880 },
881 );
882 vec![AtlasPatch {
883 group_id: 0,
884 bounds,
885 uvs,
886 }]
887}
888pub fn point_in_mesh(p: [f64; 3], mesh: &ProcessMesh) -> bool {
890 let dir = vec3_normalize([1.0, 1e-4, 1e-4]);
891 let mut crossings = 0usize;
892 for &[a, b, c] in &mesh.faces {
893 if let Some(_t) =
894 ray_triangle_intersect(p, dir, mesh.verts[a], mesh.verts[b], mesh.verts[c])
895 {
896 crossings += 1;
897 }
898 }
899 crossings % 2 == 1
900}
901pub(super) fn ray_triangle_intersect(
903 orig: [f64; 3],
904 dir: [f64; 3],
905 v0: [f64; 3],
906 v1: [f64; 3],
907 v2: [f64; 3],
908) -> Option<f64> {
909 let e1 = vec3_sub(v1, v0);
910 let e2 = vec3_sub(v2, v0);
911 let h = vec3_cross(dir, e2);
912 let a = vec3_dot(e1, h);
913 if a.abs() < 1e-15 {
914 return None;
915 }
916 let f = 1.0 / a;
917 let s = vec3_sub(orig, v0);
918 let u = f * vec3_dot(s, h);
919 if !(0.0..=1.0).contains(&u) {
920 return None;
921 }
922 let q = vec3_cross(s, e1);
923 let v = f * vec3_dot(dir, q);
924 if v < 0.0 || u + v > 1.0 {
925 return None;
926 }
927 let t = f * vec3_dot(e2, q);
928 if t > 1e-15 { Some(t) } else { None }
929}
930pub fn mesh_union(a: &ProcessMesh, b: &ProcessMesh) -> BooleanResult {
935 let offset_b = a.verts.len();
936 let mut verts = a.verts.clone();
937 verts.extend_from_slice(&b.verts);
938 let faces_a: Vec<Face> = a
939 .faces
940 .iter()
941 .filter(|&&[i, j, k]| {
942 let centroid = vec3_scale(
943 vec3_add(vec3_add(a.verts[i], a.verts[j]), a.verts[k]),
944 1.0 / 3.0,
945 );
946 !point_in_mesh(centroid, b)
947 })
948 .cloned()
949 .collect();
950 let faces_b: Vec<Face> = b
951 .faces
952 .iter()
953 .filter(|&&[i, j, k]| {
954 let centroid = vec3_scale(
955 vec3_add(vec3_add(b.verts[i], b.verts[j]), b.verts[k]),
956 1.0 / 3.0,
957 );
958 !point_in_mesh(centroid, a)
959 })
960 .map(|&[i, j, k]| [i + offset_b, j + offset_b, k + offset_b])
961 .collect();
962 let mut faces = faces_a;
963 faces.extend(faces_b);
964 BooleanResult {
965 mesh: ProcessMesh::new(verts, faces),
966 is_exact: false,
967 }
968}