1use hashbrown::HashSet;
2use itertools::Itertools;
3use tracing::{error, instrument};
4
5use crate::{FaceId, HalfedgeId, MeshGraph, VertexId, error_none};
6
7#[derive(Default)]
8pub struct VertexNeighborhoodCleanup {
9 pub removed_vertices: Vec<VertexId>,
10 pub removed_halfedges: Vec<HalfedgeId>,
11 pub removed_faces: Vec<FaceId>,
12
13 pub added_vertices: Vec<VertexId>,
14}
15
16#[derive(Default)]
17struct VertexNeighborhoodCleanupStep {
18 pub removed_vertices: Vec<VertexId>,
19 pub removed_halfedges: Vec<HalfedgeId>,
20 pub removed_faces: Vec<FaceId>,
21
22 pub added_duplicated_vertices: Vec<VertexId>,
23 pub touched_vertices: Vec<VertexId>,
24}
25
26impl MeshGraph {
27 #[instrument(skip(self))]
35 pub fn make_vertex_neighborhood_manifold(
36 &mut self,
37 vertex_id: VertexId,
38 ) -> VertexNeighborhoodCleanup {
39 #[cfg(feature = "rerun")]
40 self.log_vert_rerun("make_neigh_manifold", vertex_id);
41
42 let mut result = VertexNeighborhoodCleanup::default();
43
44 let mut vertices = vec![vertex_id];
45
46 let vertex_count = self.vertices.len();
47 let mut iterations = 0;
48
49 while let Some(&v_id) = vertices.first()
50 && iterations < vertex_count
51 {
52 if !self.vertices.contains_key(v_id) {
53 vertices.swap_remove(0);
54 continue;
55 }
56
57 let VertexNeighborhoodCleanupStep {
58 added_duplicated_vertices,
59 touched_vertices,
60 removed_vertices,
61 removed_halfedges,
62 removed_faces,
63 } = self.make_vertex_neighborhood_manifold_step(v_id);
64
65 if removed_vertices.is_empty()
66 && removed_halfedges.is_empty()
67 && removed_faces.is_empty()
68 && added_duplicated_vertices.is_empty()
69 && touched_vertices.is_empty()
70 {
71 vertices.swap_remove(0);
72 }
73
74 vertices.extend(touched_vertices);
75 for added_vertex in added_duplicated_vertices {
76 vertices.push(added_vertex);
77
78 result.added_vertices.push(added_vertex);
79 }
80
81 result.removed_vertices.extend(removed_vertices);
82 result.removed_halfedges.extend(removed_halfedges);
83 result.removed_faces.extend(removed_faces);
84
85 iterations += 1;
86 }
87
88 for &cancelled_v_id in
89 HashSet::<VertexId>::from_iter(result.removed_vertices.iter().copied())
90 .intersection(&HashSet::from_iter(result.added_vertices.iter().copied()))
91 {
92 result.added_vertices.retain(|&v_id| v_id != cancelled_v_id);
93 result
94 .removed_vertices
95 .retain(|&v_id| v_id != cancelled_v_id);
96 }
97
98 result
99 }
100
101 fn make_vertex_neighborhood_manifold_step(
102 &mut self,
103 vertex_id: VertexId,
104 ) -> VertexNeighborhoodCleanupStep {
105 let mut removed_vertices = Vec::new();
106 let mut removed_halfedges = Vec::new();
107 let mut removed_faces = Vec::new();
108
109 let added_duplicated_vertices = self
110 .make_vertex_neighborhood_manifold_inner(
111 vertex_id,
112 &mut removed_vertices,
113 &mut removed_halfedges,
114 &mut removed_faces,
115 )
116 .unwrap_or_default();
117
118 VertexNeighborhoodCleanupStep {
119 added_duplicated_vertices,
120 touched_vertices: vec![],
121 removed_vertices,
122 removed_halfedges,
123 removed_faces,
124 }
125 }
126
127 pub fn make_vertex_neighborhood_manifold_inner(
128 &mut self,
129 vertex_id: VertexId,
130 removed_vertices: &mut Vec<VertexId>,
131 removed_halfedges: &mut Vec<HalfedgeId>,
132 removed_faces: &mut Vec<FaceId>,
133 ) -> Option<Vec<VertexId>> {
134 self.vertices.get(vertex_id)?;
135
136 if self.remove_single_face(
137 vertex_id,
138 removed_vertices,
139 removed_halfedges,
140 removed_faces,
141 )? {
142 return Some(vec![]);
143 }
144
145 if self.remove_neighboring_flaps(
146 vertex_id,
147 removed_vertices,
148 removed_halfedges,
149 removed_faces,
150 )? {
151 return Some(vec![]);
152 }
153
154 if let Some(new_vertices) = self.split_disconnected_neighborhoods(vertex_id)
155 && !new_vertices.is_empty()
156 {
157 return Some(new_vertices);
158 }
159
160 if let Some(inserted_duplicated_vertex) = self.remove_degenerate_faces(
161 vertex_id,
162 removed_vertices,
163 removed_halfedges,
164 removed_faces,
165 ) {
166 return Some(vec![inserted_duplicated_vertex]);
167 }
168
169 self.remove_degenerate_edges(vertex_id).map(|v| vec![v])
170 }
171
172 fn split_disconnected_neighborhoods(&mut self, vertex_id: VertexId) -> Option<Vec<VertexId>> {
173 let mut new_vertices = Vec::new();
174
175 let mut outgoing_halfedges = HashSet::<HalfedgeId>::from_iter(
176 self.outgoing_halfedges
177 .get(vertex_id)
178 .or_else(error_none!("Outgoing halfedges not found"))?
179 .iter()
180 .copied(),
181 );
182
183 while let Some(&start_he_id) = outgoing_halfedges.iter().next() {
184 let mut current_he_id = start_he_id;
185
186 let len = outgoing_halfedges.len();
187 let mut current_outgoing_halfedges = Vec::with_capacity(len);
188
189 loop {
190 if !outgoing_halfedges.remove(¤t_he_id) {
191 return None;
192 }
193 current_outgoing_halfedges.push(current_he_id);
194 if current_outgoing_halfedges.len() == len {
195 return None;
196 }
197
198 let cur_he = self
199 .halfedges
200 .get(current_he_id)
201 .or_else(error_none!("Halfedge not found"))?;
202
203 let Some(next_he_id) = cur_he.cw_rotated_neighbour(self) else {
204 let mut back_he_id = start_he_id;
212 while let Some(prev_he_id) = self
213 .halfedges
214 .get(back_he_id)
215 .or_else(error_none!("Halfedge not found"))?
216 .ccw_rotated_neighbour(self)
217 {
218 if !outgoing_halfedges.remove(&prev_he_id) {
219 break;
220 }
221 current_outgoing_halfedges.push(prev_he_id);
222 back_he_id = prev_he_id;
223 }
224
225 break;
226 };
227
228 if next_he_id == start_he_id {
229 break;
230 }
231
232 current_he_id = next_he_id;
233 }
234
235 if !outgoing_halfedges.is_empty() {
236 let new_vertex_id = self
237 .duplicate_vertex_and_assign_halfedges(vertex_id, current_outgoing_halfedges)?;
238
239 new_vertices.push(new_vertex_id);
240
241 self.vertices
242 .get_mut(vertex_id)
243 .or_else(error_none!("Vertex not found"))?
244 .outgoing_halfedge = Some(*outgoing_halfedges.iter().next().unwrap()); self.outgoing_halfedges
247 .insert(vertex_id, Vec::from_iter(outgoing_halfedges));
248
249 return Some(new_vertices);
250 }
251 }
252
253 Some(new_vertices)
254 }
255
256 fn duplicate_vertex_and_assign_halfedges(
257 &mut self,
258 vertex_id: VertexId,
259 outgoing_halfedges: Vec<HalfedgeId>,
260 ) -> Option<VertexId> {
261 let new_vert_id = self.add_vertex(
262 *self
263 .positions
264 .get(vertex_id)
265 .or_else(error_none!("Position not found"))?
266 + glam::Vec3::new(0.1, 0.0, 0.0),
267 );
268
269 tracing::debug!("Duplicated {vertex_id:?}: {new_vert_id:?}");
270
271 for &he_id in &outgoing_halfedges {
272 let he = self
273 .halfedges
274 .get(he_id)
275 .or_else(error_none!("Halfedge not found"))?;
276
277 let twin_id = he.twin.or_else(error_none!("Twin not found"))?;
278
279 self.halfedges
280 .get_mut(twin_id)
281 .or_else(error_none!("Twin not found"))?
282 .end_vertex = new_vert_id;
283 }
284
285 self.vertices[new_vert_id].outgoing_halfedge = Some(outgoing_halfedges[0]);
287
288 self.outgoing_halfedges
289 .insert(new_vert_id, outgoing_halfedges);
290
291 Some(new_vert_id)
292 }
293
294 fn remove_degenerate_edges(&mut self, vertex_id: VertexId) -> Option<VertexId> {
295 let halfedges = self
296 .vertices
297 .get(vertex_id)
298 .or_else(error_none!("Vertex not found"))?
299 .outgoing_halfedges(self)
300 .collect_vec();
301
302 for [he_id1, he_id2] in halfedges.into_iter().array_combinations() {
303 if self.halfedges_share_all_vertices(he_id1, he_id2) {
304 let he1 = self.halfedges[he_id1]; let twin_id1 = he1.twin.or_else(error_none!("Twin not found"))?;
306 let face_id1 = self
307 .halfedges
308 .get(twin_id1)
309 .or_else(error_none!("Halfedge not found"))?
310 .face
311 .or_else(error_none!("Face not found"))?;
312 let face_id2 = self.halfedges[he_id2] .face
314 .or_else(error_none!("Face not found"))?;
315
316 let coincident_face_ids = self.vertices[vertex_id].faces(self).collect_vec();
318 let mut start_idx = 0;
319
320 for (idx, face_id) in coincident_face_ids.iter().enumerate() {
321 if *face_id == face_id1 {
322 start_idx = idx;
323 break;
324 }
325 }
326
327 let mut end_idx = start_idx;
328 let mut side_one = vec![];
329
330 for _ in 0..coincident_face_ids.len() {
331 let face_id = coincident_face_ids[end_idx];
332
333 side_one.push(face_id);
334
335 end_idx += 1;
336 end_idx %= coincident_face_ids.len();
337
338 if face_id == face_id2 {
339 break;
340 }
341 }
342
343 let mut side_two = vec![];
344
345 for _ in 0..coincident_face_ids.len() {
346 if end_idx == start_idx {
347 break;
348 }
349
350 let face_id = coincident_face_ids[end_idx];
351
352 side_two.push(face_id);
353
354 end_idx += 1;
355 end_idx %= coincident_face_ids.len();
356 }
357
358 return self.split_regions_at_edge(vertex_id, he1.end_vertex, side_one, side_two);
359 }
360 }
361
362 None
363 }
364
365 #[instrument(skip(self))]
366 fn split_regions_at_edge(
367 &mut self,
368 vertex_id: VertexId,
369 other_vertex_id: VertexId,
370 side_one: Vec<FaceId>,
371 side_two: Vec<FaceId>,
372 ) -> Option<VertexId> {
373 if side_one.len() < 2 || side_two.len() < 2 {
374 error!("Not enough halfedges to split");
375 return None;
376 }
377
378 #[cfg(feature = "rerun")]
379 {
380 self.log_vert_rerun("split_regions_at_edge", vertex_id);
381 self.log_faces_rerun("split_regions_at_edge/side_one", &side_one);
382 self.log_faces_rerun("split_regions_at_edge/side_two", &side_two);
383 }
384
385 let new_vertex_id = self.add_vertex(
386 *self
387 .positions
388 .get(vertex_id)
389 .or_else(error_none!("Vertex position not found"))?,
390 );
391
392 for face_id in &side_two {
394 let face = self
395 .faces
396 .get(*face_id)
397 .or_else(error_none!("Face not found"))?;
398
399 for he_id in face.halfedges(self).collect_vec() {
400 let he = &mut self.halfedges[he_id];
402
403 if he.end_vertex == vertex_id {
404 he.end_vertex = new_vertex_id;
405 }
406 }
407 }
408
409 self.weld_faces_at(
410 vertex_id,
411 other_vertex_id,
412 side_one[side_one.len() - 1],
413 side_one[0],
414 );
415 self.weld_faces_at(
416 new_vertex_id,
417 other_vertex_id,
418 side_two[0],
419 side_two[side_two.len() - 1],
420 );
421
422 #[cfg(feature = "instrumentation")]
426 crate::record_op_trace!(
427 "split_regions_at_edge({vertex_id:?}, {other_vertex_id:?}): new {new_vertex_id:?}"
428 );
429 self.rebuild_vertex_outgoing_list(vertex_id);
430 self.rebuild_vertex_outgoing_list(new_vertex_id);
431
432 Some(new_vertex_id)
433 }
434
435 #[instrument(skip_all)]
436 pub fn remove_degenerate_faces(
437 &mut self,
438 vertex_id: VertexId,
439 removed_vertices: &mut Vec<VertexId>,
440 removed_halfedges: &mut Vec<HalfedgeId>,
441 removed_faces: &mut Vec<FaceId>,
442 ) -> Option<VertexId> {
443 let faces = self
444 .vertices
445 .get(vertex_id)
446 .or_else(error_none!("Vertex not found"))?
447 .faces(self)
448 .collect_vec();
449
450 for [face_id1, face_id2] in faces.into_iter().array_combinations() {
451 if self.faces_share_all_vertices(face_id1, face_id2) {
452 let coincident_face_ids = self.vertices[vertex_id].faces(self).collect_vec();
454 let mut start_idx = 0;
455
456 for (idx, face_id) in coincident_face_ids.iter().enumerate() {
457 if *face_id == face_id1 {
458 start_idx = (idx + 1) % coincident_face_ids.len();
459 break;
460 }
461 }
462
463 let mut end_idx = start_idx;
464 let mut side_one = vec![];
465
466 for _ in 0..coincident_face_ids.len() {
467 let face_id = coincident_face_ids[end_idx];
468
469 end_idx += 1;
470 end_idx %= coincident_face_ids.len();
471
472 if face_id == face_id2 {
473 break;
474 }
475
476 side_one.push(face_id);
477 }
478
479 let mut side_two = vec![];
480
481 for _ in 0..coincident_face_ids.len() {
482 if end_idx == start_idx {
483 break;
484 }
485
486 let face_id = coincident_face_ids[end_idx];
487
488 side_two.push(face_id);
489
490 end_idx += 1;
491 end_idx %= coincident_face_ids.len();
492 }
493
494 side_two.pop();
495
496 let new_vertex_id =
497 self.split_regions_at_vertex(vertex_id, side_one, side_two, removed_halfedges);
498
499 let (del_v_ids, del_he_ids) = self.remove_face(face_id1);
500 removed_vertices.extend(del_v_ids);
501 removed_halfedges.extend(del_he_ids);
502 removed_faces.push(face_id1);
503
504 let (del_v_ids, del_he_ids) = self.remove_face(face_id2);
505 removed_vertices.extend(del_v_ids);
506 removed_halfedges.extend(del_he_ids);
507 removed_faces.push(face_id2);
508
509 return new_vertex_id;
510 }
511 }
512
513 None
514 }
515
516 #[instrument(skip(self))]
517 fn split_regions_at_vertex(
518 &mut self,
519 vertex_id: VertexId,
520 side_one: Vec<FaceId>,
521 side_two: Vec<FaceId>,
522 removed_halfedges: &mut Vec<HalfedgeId>,
523 ) -> Option<VertexId> {
524 if side_one.len() < 2 || side_two.len() < 2 {
525 error!("Not enough halfedges to split");
526 return None;
527 }
528
529 #[cfg(feature = "rerun")]
530 {
531 self.log_vert_rerun("split_regions_at_vertex", vertex_id);
532 self.log_faces_rerun("split_regions_at_vertex/side_one", &side_one);
533 self.log_faces_rerun("split_regions_at_vertex/side_two", &side_two);
534 }
535
536 let new_vertex_id = self.add_vertex(
537 *self
538 .positions
539 .get(vertex_id)
540 .or_else(error_none!("Vertex position not found"))?,
541 );
542
543 for face_id in &side_two {
545 let face = self
546 .faces
547 .get(*face_id)
548 .or_else(error_none!("Face not found"))?;
549
550 for he_id in face.halfedges(self).collect_vec() {
551 let he = &mut self.halfedges[he_id];
553 if he.end_vertex == vertex_id {
554 he.end_vertex = new_vertex_id;
555 }
556 }
557 }
558
559 self.weld_faces(vertex_id, side_one[side_one.len() - 1], side_one[0]);
560 self.weld_faces(new_vertex_id, side_two[0], side_two[side_two.len() - 1]);
561
562 #[cfg(feature = "instrumentation")]
564 crate::record_op_trace!("split_regions_at_vertex({vertex_id:?}): new {new_vertex_id:?}");
565 self.rebuild_vertex_outgoing_list(vertex_id);
566 self.rebuild_vertex_outgoing_list(new_vertex_id);
567
568 Some(new_vertex_id)
569 }
570
571 fn weld_faces(
578 &mut self,
579 start_vertex_id: VertexId,
580 face_id1: FaceId,
581 face_id2: FaceId,
582 ) -> Option<()> {
583 let face1 = self
590 .faces
591 .get(face_id1)
592 .or_else(error_none!("Face not found"))?;
593 let face2 = self
594 .faces
595 .get(face_id2)
596 .or_else(error_none!("Face not found"))?;
597
598 let face1_vertices = face1.vertices(self).collect::<HashSet<_>>();
599 let face2_vertices = face2.vertices(self).collect::<HashSet<_>>();
600
601 let mut other_common_vertex_id = None;
602
603 for &v_id in face1_vertices.intersection(&face2_vertices) {
604 if v_id != start_vertex_id {
605 other_common_vertex_id = Some(v_id);
606 break;
607 }
608 }
609
610 let other_common_vertex_id =
611 other_common_vertex_id.or_else(error_none!("No other common vertex found"))?;
612
613 self.weld_faces_at(start_vertex_id, other_common_vertex_id, face_id1, face_id2)
614 }
615
616 #[instrument(skip(self))]
617 fn weld_faces_at(
618 &mut self,
619 start_vertex_id: VertexId,
620 other_common_vertex_id: VertexId,
621 face_id1: FaceId,
622 face_id2: FaceId,
623 ) -> Option<()> {
624 let face1 = self
625 .faces
626 .get(face_id1)
627 .or_else(error_none!("Face not found"))?;
628 let face2 = self
629 .faces
630 .get(face_id2)
631 .or_else(error_none!("Face not found"))?;
632
633 let he1_id = face1
634 .halfedge_between(start_vertex_id, other_common_vertex_id, self)
635 .or_else(error_none!("Halfedge between vertices not found"))?;
636
637 let he2_id = face2
638 .halfedge_between(start_vertex_id, other_common_vertex_id, self)
639 .or_else(error_none!("Halfedge between vertices not found"))?;
640
641 if !self.halfedges.contains_key(he1_id) || !self.halfedges.contains_key(he2_id) {
644 error!("Halfedge not found in weld_faces_at");
645 return None;
646 }
647 if he1_id == he2_id {
652 error!("weld_faces_at: faces share the same halfedge {he1_id:?}");
653 return None;
654 }
655 let t1 = self.halfedges.get(he1_id).and_then(|h| h.twin);
665 let t2 = self.halfedges.get(he2_id).and_then(|h| h.twin);
666 let t1_swappable = t1.is_some_and(|t1| {
667 t1 != he2_id
668 && self
669 .halfedges
670 .get(t1)
671 .is_some_and(|t| t.twin == Some(he1_id))
672 });
673 let t2_swappable = t2.is_some_and(|t2| {
674 t2 != he1_id
675 && self
676 .halfedges
677 .get(t2)
678 .is_some_and(|t| t.twin == Some(he2_id))
679 });
680 match (t1_swappable, t2_swappable) {
681 (true, true) => {
682 let t1 = t1.unwrap();
685 let t2 = t2.unwrap();
686 if t1 == t2 {
687 error!("weld_faces_at: old partners are identical {t1:?}");
690 } else {
691 self.halfedges.get_mut(t1).unwrap().twin = Some(t2);
692 self.halfedges.get_mut(t2).unwrap().twin = Some(t1);
693 }
694 }
695 (true, false) => {
696 let t1 = t1.unwrap();
700 let t1_start = self
702 .halfedges
703 .get(he1_id)
704 .map(|h| h.end_vertex)
705 .or_else(error_none!("Weld sever start vertex not found"))?;
706 if self.pair_with_fresh_boundary_half(t1, t1_start).is_none() {
707 error!("weld_faces_at: could not re-pair severed partner {t1:?}");
708 }
709 }
710 (false, true) => {
711 let t2 = t2.unwrap();
713 let t2_start = self
714 .halfedges
715 .get(he2_id)
716 .map(|h| h.end_vertex)
717 .or_else(error_none!("Weld sever start vertex not found"))?;
718 if self.pair_with_fresh_boundary_half(t2, t2_start).is_none() {
719 error!("weld_faces_at: could not re-pair severed partner {t2:?}");
720 }
721 }
722 _ => {}
723 }
724 self.halfedges[he1_id].twin = Some(he2_id);
725 self.halfedges[he2_id].twin = Some(he1_id);
726
727 let (start_out_he_id, other_out_he_id) =
728 if self.halfedges[he1_id].end_vertex == start_vertex_id {
729 (he2_id, he1_id)
730 } else {
731 (he1_id, he2_id)
732 };
733
734 self.vertices
735 .get_mut(start_vertex_id)
736 .or_else(error_none!("Start vertex not found"))?
737 .outgoing_halfedge = Some(start_out_he_id);
738 self.vertices
739 .get_mut(other_common_vertex_id)
740 .or_else(error_none!("Other vertex not found"))?
741 .outgoing_halfedge = Some(other_out_he_id);
742
743 Some(())
744 }
745
746 pub(crate) fn remove_single_face(
747 &mut self,
748 vertex_id: VertexId,
749 removed_vertices: &mut Vec<VertexId>,
750 removed_halfedges: &mut Vec<HalfedgeId>,
751 removed_faces: &mut Vec<FaceId>,
752 ) -> Option<bool> {
753 let face_ids = self
754 .outgoing_halfedges
755 .get(vertex_id)
756 .or_else(error_none!(
757 "Outgoing halfedges not found for vertex {vertex_id:?}"
758 ))?
759 .iter()
760 .filter_map(|&he_id| {
761 self.halfedges
762 .get(he_id)
763 .or_else(error_none!("Halfedge not found"))
764 .and_then(|he| he.face)
765 })
766 .collect_vec();
767
768 if face_ids.len() == 1 {
769 let face_id = face_ids[0];
770
771 let (v_ids, he_ids) = self.remove_face(face_id);
772
773 removed_vertices.extend(v_ids);
774 removed_halfedges.extend(he_ids);
775 removed_faces.push(face_id);
776
777 Some(true)
778 } else {
779 Some(false)
780 }
781 }
782
783 pub fn remove_neighboring_flaps(
785 &mut self,
786 vertex_id: VertexId,
787 removed_vertices: &mut Vec<VertexId>,
788 removed_halfedges: &mut Vec<HalfedgeId>,
789 removed_faces: &mut Vec<FaceId>,
790 ) -> Option<bool> {
791 let faces = self
792 .vertices
793 .get(vertex_id)
794 .or_else(error_none!("Vertex not found"))?
795 .faces(self)
796 .collect_vec();
797
798 if faces.len() < 2 {
799 return Some(false);
800 }
801
802 let mut face_tuples = faces.into_iter().circular_array_windows().collect_vec();
803
804 while let Some([face_id1, face_id2]) = face_tuples.pop() {
805 if self.faces_share_all_vertices(face_id1, face_id2) {
806 #[cfg(feature = "rerun")]
807 {
808 self.log_vert_rerun("flap", vertex_id);
809 self.log_face_rerun("flap1", face_id1);
810 self.log_face_rerun("flap2", face_id2);
811 }
812
813 let mut halfedges_of_faces = HashSet::<HalfedgeId>::from_iter(
814 self.halfedges.iter().filter_map(|(he_id, he)| {
815 if he.face == Some(face_id1) || he.face == Some(face_id2) {
816 Some(he_id)
817 } else {
818 None
819 }
820 }),
821 );
822
823 let (vs, hes) = self.remove_face(face_id1);
824 for he_id in &hes {
825 halfedges_of_faces.remove(he_id);
826 }
827 removed_vertices.extend(vs);
828 removed_halfedges.extend(hes);
829 removed_faces.push(face_id1);
830
831 let (vs, hes) = self.remove_face(face_id2);
832 for he_id in &hes {
833 halfedges_of_faces.remove(he_id);
834 }
835 removed_vertices.extend(vs);
836 removed_halfedges.extend(hes);
837 removed_faces.push(face_id2);
838
839 if halfedges_of_faces.len() >= 2 {
841 for [he_id1, he_id2] in halfedges_of_faces.iter().copied().array_combinations()
842 {
843 let Some(he1) = self.halfedges.get(he_id1) else {
844 continue;
846 };
847 let twin_id1 = he1.twin.or_else(error_none!("Twin 1 missing"))?;
848
849 let Some(he2) = self.halfedges.get(he_id2) else {
850 continue;
851 };
852 let twin_id2 = he2.twin.or_else(error_none!("Twin 2 missing"))?;
853
854 let start_v_id1 = he1
855 .start_vertex(self)
856 .or_else(error_none!("Start vertex 1 missing"))?;
857 let start_v_id2 = he2
858 .start_vertex(self)
859 .or_else(error_none!("Start vertex 2 missing"))?;
860
861 if he1.end_vertex != start_v_id2 || he2.end_vertex != start_v_id1 {
862 continue;
863 }
864
865 let he1_end_v = he1.end_vertex;
869 let he2_end_v = he2.end_vertex;
870
871 self.remove_only_halfedge(he_id1);
872 self.remove_only_halfedge(he_id2);
873 removed_halfedges.push(he_id1);
874 removed_halfedges.push(he_id2);
875
876 #[cfg(feature = "instrumentation")]
877 crate::record_op_trace!(
878 "flap removal: removed {he_id1:?}+{he_id2:?} (edge {start_v_id1:?}-{start_v_id2:?}); twins {twin_id1:?}/{twin_id2:?}"
879 );
880
881 let twin1_alive = self.halfedges.contains_key(twin_id1);
888 let twin2_alive = self.halfedges.contains_key(twin_id2);
889
890 if twin1_alive
897 && let Some(t1) = self.halfedges.get(twin_id1).and_then(|h| h.twin)
898 && t1 != twin_id2
899 && let Some(t1_he) = self.halfedges.get(t1)
900 && t1_he.twin == Some(twin_id1)
901 && self
902 .pair_with_fresh_boundary_half(
903 t1,
904 self.halfedges[twin_id1].end_vertex,
905 )
906 .is_none()
907 {
908 error!("remove_neighboring_flaps: could not re-pair {t1:?}");
909 }
910 if twin2_alive
911 && let Some(t2) = self.halfedges.get(twin_id2).and_then(|h| h.twin)
912 && t2 != twin_id1
913 && let Some(t2_he) = self.halfedges.get(t2)
914 && t2_he.twin == Some(twin_id2)
915 && self
916 .pair_with_fresh_boundary_half(
917 t2,
918 self.halfedges[twin_id2].end_vertex,
919 )
920 .is_none()
921 {
922 error!("remove_neighboring_flaps: could not re-pair {t2:?}");
923 }
924
925 match (twin1_alive, twin2_alive) {
931 (true, true) => {
932 self.halfedges.get_mut(twin_id1).unwrap().twin = Some(twin_id2);
933 self.halfedges.get_mut(twin_id2).unwrap().twin = Some(twin_id1);
934 #[cfg(feature = "instrumentation")]
935 crate::record_op_trace!(
936 "flap removal: re-paired {twin_id1:?}<->{twin_id2:?}"
937 );
938 if let Some(v) = self.vertices.get_mut(start_v_id1) {
939 v.outgoing_halfedge = Some(twin_id2);
940 }
941 if let Some(v) = self.vertices.get_mut(start_v_id2) {
942 v.outgoing_halfedge = Some(twin_id1);
943 }
944 }
945 (true, false) => {
946 match self.pair_with_fresh_boundary_half(twin_id1, he1_end_v) {
949 Some(fresh) => {
950 if let Some(v) = self.vertices.get_mut(start_v_id1) {
951 v.outgoing_halfedge = Some(fresh);
952 }
953 if let Some(v) = self.vertices.get_mut(start_v_id2) {
954 v.outgoing_halfedge = Some(twin_id1);
955 }
956 }
957 None => error!(
958 "remove_neighboring_flaps: could not re-pair twin {twin_id1:?}"
959 ),
960 }
961 }
962 (false, true) => {
963 match self.pair_with_fresh_boundary_half(twin_id2, he2_end_v) {
964 Some(fresh) => {
965 if let Some(v) = self.vertices.get_mut(start_v_id1) {
966 v.outgoing_halfedge = Some(twin_id2);
967 }
968 if let Some(v) = self.vertices.get_mut(start_v_id2) {
969 v.outgoing_halfedge = Some(fresh);
970 }
971 }
972 None => error!(
973 "remove_neighboring_flaps: could not re-pair twin {twin_id2:?}"
974 ),
975 }
976 }
977 (false, false) => {
978 self.reseed_outgoing_if_dead(start_v_id1);
984 self.reseed_outgoing_if_dead(start_v_id2);
985 }
986 }
987 }
988 } else if halfedges_of_faces.len() == 1 {
989 tracing::debug!("Single orphaned halfedge after flap removal (boundary edge)");
990
991 if let Some(&single_he_id) = halfedges_of_faces.iter().next() {
992 if let Some(single_he) = self.halfedges.get(single_he_id) {
993 if single_he.twin.is_none() {
994 tracing::error!("Single orphaned halfedge found and has no twin");
995 }
996 } else {
997 tracing::error!("Single orphaned halfedge not found")
998 }
999 }
1000 }
1001
1002 return Some(true);
1003 }
1004 }
1005
1006 Some(false)
1007 }
1008}
1009
1010#[cfg(test)]
1011mod tests {
1012 use crate::ops::AddOrGetEdge;
1013
1014 use super::*;
1015 use glam::Vec3;
1016
1017 #[test]
1018 fn test_remove_degenerate_faces() {
1019 crate::utils::get_tracing_subscriber();
1020 let mut meshgraph = MeshGraph::new();
1021
1022 let center_v_id = meshgraph.add_vertex(Vec3::new(0.0, 0.0, 1.0));
1023
1024 let v1_id = meshgraph.add_vertex(Vec3::new(-0.2, 0.0, 0.0));
1025 let he_c_1_id = meshgraph
1026 .add_or_get_edge(center_v_id, v1_id)
1027 .unwrap()
1028 .start_to_end_he_id;
1029 let v1p_id = meshgraph.add_vertex(Vec3::new(-0.2, 0.0, 0.0));
1030 let AddOrGetEdge {
1031 start_to_end_he_id: he_c_1p_id,
1032 twin_he_id: he_1p_c_id,
1033 ..
1034 } = meshgraph.add_or_get_edge(center_v_id, v1p_id).unwrap();
1035
1036 let v2_id = meshgraph.add_vertex(Vec3::new(-1.0, 1.0, 0.0));
1037 let he_c_2_id = meshgraph
1038 .add_or_get_edge(center_v_id, v2_id)
1039 .unwrap()
1040 .start_to_end_he_id;
1041
1042 let v3_id = meshgraph.add_vertex(Vec3::new(-1.0, -1.0, 0.0));
1043 let he_c_3_id = meshgraph
1044 .add_or_get_edge(center_v_id, v3_id)
1045 .unwrap()
1046 .start_to_end_he_id;
1047
1048 let v4_id = meshgraph.add_vertex(Vec3::new(0.2, 0.0, 0.0));
1049 let he_c_4_id = meshgraph
1050 .add_or_get_edge(center_v_id, v4_id)
1051 .unwrap()
1052 .start_to_end_he_id;
1053
1054 let v4p_id = meshgraph.add_vertex(Vec3::new(0.2, 0.0, 0.0));
1055 let AddOrGetEdge {
1056 start_to_end_he_id: he_c_4p_id,
1057 twin_he_id: he_4p_c_id,
1058 ..
1059 } = meshgraph.add_or_get_edge(center_v_id, v4p_id).unwrap();
1060
1061 let v5_id = meshgraph.add_vertex(Vec3::new(1.0, -1.0, 0.0));
1062 let he_c_5_id = meshgraph
1063 .add_or_get_edge(center_v_id, v5_id)
1064 .unwrap()
1065 .start_to_end_he_id;
1066
1067 let v6_id = meshgraph.add_vertex(Vec3::new(1.0, 1.0, 0.0));
1068 let he_c_6_id = meshgraph
1069 .add_or_get_edge(center_v_id, v6_id)
1070 .unwrap()
1071 .start_to_end_he_id;
1072
1073 meshgraph
1074 .add_face_from_halfedges(he_c_1_id, he_c_2_id)
1075 .unwrap();
1076 meshgraph
1077 .add_face_from_halfedges(he_c_2_id, he_c_3_id)
1078 .unwrap();
1079 meshgraph
1080 .add_face_from_halfedges(he_c_3_id, he_c_1p_id)
1081 .unwrap();
1082
1083 meshgraph
1084 .add_face_from_halfedges(he_c_1p_id, he_c_4p_id)
1085 .unwrap();
1086
1087 meshgraph
1088 .add_face_from_halfedges(he_c_4p_id, he_c_5_id)
1089 .unwrap();
1090 meshgraph
1091 .add_face_from_halfedges(he_c_5_id, he_c_6_id)
1092 .unwrap();
1093 meshgraph
1094 .add_face_from_halfedges(he_c_6_id, he_c_4_id)
1095 .unwrap();
1096
1097 meshgraph
1098 .add_face_from_halfedges(he_c_1_id, he_c_4_id)
1099 .unwrap();
1100
1101 meshgraph.halfedges[he_c_1p_id].end_vertex = v1_id;
1102 meshgraph.halfedges[he_c_4p_id].end_vertex = v4_id;
1103
1104 let AddOrGetEdge {
1106 start_to_end_he_id: he_1p_4p_id,
1107 twin_he_id: he_4p_1p_id,
1108 ..
1109 } = meshgraph.add_or_get_edge(v1p_id, v4p_id).unwrap();
1110 meshgraph.halfedges[he_1p_4p_id].end_vertex = v4_id;
1111 meshgraph.halfedges[he_4p_1p_id].end_vertex = v1_id;
1112
1113 let AddOrGetEdge {
1114 start_to_end_he_id: he_3_1p_id,
1115 twin_he_id: he_1p_3_id,
1116 ..
1117 } = meshgraph.add_or_get_edge(v3_id, v1p_id).unwrap();
1118 meshgraph.halfedges[he_3_1p_id].end_vertex = v1_id;
1119
1120 let AddOrGetEdge {
1121 start_to_end_he_id: he_4p_5_id,
1122 twin_he_id: he_5_4p_id,
1123 ..
1124 } = meshgraph.add_or_get_edge(v4p_id, v5_id).unwrap();
1125 meshgraph.halfedges[he_5_4p_id].end_vertex = v4_id;
1126
1127 meshgraph.outgoing_halfedges[v1_id].push(he_1p_4p_id);
1128 meshgraph.outgoing_halfedges[v1_id].push(he_1p_c_id);
1129 meshgraph.outgoing_halfedges[v1_id].push(he_1p_3_id);
1130 meshgraph.outgoing_halfedges[v4_id].push(he_4p_1p_id);
1131 meshgraph.outgoing_halfedges[v4_id].push(he_4p_c_id);
1132 meshgraph.outgoing_halfedges[v4_id].push(he_4p_5_id);
1133
1134 meshgraph.remove_only_vertex(v1p_id);
1135 meshgraph.remove_only_vertex(v4p_id);
1136
1137 #[cfg(feature = "rerun")]
1138 meshgraph.log_rerun();
1139
1140 let mut removed_vertices = vec![];
1141 let mut removed_halfedges = vec![];
1142 let mut removed_faces = vec![];
1143
1144 meshgraph.remove_degenerate_faces(
1145 center_v_id,
1146 &mut removed_vertices,
1147 &mut removed_halfedges,
1148 &mut removed_faces,
1149 );
1150
1151 #[cfg(feature = "rerun")]
1152 {
1153 meshgraph.log_rerun();
1154 crate::RR.flush_blocking().unwrap();
1155 }
1156 }
1157
1158 #[test]
1159 fn test_remove_degenerate_edges() {
1160 crate::utils::get_tracing_subscriber();
1161
1162 let mut meshgraph = MeshGraph::new();
1163
1164 let center_v_id = meshgraph.add_vertex(Vec3::new(0.0, 0.0, 1.0));
1165
1166 let v1_id = meshgraph.add_vertex(Vec3::new(0.0, 0.0, 0.0));
1167 let he_c_1_id = meshgraph
1168 .add_or_get_edge(center_v_id, v1_id)
1169 .unwrap()
1170 .start_to_end_he_id;
1171
1172 let v1p_id = meshgraph.add_vertex(Vec3::new(0.0, 0.0, 0.0));
1173 let AddOrGetEdge {
1174 start_to_end_he_id: he_c_1p_id,
1175 twin_he_id: he_1p_c_id,
1176 ..
1177 } = meshgraph.add_or_get_edge(center_v_id, v1p_id).unwrap();
1178
1179 let v2_id = meshgraph.add_vertex(Vec3::new(-1.0, 1.0, 0.0));
1180 let he_c_2_id = meshgraph
1181 .add_or_get_edge(center_v_id, v2_id)
1182 .unwrap()
1183 .start_to_end_he_id;
1184
1185 let v3_id = meshgraph.add_vertex(Vec3::new(-1.0, -1.0, 0.0));
1186 let he_c_3_id = meshgraph
1187 .add_or_get_edge(center_v_id, v3_id)
1188 .unwrap()
1189 .start_to_end_he_id;
1190
1191 let v5_id = meshgraph.add_vertex(Vec3::new(1.0, -1.0, 0.0));
1192 let he_c_5_id = meshgraph
1193 .add_or_get_edge(center_v_id, v5_id)
1194 .unwrap()
1195 .start_to_end_he_id;
1196
1197 let v6_id = meshgraph.add_vertex(Vec3::new(1.0, 1.0, 0.0));
1198 let he_c_6_id = meshgraph
1199 .add_or_get_edge(center_v_id, v6_id)
1200 .unwrap()
1201 .start_to_end_he_id;
1202
1203 meshgraph
1204 .add_face_from_halfedges(he_c_1_id, he_c_2_id)
1205 .unwrap();
1206 meshgraph
1207 .add_face_from_halfedges(he_c_2_id, he_c_3_id)
1208 .unwrap();
1209 meshgraph
1210 .add_face_from_halfedges(he_c_3_id, he_c_1p_id)
1211 .unwrap();
1212
1213 meshgraph
1214 .add_face_from_halfedges(he_c_1p_id, he_c_5_id)
1215 .unwrap();
1216
1217 meshgraph
1218 .add_face_from_halfedges(he_c_5_id, he_c_6_id)
1219 .unwrap();
1220 meshgraph
1221 .add_face_from_halfedges(he_c_6_id, he_c_1_id)
1222 .unwrap();
1223
1224 meshgraph.halfedges[he_c_1p_id].end_vertex = v1_id;
1225
1226 let AddOrGetEdge {
1228 start_to_end_he_id: he_3_1p_id,
1229 twin_he_id: he_1p_3_id,
1230 ..
1231 } = meshgraph.add_or_get_edge(v3_id, v1p_id).unwrap();
1232 meshgraph.halfedges[he_3_1p_id].end_vertex = v1_id;
1233
1234 let AddOrGetEdge {
1235 start_to_end_he_id: he_1p_5_id,
1236 twin_he_id: he_5_1p_id,
1237 ..
1238 } = meshgraph.add_or_get_edge(v1p_id, v5_id).unwrap();
1239 meshgraph.halfedges[he_5_1p_id].end_vertex = v1_id;
1240
1241 meshgraph.outgoing_halfedges[v1_id].push(he_1p_c_id);
1242 meshgraph.outgoing_halfedges[v1_id].push(he_1p_5_id);
1243 meshgraph.outgoing_halfedges[v1_id].push(he_1p_3_id);
1244
1245 meshgraph.remove_only_vertex(v1p_id);
1246
1247 #[cfg(feature = "rerun")]
1248 meshgraph.log_rerun();
1249
1250 meshgraph.remove_degenerate_edges(center_v_id);
1251
1252 #[cfg(feature = "rerun")]
1253 {
1254 meshgraph.log_rerun();
1255 crate::RR.flush_blocking().unwrap();
1256 }
1257 }
1258
1259 #[test]
1260 fn test_remove_flap() {
1261 crate::utils::get_tracing_subscriber();
1262
1263 let mut mesh_graph = MeshGraph::new();
1264
1265 let v1 = mesh_graph.add_vertex(Vec3::new(0.0, 0.0, 0.0));
1266 let v2 = mesh_graph.add_vertex(Vec3::new(1.0, 0.0, 0.0));
1267 let v3 = mesh_graph.add_vertex(Vec3::new(0.0, 1.0, 0.0));
1268
1269 let v4 = mesh_graph.add_vertex(Vec3::new(1.0, 1.0, 0.5));
1270 let v5 = mesh_graph.add_vertex(Vec3::new(1.0, 1.0, -0.5));
1271
1272 let edge1 = mesh_graph.add_edge(v1, v2).unwrap();
1273 let edge2 = mesh_graph.add_edge(v2, v3).unwrap();
1274 let edge2_d = mesh_graph.add_edge(v2, v3).unwrap();
1275
1276 mesh_graph
1277 .add_face_from_halfedges(edge1.start_to_end_he_id, edge2.start_to_end_he_id)
1278 .unwrap();
1279 mesh_graph
1280 .add_face_from_halfedges(edge2_d.twin_he_id, edge1.twin_he_id)
1281 .unwrap();
1282
1283 mesh_graph
1284 .add_face_from_halfedge_and_vertex(edge2.twin_he_id, v4)
1285 .unwrap();
1286 mesh_graph
1287 .add_face_from_halfedge_and_vertex(edge2_d.start_to_end_he_id, v5)
1288 .unwrap();
1289
1290 #[cfg(feature = "rerun")]
1291 mesh_graph.log_rerun();
1292
1293 let mut removed_vertices = Vec::new();
1294 let mut removed_halfedges = Vec::new();
1295 let mut removed_faces = Vec::new();
1296
1297 mesh_graph.remove_neighboring_flaps(
1298 v1,
1299 &mut removed_vertices,
1300 &mut removed_halfedges,
1301 &mut removed_faces,
1302 );
1303
1304 #[cfg(feature = "rerun")]
1305 {
1306 mesh_graph.log_rerun();
1307 crate::RR.flush_blocking().unwrap();
1308 }
1309
1310 assert_eq!(removed_vertices.len(), 1);
1311 assert_eq!(removed_halfedges.len(), 6);
1312 assert_eq!(removed_faces.len(), 2);
1313 }
1314
1315 #[test]
1316 fn test_remove_double_flaps() {
1317 crate::utils::get_tracing_subscriber();
1318
1319 let mut mesh_graph = MeshGraph::new();
1320
1321 let v1 = mesh_graph.add_vertex(Vec3::new(0.0, 0.0, 0.0));
1322 let v2 = mesh_graph.add_vertex(Vec3::new(1.0, 0.0, 0.0));
1323 let v3 = mesh_graph.add_vertex(Vec3::new(0.0, 1.0, 0.0));
1324 let v4 = mesh_graph.add_vertex(Vec3::new(1.0, 1.0, 0.0));
1325
1326 let v5 = mesh_graph.add_vertex(Vec3::new(0.0, -1.0, 0.0));
1327
1328 let edge1 = mesh_graph.add_edge(v1, v2).unwrap();
1329 let edge1_d = mesh_graph.add_edge(v1, v2).unwrap();
1330 let edge2 = mesh_graph.add_edge(v2, v3).unwrap();
1331 let edge2_d = mesh_graph.add_edge(v2, v3).unwrap();
1332
1333 mesh_graph
1334 .add_face_from_halfedges(edge1.start_to_end_he_id, edge2.start_to_end_he_id)
1335 .unwrap();
1336 mesh_graph
1337 .add_face_from_halfedge_and_vertex(edge2.twin_he_id, v4)
1338 .unwrap();
1339
1340 mesh_graph
1341 .add_face_from_halfedge_and_vertex(edge2_d.start_to_end_he_id, v4)
1342 .unwrap();
1343 mesh_graph
1344 .add_face_from_halfedges(edge2_d.twin_he_id, edge1_d.twin_he_id)
1345 .unwrap();
1346
1347 mesh_graph
1348 .add_face_from_halfedge_and_vertex(edge1_d.start_to_end_he_id, v5)
1349 .unwrap();
1350
1351 #[cfg(feature = "rerun")]
1352 mesh_graph.log_rerun();
1353
1354 let mut removed_vertices = Vec::new();
1355 let mut removed_halfedges = Vec::new();
1356 let mut removed_faces = Vec::new();
1357
1358 mesh_graph.remove_neighboring_flaps(
1359 v1,
1360 &mut removed_vertices,
1361 &mut removed_halfedges,
1362 &mut removed_faces,
1363 );
1364
1365 #[cfg(feature = "rerun")]
1366 {
1367 mesh_graph.log_rerun();
1368 crate::RR.flush_blocking().unwrap();
1369 }
1370
1371 assert_eq!(removed_vertices.len(), 0);
1372 assert_eq!(removed_halfedges.len(), 6);
1373 assert_eq!(removed_faces.len(), 2);
1374 }
1375}