1#![allow(clippy::needless_range_loop)]
2#![allow(dead_code)]
17
18use std::collections::HashMap;
19
20#[inline]
25fn add3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
26 [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
27}
28
29#[inline]
30fn sub3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
31 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
32}
33
34#[inline]
35fn scale3(a: [f64; 3], s: f64) -> [f64; 3] {
36 [a[0] * s, a[1] * s, a[2] * s]
37}
38
39#[inline]
40fn dot3(a: [f64; 3], b: [f64; 3]) -> f64 {
41 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
42}
43
44#[inline]
45fn cross3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
46 [
47 a[1] * b[2] - a[2] * b[1],
48 a[2] * b[0] - a[0] * b[2],
49 a[0] * b[1] - a[1] * b[0],
50 ]
51}
52
53#[inline]
54fn len3(a: [f64; 3]) -> f64 {
55 dot3(a, a).sqrt()
56}
57
58#[inline]
59fn midpoint3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
60 scale3(add3(a, b), 0.5)
61}
62
63#[derive(Debug, Clone)]
69pub struct SubdivMesh {
70 pub vertices: Vec<[f64; 3]>,
72 pub faces: Vec<[usize; 3]>,
74}
75
76impl SubdivMesh {
77 pub fn new(vertices: Vec<[f64; 3]>, faces: Vec<[usize; 3]>) -> Self {
79 Self { vertices, faces }
80 }
81
82 pub fn n_vertices(&self) -> usize {
84 self.vertices.len()
85 }
86
87 pub fn n_faces(&self) -> usize {
89 self.faces.len()
90 }
91
92 pub fn vertex_neighbours(&self) -> Vec<Vec<usize>> {
94 let n = self.vertices.len();
95 let mut nbrs: Vec<std::collections::HashSet<usize>> =
96 vec![std::collections::HashSet::new(); n];
97 for face in &self.faces {
98 let [a, b, c] = *face;
99 nbrs[a].insert(b);
100 nbrs[a].insert(c);
101 nbrs[b].insert(a);
102 nbrs[b].insert(c);
103 nbrs[c].insert(a);
104 nbrs[c].insert(b);
105 }
106 nbrs.into_iter().map(|s| s.into_iter().collect()).collect()
107 }
108
109 pub fn boundary_vertices(&self) -> Vec<bool> {
111 let n = self.vertices.len();
112 let mut edge_count: HashMap<(usize, usize), usize> = HashMap::new();
113 for face in &self.faces {
114 let [a, b, c] = *face;
115 for (u, v) in [(a, b), (b, c), (c, a)] {
116 let key = (u.min(v), u.max(v));
117 *edge_count.entry(key).or_insert(0) += 1;
118 }
119 }
120 let mut is_boundary = vec![false; n];
121 for ((u, v), count) in &edge_count {
122 if *count == 1 {
123 is_boundary[*u] = true;
124 is_boundary[*v] = true;
125 }
126 }
127 is_boundary
128 }
129}
130
131pub fn midpoint_subdivision(mesh: &SubdivMesh) -> SubdivMesh {
142 let mut new_verts = mesh.vertices.clone();
143 let mut edge_mid: HashMap<(usize, usize), usize> = HashMap::new();
145
146 let get_or_insert_mid = |a: usize,
147 b: usize,
148 verts: &mut Vec<[f64; 3]>,
149 edge_mid: &mut HashMap<(usize, usize), usize>|
150 -> usize {
151 let key = (a.min(b), a.max(b));
152 if let Some(&idx) = edge_mid.get(&key) {
153 return idx;
154 }
155 let mid = midpoint3(verts[a], verts[b]);
156 let idx = verts.len();
157 verts.push(mid);
158 edge_mid.insert(key, idx);
159 idx
160 };
161
162 let mut new_faces = Vec::with_capacity(mesh.faces.len() * 4);
163 for face in &mesh.faces {
164 let [a, b, c] = *face;
165 let ab = get_or_insert_mid(a, b, &mut new_verts, &mut edge_mid);
166 let bc = get_or_insert_mid(b, c, &mut new_verts, &mut edge_mid);
167 let ca = get_or_insert_mid(c, a, &mut new_verts, &mut edge_mid);
168 new_faces.push([a, ab, ca]);
169 new_faces.push([ab, b, bc]);
170 new_faces.push([ca, bc, c]);
171 new_faces.push([ab, bc, ca]);
172 }
173 SubdivMesh::new(new_verts, new_faces)
174}
175
176pub fn loop_subdivision(mesh: &SubdivMesh) -> SubdivMesh {
189 let nv = mesh.vertices.len();
190 let is_boundary = mesh.boundary_vertices();
191
192 let mut edge_opposite: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
196 for face in &mesh.faces {
197 let [a, b, c] = *face;
198 edge_opposite.entry((a, b)).or_default().push(c);
199 edge_opposite.entry((b, c)).or_default().push(a);
200 edge_opposite.entry((c, a)).or_default().push(b);
201 }
202
203 let mut edge_mid: HashMap<(usize, usize), usize> = HashMap::new();
205 let mut new_verts = mesh.vertices.clone();
206
207 let mut compute_edge_point = |a: usize, b: usize, verts: &mut Vec<[f64; 3]>| -> usize {
208 let key = (a.min(b), a.max(b));
209 if let Some(&idx) = edge_mid.get(&key) {
210 return idx;
211 }
212 let opp_ab: Vec<usize> = edge_opposite.get(&(a, b)).cloned().unwrap_or_default();
214 let opp_ba: Vec<usize> = edge_opposite.get(&(b, a)).cloned().unwrap_or_default();
215 let all_opp: Vec<usize> = opp_ab.iter().chain(opp_ba.iter()).copied().collect();
216
217 let ep = if all_opp.len() == 2 && !is_boundary[a] && !is_boundary[b] {
218 let pa = verts[a];
220 let pb = verts[b];
221 let pc = verts[all_opp[0]];
222 let pd = verts[all_opp[1]];
223 add3(
224 scale3(add3(pa, pb), 3.0 / 8.0),
225 scale3(add3(pc, pd), 1.0 / 8.0),
226 )
227 } else {
228 midpoint3(verts[a], verts[b])
230 };
231 let idx = verts.len();
232 verts.push(ep);
233 edge_mid.insert(key, idx);
234 idx
235 };
236
237 let mut new_faces = Vec::with_capacity(mesh.faces.len() * 4);
239 for face in &mesh.faces {
240 let [a, b, c] = *face;
241 let ab = compute_edge_point(a, b, &mut new_verts);
242 let bc = compute_edge_point(b, c, &mut new_verts);
243 let ca = compute_edge_point(c, a, &mut new_verts);
244 new_faces.push([a, ab, ca]);
245 new_faces.push([ab, b, bc]);
246 new_faces.push([ca, bc, c]);
247 new_faces.push([ab, bc, ca]);
248 }
249
250 let nbrs = mesh.vertex_neighbours();
252 for v in 0..nv {
253 if is_boundary[v] {
254 let bnd_nbrs: Vec<usize> = nbrs[v]
256 .iter()
257 .copied()
258 .filter(|&u| is_boundary[u])
259 .collect();
260 if bnd_nbrs.len() >= 2 {
261 let avg = scale3(
262 bnd_nbrs
263 .iter()
264 .fold([0.0; 3], |acc, &u| add3(acc, mesh.vertices[u])),
265 1.0 / bnd_nbrs.len() as f64,
266 );
267 new_verts[v] = add3(scale3(mesh.vertices[v], 0.5), scale3(avg, 0.5));
268 }
269 continue;
270 }
271 let k = nbrs[v].len();
272 if k == 0 {
273 continue;
274 }
275 let beta = if k == 3 {
277 3.0 / 16.0
278 } else {
279 3.0 / (8.0 * k as f64)
280 };
281 let sum_nbr = nbrs[v]
282 .iter()
283 .fold([0.0; 3], |acc, &u| add3(acc, mesh.vertices[u]));
284 new_verts[v] = add3(
285 scale3(mesh.vertices[v], 1.0 - k as f64 * beta),
286 scale3(sum_nbr, beta),
287 );
288 }
289
290 SubdivMesh::new(new_verts, new_faces)
291}
292
293pub fn butterfly_subdivision(mesh: &SubdivMesh) -> SubdivMesh {
305 let is_boundary = mesh.boundary_vertices();
306
307 let nbrs = mesh.vertex_neighbours();
309
310 let mut edge_opposite: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
312 for face in &mesh.faces {
313 let [a, b, c] = *face;
314 edge_opposite.entry((a, b)).or_default().push(c);
315 edge_opposite.entry((b, c)).or_default().push(a);
316 edge_opposite.entry((c, a)).or_default().push(b);
317 }
318
319 let mut edge_mid: HashMap<(usize, usize), usize> = HashMap::new();
320 let mut new_verts = mesh.vertices.clone();
321
322 let compute_butterfly_point = |a: usize,
323 b: usize,
324 verts: &Vec<[f64; 3]>,
325 _is_boundary: &[bool],
326 nbrs: &[Vec<usize>],
327 edge_opposite: &HashMap<(usize, usize), Vec<usize>>|
328 -> [f64; 3] {
329 let opp_ab = edge_opposite.get(&(a, b)).cloned().unwrap_or_default();
331 let opp_ba = edge_opposite.get(&(b, a)).cloned().unwrap_or_default();
332
333 if opp_ab.len() == 1 && opp_ba.len() == 1 {
334 let (c, d) = (opp_ab[0], opp_ba[0]);
336 let base = add3(scale3(verts[a], 0.5), scale3(verts[b], 0.5));
339 let opp_contrib = scale3(add3(verts[c], verts[d]), 1.0 / 8.0);
340
341 let wing_a: Vec<usize> = nbrs[a]
344 .iter()
345 .copied()
346 .filter(|&u| u != b && u != c && u != d)
347 .collect();
348 let wing_b: Vec<usize> = nbrs[b]
349 .iter()
350 .copied()
351 .filter(|&u| u != a && u != c && u != d)
352 .collect();
353
354 let wing_sum = wing_a
355 .iter()
356 .chain(wing_b.iter())
357 .fold([0.0; 3], |acc, &u| add3(acc, verts[u]));
358 let n_wings = (wing_a.len() + wing_b.len()) as f64;
359 if n_wings > 0.0 {
360 let wing_contrib = scale3(wing_sum, -1.0 / 16.0 / n_wings * n_wings);
361 let _ = wing_contrib; }
363 let sub = scale3(
365 wing_a
366 .iter()
367 .chain(wing_b.iter())
368 .fold([0.0; 3], |acc, &u| add3(acc, verts[u])),
369 if n_wings > 0.0 { -1.0 / 16.0 } else { 0.0 },
370 );
371 add3(add3(base, opp_contrib), sub)
372 } else {
373 midpoint3(verts[a], verts[b])
375 }
376 };
377
378 let mut new_faces = Vec::with_capacity(mesh.faces.len() * 4);
379 for face in &mesh.faces {
380 let [a, b, c] = *face;
381 let key_ab = (a.min(b), a.max(b));
382 let key_bc = (b.min(c), b.max(c));
383 let key_ca = (c.min(a), c.max(a));
384
385 let ab = if let Some(&idx) = edge_mid.get(&key_ab) {
386 idx
387 } else {
388 let pt = compute_butterfly_point(a, b, &new_verts, &is_boundary, &nbrs, &edge_opposite);
389 let idx = new_verts.len();
390 new_verts.push(pt);
391 edge_mid.insert(key_ab, idx);
392 idx
393 };
394 let bc = if let Some(&idx) = edge_mid.get(&key_bc) {
395 idx
396 } else {
397 let pt = compute_butterfly_point(b, c, &new_verts, &is_boundary, &nbrs, &edge_opposite);
398 let idx = new_verts.len();
399 new_verts.push(pt);
400 edge_mid.insert(key_bc, idx);
401 idx
402 };
403 let ca = if let Some(&idx) = edge_mid.get(&key_ca) {
404 idx
405 } else {
406 let pt = compute_butterfly_point(c, a, &new_verts, &is_boundary, &nbrs, &edge_opposite);
407 let idx = new_verts.len();
408 new_verts.push(pt);
409 edge_mid.insert(key_ca, idx);
410 idx
411 };
412
413 new_faces.push([a, ab, ca]);
414 new_faces.push([ab, b, bc]);
415 new_faces.push([ca, bc, c]);
416 new_faces.push([ab, bc, ca]);
417 }
418
419 SubdivMesh::new(new_verts, new_faces)
421}
422
423#[derive(Debug, Clone)]
429pub struct QuadMesh {
430 pub vertices: Vec<[f64; 3]>,
432 pub faces: Vec<[usize; 4]>,
434}
435
436impl QuadMesh {
437 pub fn new(vertices: Vec<[f64; 3]>, faces: Vec<[usize; 4]>) -> Self {
439 Self { vertices, faces }
440 }
441
442 pub fn n_vertices(&self) -> usize {
444 self.vertices.len()
445 }
446
447 pub fn n_faces(&self) -> usize {
449 self.faces.len()
450 }
451}
452
453pub fn catmull_clark_subdivision(mesh: &QuadMesh) -> QuadMesh {
464 let nv = mesh.vertices.len();
465 let nf = mesh.faces.len();
466
467 let face_points: Vec<[f64; 3]> = mesh
469 .faces
470 .iter()
471 .map(|face| {
472 let sum = face
473 .iter()
474 .fold([0.0; 3], |acc, &i| add3(acc, mesh.vertices[i]));
475 scale3(sum, 0.25)
476 })
477 .collect();
478
479 let mut edge_faces: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
482 for (fi, face) in mesh.faces.iter().enumerate() {
483 for k in 0..4 {
484 let a = face[k];
485 let b = face[(k + 1) % 4];
486 let key = (a.min(b), a.max(b));
487 edge_faces.entry(key).or_default().push(fi);
488 }
489 }
490
491 let mut edge_point_map: HashMap<(usize, usize), usize> = HashMap::new();
492 let mut all_verts: Vec<[f64; 3]> = mesh.vertices.clone();
493 let face_point_start = nv;
495 for &fp in &face_points {
496 all_verts.push(fp);
497 }
498 let edge_point_start = all_verts.len();
499
500 for (&(a, b), fis) in &edge_faces {
501 let ep = if fis.len() == 2 {
502 let pa = mesh.vertices[a];
504 let pb = mesh.vertices[b];
505 let fp0 = face_points[fis[0]];
506 let fp1 = face_points[fis[1]];
507 scale3(add3(add3(pa, pb), add3(fp0, fp1)), 0.25)
508 } else {
509 midpoint3(mesh.vertices[a], mesh.vertices[b])
511 };
512 let idx = all_verts.len();
513 all_verts.push(ep);
514 edge_point_map.insert((a, b), idx);
515 edge_point_map.insert((b, a), idx);
516 }
517
518 let mut vertex_faces: Vec<Vec<usize>> = vec![Vec::new(); nv];
525 let mut vertex_edge_midpoints: Vec<Vec<[f64; 3]>> = vec![Vec::new(); nv];
526 for (fi, face) in mesh.faces.iter().enumerate() {
527 for k in 0..4 {
528 let v = face[k];
529 let next = face[(k + 1) % 4];
530 let prev = face[(k + 3) % 4];
531 vertex_faces[v].push(fi);
532 vertex_edge_midpoints[v].push(midpoint3(mesh.vertices[v], mesh.vertices[next]));
533 vertex_edge_midpoints[v].push(midpoint3(mesh.vertices[v], mesh.vertices[prev]));
534 }
535 }
536
537 for v in 0..nv {
538 let n = vertex_faces[v].len() as f64;
539 if n == 0.0 {
540 continue;
541 }
542 let f_sum = vertex_faces[v]
543 .iter()
544 .fold([0.0; 3], |acc, &fi| add3(acc, face_points[fi]));
545 let f_avg = scale3(f_sum, 1.0 / n);
546
547 let r_sum = vertex_edge_midpoints[v]
548 .iter()
549 .fold([0.0; 3], |acc, &em| add3(acc, em));
550 let r_avg = scale3(r_sum, 1.0 / vertex_edge_midpoints[v].len() as f64);
551
552 let p = mesh.vertices[v];
553 all_verts[v] = scale3(
554 add3(add3(f_avg, scale3(r_avg, 2.0)), scale3(p, n - 3.0)),
555 1.0 / n,
556 );
557 }
558
559 let mut new_faces = Vec::with_capacity(nf * 4);
562 for (fi, face) in mesh.faces.iter().enumerate() {
563 let fp = face_point_start + fi;
564 for k in 0..4 {
565 let va = face[k];
566 let vb = face[(k + 1) % 4];
567 let vc = face[(k + 2) % 4]; let _ = vc;
569 let ep_ab = *edge_point_map.get(&(va, vb)).unwrap_or(&va);
570 let ep_prev = *edge_point_map.get(&(face[(k + 3) % 4], va)).unwrap_or(&va);
571 new_faces.push([va, ep_ab, fp, ep_prev]);
572 }
573 }
574
575 let _ = edge_point_start; QuadMesh::new(all_verts, new_faces)
578}
579
580#[derive(Debug, Clone, Copy)]
586pub enum AdaptiveCriterion {
587 MaxEdgeLength(f64),
589 AspectRatio(f64),
591 NearVertices,
593}
594
595pub fn adaptive_midpoint_subdivision(
600 mesh: &SubdivMesh,
601 criterion: AdaptiveCriterion,
602 selected_faces: Option<&[usize]>,
603) -> SubdivMesh {
604 let mut new_verts = mesh.vertices.clone();
605 let mut edge_mid: HashMap<(usize, usize), usize> = HashMap::new();
606 let mut new_faces = Vec::new();
607
608 let should_subdivide = |fi: usize, face: &[usize; 3]| -> bool {
609 if let Some(sel) = selected_faces {
610 return sel.contains(&fi);
611 }
612 let [a, b, c] = *face;
613 let pa = mesh.vertices[a];
614 let pb = mesh.vertices[b];
615 let pc = mesh.vertices[c];
616 match criterion {
617 AdaptiveCriterion::MaxEdgeLength(max_len) => {
618 let lab = len3(sub3(pb, pa));
619 let lbc = len3(sub3(pc, pb));
620 let lca = len3(sub3(pa, pc));
621 lab > max_len || lbc > max_len || lca > max_len
622 }
623 AdaptiveCriterion::AspectRatio(max_ar) => {
624 let lab = len3(sub3(pb, pa));
625 let lbc = len3(sub3(pc, pb));
626 let lca = len3(sub3(pa, pc));
627 let max_l = lab.max(lbc).max(lca);
628 let min_l = lab.min(lbc).min(lca);
629 if min_l < f64::EPSILON {
630 true
631 } else {
632 max_l / min_l > max_ar
633 }
634 }
635 AdaptiveCriterion::NearVertices => false,
636 }
637 };
638
639 let get_or_insert = |a: usize,
640 b: usize,
641 verts: &mut Vec<[f64; 3]>,
642 em: &mut HashMap<(usize, usize), usize>|
643 -> usize {
644 let key = (a.min(b), a.max(b));
645 if let Some(&idx) = em.get(&key) {
646 return idx;
647 }
648 let mid = midpoint3(verts[a], verts[b]);
649 let idx = verts.len();
650 verts.push(mid);
651 em.insert(key, idx);
652 idx
653 };
654
655 for (fi, face) in mesh.faces.iter().enumerate() {
656 let [a, b, c] = *face;
657 if should_subdivide(fi, face) {
658 let ab = get_or_insert(a, b, &mut new_verts, &mut edge_mid);
659 let bc = get_or_insert(b, c, &mut new_verts, &mut edge_mid);
660 let ca = get_or_insert(c, a, &mut new_verts, &mut edge_mid);
661 new_faces.push([a, ab, ca]);
662 new_faces.push([ab, b, bc]);
663 new_faces.push([ca, bc, c]);
664 new_faces.push([ab, bc, ca]);
665 } else {
666 new_faces.push([a, b, c]);
667 }
668 }
669
670 SubdivMesh::new(new_verts, new_faces)
671}
672
673pub fn doo_sabin_step(mesh: &SubdivMesh) -> (Vec<[f64; 3]>, Vec<[usize; 3]>) {
688 let mut new_verts: Vec<[f64; 3]> = Vec::new();
691 let mut fv_map: HashMap<(usize, usize), usize> = HashMap::new();
693
694 for (fi, face) in mesh.faces.iter().enumerate() {
695 let centroid = scale3(
696 face.iter()
697 .fold([0.0; 3], |acc, &v| add3(acc, mesh.vertices[v])),
698 1.0 / 3.0,
699 );
700 for (k, &v) in face.iter().enumerate() {
701 let nv = midpoint3(mesh.vertices[v], centroid);
703 let idx = new_verts.len();
704 new_verts.push(nv);
705 fv_map.insert((fi, k), idx);
706 }
707 }
708
709 let mut new_faces: Vec<[usize; 3]> = Vec::new();
711 for (fi, _) in mesh.faces.iter().enumerate() {
712 let v0 = *fv_map.get(&(fi, 0)).expect("key must exist");
713 let v1 = *fv_map.get(&(fi, 1)).expect("key must exist");
714 let v2 = *fv_map.get(&(fi, 2)).expect("key must exist");
715 new_faces.push([v0, v1, v2]);
716 }
717
718 (new_verts, new_faces)
719}
720
721pub fn subdivide_n_times_midpoint(mesh: &SubdivMesh, n: usize) -> SubdivMesh {
727 let mut current = mesh.clone();
728 for _ in 0..n {
729 current = midpoint_subdivision(¤t);
730 }
731 current
732}
733
734pub fn subdivide_n_times_loop(mesh: &SubdivMesh, n: usize) -> SubdivMesh {
736 let mut current = mesh.clone();
737 for _ in 0..n {
738 current = loop_subdivision(¤t);
739 }
740 current
741}
742
743pub fn subdivide_n_times_butterfly(mesh: &SubdivMesh, n: usize) -> SubdivMesh {
745 let mut current = mesh.clone();
746 for _ in 0..n {
747 current = butterfly_subdivision(¤t);
748 }
749 current
750}
751
752pub fn subdivide_quad_n_times(mesh: &QuadMesh, n: usize) -> QuadMesh {
754 let mut current = mesh.clone();
755 for _ in 0..n {
756 current = catmull_clark_subdivision(¤t);
757 }
758 current
759}
760
761#[cfg(test)]
766mod tests {
767 use super::*;
768
769 fn equilateral_tri_mesh() -> SubdivMesh {
773 let h = (3.0_f64).sqrt() / 2.0;
774 SubdivMesh::new(
775 vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.5, h, 0.0]],
776 vec![[0, 1, 2]],
777 )
778 }
779
780 fn two_tri_mesh() -> SubdivMesh {
782 SubdivMesh::new(
783 vec![
784 [0.0, 0.0, 0.0],
785 [1.0, 0.0, 0.0],
786 [0.5, 1.0, 0.0],
787 [1.5, 1.0, 0.0],
788 ],
789 vec![[0, 1, 2], [1, 3, 2]],
790 )
791 }
792
793 fn unit_square_tri_mesh() -> SubdivMesh {
795 SubdivMesh::new(
796 vec![
797 [0.0, 0.0, 0.0],
798 [1.0, 0.0, 0.0],
799 [1.0, 1.0, 0.0],
800 [0.0, 1.0, 0.0],
801 ],
802 vec![[0, 1, 2], [0, 2, 3]],
803 )
804 }
805
806 fn unit_square_quad_mesh() -> QuadMesh {
808 QuadMesh::new(
809 vec![
810 [0.0, 0.0, 0.0],
811 [1.0, 0.0, 0.0],
812 [1.0, 1.0, 0.0],
813 [0.0, 1.0, 0.0],
814 ],
815 vec![[0, 1, 2, 3]],
816 )
817 }
818
819 fn tet_surface() -> SubdivMesh {
821 SubdivMesh::new(
822 vec![
823 [1.0, 1.0, 1.0],
824 [-1.0, -1.0, 1.0],
825 [-1.0, 1.0, -1.0],
826 [1.0, -1.0, -1.0],
827 ],
828 vec![[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]],
829 )
830 }
831
832 #[test]
835 fn test_subdiv_mesh_n_vertices() {
836 let m = equilateral_tri_mesh();
837 assert_eq!(m.n_vertices(), 3);
838 }
839
840 #[test]
841 fn test_subdiv_mesh_n_faces() {
842 let m = unit_square_tri_mesh();
843 assert_eq!(m.n_faces(), 2);
844 }
845
846 #[test]
847 fn test_vertex_neighbours_triangle() {
848 let m = equilateral_tri_mesh();
849 let nbrs = m.vertex_neighbours();
850 assert_eq!(nbrs.len(), 3);
851 for v in 0..3 {
852 assert_eq!(
853 nbrs[v].len(),
854 2,
855 "each vertex in triangle should have 2 neighbours"
856 );
857 }
858 }
859
860 #[test]
861 fn test_vertex_neighbours_two_triangles() {
862 let m = two_tri_mesh();
863 let nbrs = m.vertex_neighbours();
864 for (i, nb) in nbrs.iter().enumerate() {
866 assert!(!nb.is_empty(), "vertex {i} should have neighbours");
867 }
868 }
869
870 #[test]
871 fn test_boundary_vertices_single_triangle() {
872 let m = equilateral_tri_mesh();
873 let bnd = m.boundary_vertices();
874 assert!(
876 bnd.iter().all(|&b| b),
877 "all vertices should be boundary in single triangle"
878 );
879 }
880
881 #[test]
882 fn test_boundary_vertices_closed_surface() {
883 let m = tet_surface();
885 let bnd = m.boundary_vertices();
886 assert!(
887 bnd.iter().all(|&b| !b),
888 "closed surface should have no boundary vertices"
889 );
890 }
891
892 #[test]
895 fn test_midpoint_one_to_four() {
896 let m = equilateral_tri_mesh();
897 let refined = midpoint_subdivision(&m);
898 assert_eq!(
899 refined.n_faces(),
900 4,
901 "one triangle → 4 after midpoint subdivision"
902 );
903 }
904
905 #[test]
906 fn test_midpoint_vertex_count() {
907 let m = equilateral_tri_mesh();
908 let refined = midpoint_subdivision(&m);
909 assert_eq!(refined.n_vertices(), 6);
911 }
912
913 #[test]
914 fn test_midpoint_preserves_original_vertices() {
915 let m = equilateral_tri_mesh();
916 let refined = midpoint_subdivision(&m);
917 for (i, &orig) in m.vertices.iter().enumerate() {
918 let refined_v = refined.vertices[i];
919 for k in 0..3 {
920 assert!(
921 (refined_v[k] - orig[k]).abs() < 1e-12,
922 "original vertex {i} component {k} should be preserved"
923 );
924 }
925 }
926 }
927
928 #[test]
929 fn test_midpoint_two_faces_to_eight() {
930 let m = unit_square_tri_mesh();
931 let refined = midpoint_subdivision(&m);
932 assert_eq!(refined.n_faces(), 8);
933 }
934
935 #[test]
936 fn test_midpoint_twice() {
937 let m = equilateral_tri_mesh();
938 let r2 = subdivide_n_times_midpoint(&m, 2);
939 assert_eq!(r2.n_faces(), 16, "two levels: 1→4→16 faces");
940 }
941
942 #[test]
943 fn test_midpoint_shared_edge_single_midpoint() {
944 let m = two_tri_mesh();
946 let refined = midpoint_subdivision(&m);
947 assert!(refined.n_vertices() >= 4, "should have at least 4 vertices");
949 }
950
951 #[test]
952 fn test_midpoint_all_vertices_finite() {
953 let m = tet_surface();
954 let refined = midpoint_subdivision(&m);
955 for v in &refined.vertices {
956 assert!(
957 v.iter().all(|c| c.is_finite()),
958 "all vertex coordinates should be finite"
959 );
960 }
961 }
962
963 #[test]
966 fn test_loop_one_to_four_faces() {
967 let m = equilateral_tri_mesh();
968 let refined = loop_subdivision(&m);
969 assert_eq!(refined.n_faces(), 4);
970 }
971
972 #[test]
973 fn test_loop_tet_face_count() {
974 let m = tet_surface();
975 let refined = loop_subdivision(&m);
976 assert_eq!(refined.n_faces(), 16, "4 faces → 16 after one Loop step");
977 }
978
979 #[test]
980 fn test_loop_vertices_finite() {
981 let m = tet_surface();
982 let refined = loop_subdivision(&m);
983 for v in &refined.vertices {
984 assert!(
985 v.iter().all(|c| c.is_finite()),
986 "Loop subdivision should produce finite vertices"
987 );
988 }
989 }
990
991 #[test]
992 fn test_loop_twice() {
993 let m = tet_surface();
994 let r2 = subdivide_n_times_loop(&m, 2);
995 assert_eq!(r2.n_faces(), 64, "two Loop levels: 4→16→64 faces");
996 }
997
998 #[test]
999 fn test_loop_smooth_moves_vertices() {
1000 let m = two_tri_mesh();
1003 let refined = loop_subdivision(&m);
1004 assert!(
1006 refined.n_vertices() > 4,
1007 "should have more vertices after Loop subdivision"
1008 );
1009 }
1010
1011 #[test]
1012 fn test_loop_boundary_preserved_approximately() {
1013 let m = equilateral_tri_mesh();
1015 let refined = loop_subdivision(&m);
1016 for i in 0..3 {
1018 let orig = m.vertices[i];
1019 let new_v = refined.vertices[i];
1020 let dist = len3(sub3(orig, new_v));
1021 assert!(
1022 dist < 0.5 + 1e-10,
1023 "boundary vertex {i} moved too far: dist={dist}"
1024 );
1025 }
1026 }
1027
1028 #[test]
1031 fn test_butterfly_one_to_four_faces() {
1032 let m = equilateral_tri_mesh();
1033 let refined = butterfly_subdivision(&m);
1034 assert_eq!(refined.n_faces(), 4);
1035 }
1036
1037 #[test]
1038 fn test_butterfly_tet_face_count() {
1039 let m = tet_surface();
1040 let refined = butterfly_subdivision(&m);
1041 assert_eq!(refined.n_faces(), 16);
1042 }
1043
1044 #[test]
1045 fn test_butterfly_preserves_original_vertices() {
1046 let m = tet_surface();
1048 let refined = butterfly_subdivision(&m);
1049 for (i, &orig) in m.vertices.iter().enumerate() {
1050 let new_v = refined.vertices[i];
1051 for k in 0..3 {
1052 assert!(
1053 (new_v[k] - orig[k]).abs() < 1e-10,
1054 "butterfly vertex {i}[{k}] should be preserved: orig={} new={}",
1055 orig[k],
1056 new_v[k]
1057 );
1058 }
1059 }
1060 }
1061
1062 #[test]
1063 fn test_butterfly_vertices_finite() {
1064 let m = tet_surface();
1065 let refined = butterfly_subdivision(&m);
1066 for v in &refined.vertices {
1067 assert!(v.iter().all(|c| c.is_finite()));
1068 }
1069 }
1070
1071 #[test]
1072 fn test_butterfly_twice() {
1073 let m = equilateral_tri_mesh();
1074 let r2 = subdivide_n_times_butterfly(&m, 2);
1075 assert_eq!(r2.n_faces(), 16);
1076 }
1077
1078 #[test]
1081 fn test_cc_one_to_four_faces() {
1082 let m = unit_square_quad_mesh();
1083 let refined = catmull_clark_subdivision(&m);
1084 assert_eq!(refined.n_faces(), 4, "one quad → 4 quads");
1085 }
1086
1087 #[test]
1088 fn test_cc_vertex_count() {
1089 let m = unit_square_quad_mesh();
1090 let refined = catmull_clark_subdivision(&m);
1091 assert_eq!(
1093 refined.n_vertices(),
1094 9,
1095 "expected 9 vertices: 4 orig + 1 face + 4 edge"
1096 );
1097 }
1098
1099 #[test]
1100 fn test_cc_vertices_finite() {
1101 let m = unit_square_quad_mesh();
1102 let refined = catmull_clark_subdivision(&m);
1103 for v in &refined.vertices {
1104 assert!(
1105 v.iter().all(|c| c.is_finite()),
1106 "all Catmull–Clark vertices should be finite"
1107 );
1108 }
1109 }
1110
1111 #[test]
1112 fn test_cc_twice() {
1113 let m = unit_square_quad_mesh();
1114 let r2 = subdivide_quad_n_times(&m, 2);
1115 assert_eq!(r2.n_faces(), 16, "two CC levels: 1→4→16 quads");
1116 }
1117
1118 #[test]
1119 fn test_cc_three_levels() {
1120 let m = unit_square_quad_mesh();
1121 let r3 = subdivide_quad_n_times(&m, 3);
1122 assert_eq!(r3.n_faces(), 64);
1123 }
1124
1125 #[test]
1126 fn test_cc_multi_quad_mesh() {
1127 let m = QuadMesh::new(
1129 vec![
1130 [0.0, 0.0, 0.0],
1131 [1.0, 0.0, 0.0],
1132 [1.0, 1.0, 0.0],
1133 [0.0, 1.0, 0.0],
1134 [2.0, 0.0, 0.0],
1135 [2.0, 1.0, 0.0],
1136 ],
1137 vec![[0, 1, 2, 3], [1, 4, 5, 2]],
1138 );
1139 let refined = catmull_clark_subdivision(&m);
1140 assert_eq!(refined.n_faces(), 8, "2 quads → 8 quads");
1141 for v in &refined.vertices {
1142 assert!(v.iter().all(|c| c.is_finite()));
1143 }
1144 }
1145
1146 #[test]
1149 fn test_adaptive_all_selected() {
1150 let m = equilateral_tri_mesh();
1151 let refined =
1152 adaptive_midpoint_subdivision(&m, AdaptiveCriterion::MaxEdgeLength(0.0), Some(&[0]));
1153 assert_eq!(refined.n_faces(), 4, "selected face should be subdivided");
1154 }
1155
1156 #[test]
1157 fn test_adaptive_none_selected() {
1158 let m = unit_square_tri_mesh();
1159 let refined =
1160 adaptive_midpoint_subdivision(&m, AdaptiveCriterion::MaxEdgeLength(100.0), None);
1161 assert_eq!(
1162 refined.n_faces(),
1163 2,
1164 "no faces should be subdivided when edge length criterion is large"
1165 );
1166 }
1167
1168 #[test]
1169 fn test_adaptive_edge_length_threshold() {
1170 let m = equilateral_tri_mesh();
1172 let refined =
1173 adaptive_midpoint_subdivision(&m, AdaptiveCriterion::MaxEdgeLength(0.5), None);
1174 assert_eq!(refined.n_faces(), 4);
1175 }
1176
1177 #[test]
1178 fn test_adaptive_aspect_ratio_perfect_equilateral_not_subdivided() {
1179 let m = equilateral_tri_mesh();
1181 let refined = adaptive_midpoint_subdivision(&m, AdaptiveCriterion::AspectRatio(2.0), None);
1182 assert_eq!(
1183 refined.n_faces(),
1184 1,
1185 "equilateral triangle should not be subdivided"
1186 );
1187 }
1188
1189 #[test]
1190 fn test_adaptive_aspect_ratio_stretched() {
1191 let m = SubdivMesh::new(
1193 vec![[0.0, 0.0, 0.0], [10.0, 0.0, 0.0], [0.05, 0.001, 0.0]],
1194 vec![[0, 1, 2]],
1195 );
1196 let refined = adaptive_midpoint_subdivision(&m, AdaptiveCriterion::AspectRatio(3.0), None);
1197 assert_eq!(
1198 refined.n_faces(),
1199 4,
1200 "high-AR triangle should be subdivided"
1201 );
1202 }
1203
1204 #[test]
1207 fn test_doo_sabin_vertex_count() {
1208 let m = equilateral_tri_mesh();
1209 let (verts, faces) = doo_sabin_step(&m);
1210 assert_eq!(verts.len(), 3, "one triangle → 3 new Doo–Sabin vertices");
1212 assert_eq!(faces.len(), 1, "one new inner triangle");
1213 }
1214
1215 #[test]
1216 fn test_doo_sabin_vertices_finite() {
1217 let m = tet_surface();
1218 let (verts, _) = doo_sabin_step(&m);
1219 for v in &verts {
1220 assert!(v.iter().all(|c| c.is_finite()));
1221 }
1222 }
1223
1224 #[test]
1225 fn test_doo_sabin_inner_triangle_smaller() {
1226 let m = equilateral_tri_mesh();
1227 let (verts, faces) = doo_sabin_step(&m);
1228 let inner_tri = faces[0];
1230 let v0 = verts[inner_tri[0]];
1231 let v1 = verts[inner_tri[1]];
1232 let _v2 = verts[inner_tri[2]];
1233 let inner_edge = len3(sub3(v1, v0));
1235 let orig_edge = len3(sub3(m.vertices[1], m.vertices[0]));
1237 assert!(
1238 inner_edge < orig_edge,
1239 "inner triangle should be smaller: inner={inner_edge} < orig={orig_edge}"
1240 );
1241 }
1242
1243 #[test]
1246 fn test_repeat_zero_times() {
1247 let m = equilateral_tri_mesh();
1248 let refined = subdivide_n_times_midpoint(&m, 0);
1249 assert_eq!(
1250 refined.n_faces(),
1251 m.n_faces(),
1252 "0 iterations should return unchanged mesh"
1253 );
1254 }
1255
1256 #[test]
1257 fn test_repeat_midpoint_exponential_growth() {
1258 let m = equilateral_tri_mesh();
1259 for n in 1..=4 {
1260 let refined = subdivide_n_times_midpoint(&m, n);
1261 assert_eq!(
1262 refined.n_faces(),
1263 1 << (2 * n),
1264 "n={n}: expected {} faces, got {}",
1265 1 << (2 * n),
1266 refined.n_faces()
1267 );
1268 }
1269 }
1270
1271 #[test]
1272 fn test_repeat_loop_face_growth() {
1273 let m = tet_surface();
1274 let r1 = subdivide_n_times_loop(&m, 1);
1275 let r2 = subdivide_n_times_loop(&m, 2);
1276 assert_eq!(r1.n_faces(), 16);
1277 assert_eq!(r2.n_faces(), 64);
1278 }
1279
1280 #[test]
1281 fn test_repeat_butterfly_face_growth() {
1282 let m = equilateral_tri_mesh();
1283 let r3 = subdivide_n_times_butterfly(&m, 3);
1284 assert_eq!(r3.n_faces(), 64);
1285 }
1286
1287 #[test]
1288 fn test_all_schemes_produce_valid_indices() {
1289 let m = tet_surface();
1290 for (name, refined) in [
1291 ("midpoint", midpoint_subdivision(&m)),
1292 ("loop", loop_subdivision(&m)),
1293 ("butterfly", butterfly_subdivision(&m)),
1294 ] {
1295 let nv = refined.n_vertices();
1296 for (fi, face) in refined.faces.iter().enumerate() {
1297 for &vi in face.iter() {
1298 assert!(
1299 vi < nv,
1300 "{name}: face {fi} has invalid vertex index {vi} >= {nv}"
1301 );
1302 }
1303 }
1304 }
1305 }
1306}