1use glam::Vec3;
2use hashbrown::HashSet;
3use itertools::Itertools;
4use tracing::{error, instrument};
5
6use crate::{Face, FaceId, HalfedgeId, MeshGraph, VertexId, error_none, utils::unwrap_or_return};
7
8impl MeshGraph {
9 #[instrument(skip(self))]
14 pub fn collapse_until_edges_above_min_length(
15 &mut self,
16 min_length_squared: f32,
17 marked_vertices: &mut HashSet<VertexId>,
18 ) {
19 let mut halfedges_to_collapse = self.halfedges_map(|len_sqr| len_sqr < min_length_squared);
20
21 while !halfedges_to_collapse.is_empty() {
22 let mut min_len = f32::MAX;
23 let mut min_he_id = None;
24 let mut min_center = Vec3::ZERO;
25 let mut min_twin_id = HalfedgeId::default();
26 let mut min_start_v_id = VertexId::default();
27 let mut min_end_v_id = VertexId::default();
28
29 for (&he_id, &len) in &halfedges_to_collapse {
30 if len < min_len
31 && let Some((twin_id, start_v_id, end_v_id, center)) =
32 self.can_collapse_edge_inner(he_id)
33 {
34 min_len = len;
35 min_he_id = Some(he_id);
36 min_twin_id = twin_id;
37 min_start_v_id = start_v_id;
38 min_end_v_id = end_v_id;
39 min_center = center;
40 }
41 }
42
43 let Some(min_he_id) = min_he_id else {
44 break;
46 };
47
48 let start_vertex_id = unwrap_or_return!(
49 self.halfedges[min_he_id].start_vertex(self),
50 "Start vertex not found"
51 );
52
53 let collapse_edge_result = self.collapse_edge_inner(
54 min_he_id,
55 min_twin_id,
56 min_start_v_id,
57 min_end_v_id,
58 min_center,
59 );
60
61 let vertex_neighborhoods_to_check = if collapse_edge_result.added_vertices.is_empty() {
62 if collapse_edge_result.removed_halfedges.is_empty() {
63 vec![]
64 } else {
65 vec![start_vertex_id]
66 }
67 } else {
68 marked_vertices.extend(collapse_edge_result.added_vertices.iter().copied());
69
70 let mut neighborhood = collapse_edge_result.added_vertices;
71 neighborhood.push(start_vertex_id);
72
73 neighborhood
74 };
75
76 halfedges_to_collapse.remove(&min_he_id);
77
78 for removed_he_id in collapse_edge_result.removed_halfedges {
79 halfedges_to_collapse.remove(&removed_he_id);
80 }
81
82 let mut halfedges_to_check = HashSet::new();
83
84 for vertex_id in vertex_neighborhoods_to_check {
85 let Some(outgoing_halfedges) = self.outgoing_halfedges.get(vertex_id) else {
86 continue;
88 };
89
90 for &halfedge_id in outgoing_halfedges {
91 let Some(halfedge) = self.halfedges.get(halfedge_id) else {
92 error!("Halfedge not found");
93 continue;
94 };
95
96 let twin_id = unwrap_or_return!(halfedge.twin, "Twin not found");
97
98 halfedges_to_check.insert(halfedge_id.min(twin_id));
99
100 if let Some(face_id) = halfedge.face {
101 if let Some(face) = self.faces.get(face_id) {
102 self.bvh
103 .insert_or_update_partially(face.aabb(self), face.index, 0.0);
104 } else {
105 error!("Face not found. BVH will not be updated.");
106 }
107 }
108 }
109 }
110
111 for he_id in halfedges_to_check {
112 let he = self.halfedges[he_id]; let len_sqr = he.length_squared(self);
115
116 if len_sqr < min_length_squared {
117 halfedges_to_collapse.insert(he_id, len_sqr);
118 } else {
119 halfedges_to_collapse.remove(&he_id);
120 }
121 }
122 }
123
124 #[cfg(feature = "rerun")]
125 self.log_rerun();
126 }
127
128 #[inline]
129 pub fn can_collapse_edge(&mut self, halfedge_id: HalfedgeId) -> bool {
130 self.can_collapse_edge_inner(halfedge_id).is_some()
131 }
132
133 #[instrument(skip(self))]
134 fn can_collapse_edge_inner(
135 &mut self,
136 halfedge_id: HalfedgeId,
137 ) -> Option<(HalfedgeId, VertexId, VertexId, Vec3)> {
138 let he = self
163 .halfedges
164 .get(halfedge_id)
165 .or_else(error_none!("Halfedge not found"))?;
166 let twin_id = he.twin.or_else(error_none!("Twin halfedge not found"))?;
167 let twin = self
168 .halfedges
169 .get(twin_id)
170 .or_else(error_none!("Twin halfedge not found"))?;
171
172 let start_vertex_id = twin.end_vertex;
173 self.vertices
174 .get_mut(start_vertex_id)
175 .or_else(error_none!("Start vertex not found"))?
176 .outgoing_halfedge = Some(halfedge_id);
177
178 let end_vertex_id = he.end_vertex;
179 self.vertices
180 .get_mut(end_vertex_id)
181 .or_else(error_none!("End vertex not found"))?
182 .outgoing_halfedge = Some(twin_id);
183
184 let start_pos = self
185 .positions
186 .get(start_vertex_id)
187 .or_else(error_none!("Start position not found"))?;
188
189 let end_pos = self
190 .positions
191 .get(end_vertex_id)
192 .or_else(error_none!("End position not found"))?;
193
194 let center = (start_pos + end_pos) * 0.5;
195
196 self.check_inverted_faces(start_vertex_id, center)?;
197 self.check_inverted_faces(end_vertex_id, center)?;
198
199 Some((twin_id, start_vertex_id, end_vertex_id, center))
200 }
201
202 fn check_inverted_faces(&self, vertex_id: VertexId, center: Vec3) -> Option<()> {
203 let face_ids = self.vertices[vertex_id].faces(self).skip(2).collect_vec();
205
206 for face_id in face_ids {
207 let mut orig_positions = Vec::with_capacity(3);
208 let mut new_positions = Vec::with_capacity(3);
209
210 let face = self
211 .faces
212 .get(face_id)
213 .or_else(error_none!("Face not found"))?;
214
215 for v_id in face.vertices(self) {
216 let pos = *self
217 .positions
218 .get(v_id)
219 .or_else(error_none!("Vertex pos not found"))?;
220
221 if v_id == vertex_id {
222 new_positions.push(center);
223 } else {
224 new_positions.push(pos);
225 }
226 orig_positions.push(pos);
227 }
228
229 let orig_normal = Face::normal_from_positions(&orig_positions);
230 let new_normal = Face::normal_from_positions(&new_positions);
231
232 if orig_normal.dot(new_normal) < 0.0 {
233 return None;
234 }
235 }
236
237 Some(())
238 }
239
240 #[instrument(skip(self))]
241 pub fn collapse_edge_inner(
242 &mut self,
243 halfedge_id: HalfedgeId,
244 twin_id: HalfedgeId,
245 start_v_id: VertexId,
246 end_v_id: VertexId,
247 center_pos: Vec3,
248 ) -> CollapseEdge {
249 let mut result = CollapseEdge::default();
250
251 #[cfg(feature = "rerun")]
252 {
253 self.log_he_rerun("collapse/he", halfedge_id);
254 }
255 let end_outgoing_halfedges = self.vertices[end_v_id]
258 .outgoing_halfedges(self)
259 .collect::<Vec<_>>();
260 let end_incoming_halfedges = self.vertices[end_v_id]
261 .incoming_halfedges(self)
262 .collect::<Vec<_>>();
263
264 let he = self.halfedges[halfedge_id];
265 let twin = self.halfedges[twin_id];
266
267 if !he.is_boundary() {
268 let (face_id, halfedge_ids) = unwrap_or_return!(
269 self.remove_halfedge_face(halfedge_id),
270 "Could not remove face",
271 result
272 );
273
274 self.remove_outgoing_halfedge(end_v_id, halfedge_ids[0]);
275
276 result.removed_faces.push(face_id);
277 result.removed_halfedges.extend(halfedge_ids);
278 }
279 result.removed_halfedges.push(halfedge_id);
280
281 self.remove_outgoing_halfedge(start_v_id, halfedge_id);
282
283 if !twin.is_boundary() {
284 let (face_id, halfedge_ids) = unwrap_or_return!(
285 self.remove_halfedge_face(twin_id),
286 "Failed to remove halfedge face",
287 result
288 );
289
290 self.remove_outgoing_halfedge(start_v_id, halfedge_ids[0]);
291
292 result.removed_faces.push(face_id);
293 result.removed_halfedges.extend(halfedge_ids);
294 }
295 result.removed_halfedges.push(twin_id);
296
297 self.remove_outgoing_halfedge(end_v_id, twin_id);
298
299 let outgoing_end_halfedges = self
300 .outgoing_halfedges
301 .get(end_v_id)
302 .cloned()
303 .unwrap_or_default();
304 self.outgoing_halfedges
305 .entry(start_v_id)
306 .unwrap() .or_default()
308 .extend(outgoing_end_halfedges);
309
310 for end_incoming_he in end_incoming_halfedges {
311 if let Some(end_incoming_he_mut) = self.halfedges.get_mut(end_incoming_he) {
312 end_incoming_he_mut.end_vertex = start_v_id;
313 }
314 }
315
316 self.remove_only_vertex(end_v_id);
317 result.removed_vertices.push(end_v_id);
318
319 self.halfedges.remove(halfedge_id);
320 self.halfedges.remove(twin_id);
321
322 self.positions[start_v_id] = center_pos;
323
324 let new_outgoing_he_id = end_outgoing_halfedges
325 .into_iter()
326 .find(|he_id| self.halfedges.contains_key(*he_id));
327
328 if let Some(new_outgoing_he_id) = new_outgoing_he_id {
329 self.vertices[start_v_id].outgoing_halfedge = Some(new_outgoing_he_id);
330
331 #[cfg(feature = "rerun")]
332 {
333 self.log_he_rerun(
334 "collapse/outgoing",
335 self.vertices[start_v_id].outgoing_halfedge.unwrap(),
336 );
337 }
338
339 let cleanup = self.make_vertex_neighborhood_manifold(start_v_id);
340
341 result.added_vertices = cleanup.added_vertices;
342 result.removed_vertices.extend(cleanup.removed_vertices);
343 result.removed_halfedges.extend(cleanup.removed_halfedges);
344 result.removed_faces.extend(cleanup.removed_faces);
345 } else {
346 self.remove_only_vertex(start_v_id);
347
348 result.removed_vertices.push(start_v_id);
349 }
350
351 result
352 }
353
354 #[instrument(skip(self))]
363 pub fn collapse_edge(&mut self, halfedge_id: HalfedgeId) -> CollapseEdge {
364 let he = *unwrap_or_return!(
365 self.halfedges.get(halfedge_id),
366 "Halfedge not found",
367 CollapseEdge::default()
368 );
369 let twin_id = unwrap_or_return!(he.twin, "Twin missing", CollapseEdge::default());
370 let twin = unwrap_or_return!(
371 self.halfedges.get(twin_id),
372 "Halfedge not found",
373 CollapseEdge::default()
374 );
375
376 let start_v_id = twin.end_vertex;
377 let end_v_id = he.end_vertex;
378
379 let start_pos = *unwrap_or_return!(
380 self.positions.get(start_v_id),
381 "Start position not found",
382 CollapseEdge::default()
383 );
384 let end_pos = *unwrap_or_return!(
385 self.positions.get(end_v_id),
386 "End position not found",
387 CollapseEdge::default()
388 );
389
390 let center_pos = (start_pos + end_pos) * 0.5;
391
392 self.collapse_edge_inner(halfedge_id, twin_id, start_v_id, end_v_id, center_pos)
393 }
394
395 #[instrument(skip(self))]
398 fn remove_halfedge_face(
399 &mut self,
400 halfedge_id: HalfedgeId,
401 ) -> Option<(FaceId, [HalfedgeId; 2])> {
402 let he = self
403 .halfedges
404 .get(halfedge_id)
405 .or_else(error_none!("Halfedge not found"))?;
406
407 let face_id = he.face.or_else(error_none!("Face not found"))?;
408
409 let next_he_id = he.next.or_else(error_none!("Next halfedge is None"))?;
410 let prev_he_id = he
411 .prev(self)
412 .or_else(error_none!("Previous halfedge is None"))?;
413
414 let next_twin_id = self.halfedges[next_he_id]
415 .twin
416 .or_else(error_none!("Next twin halfedge not found"))?;
417 let prev_twin_id = self.halfedges[prev_he_id]
418 .twin
419 .or_else(error_none!("Previous twin halfedge not found"))?;
420
421 let next_he = self
422 .halfedges
423 .get(next_he_id)
424 .or_else(error_none!("Next halfedge not found"))?;
425 let prev_he = self
426 .halfedges
427 .get(prev_he_id)
428 .or_else(error_none!("Previous halfedge not found"))?;
429
430 let next_end_v_id = next_he.end_vertex;
431 let prev_end_v_id = prev_he.end_vertex;
432
433 self.vertices
434 .get_mut(next_end_v_id)
435 .or_else(error_none!("Next end vertex not found"))?
436 .outgoing_halfedge = next_he.next.or_else(error_none!("Next next is None"));
437 self.vertices
438 .get_mut(prev_end_v_id)
439 .or_else(error_none!("Previous end vertex not found"))?
440 .outgoing_halfedge = prev_he.next.or_else(error_none!("Previous next is None"));
441
442 let prev_start_v_id = prev_he
443 .start_vertex(self)
444 .or_else(error_none!("Previous start vertex ID not found"))?;
445 let prev_start_v = self
446 .vertices
447 .get(prev_start_v_id)
448 .or_else(error_none!("Previous start vertex not found"))?;
449
450 if prev_start_v.outgoing_halfedge == Some(prev_he_id) {
451 self.vertices[prev_start_v_id].outgoing_halfedge = prev_he
452 .ccw_rotated_neighbour(self)
453 .or_else(|| prev_he.cw_rotated_neighbour(self))
454 .or_else(error_none!(
455 "Previous start vertex new outgoing halfedge not found"
456 ));
457 }
458
459 self.bvh.remove(
460 self.faces
461 .get(face_id)
462 .or_else(error_none!("Face not found"))?
463 .index,
464 );
465
466 self.halfedges.remove(next_he_id);
467 self.halfedges.remove(prev_he_id);
468 self.remove_outgoing_halfedge(next_end_v_id, prev_he_id);
469
470 if let Some(face) = self.faces.remove(face_id) {
471 self.bvh.remove(face.index);
472 }
473
474 self.halfedges
475 .get_mut(next_twin_id)
476 .or_else(error_none!("Next twin halfedge not found"))?
477 .twin = Some(prev_twin_id);
478
479 self.halfedges
480 .get_mut(prev_twin_id)
481 .or_else(error_none!("Previous twin halfedge not found"))?
482 .twin = Some(next_twin_id);
483
484 Some((face_id, [next_he_id, prev_he_id]))
485 }
486}
487
488#[derive(Default, Debug)]
489pub struct CollapseEdge {
490 pub removed_vertices: Vec<VertexId>,
491 pub removed_halfedges: Vec<HalfedgeId>,
492 pub removed_faces: Vec<FaceId>,
493
494 pub added_vertices: Vec<VertexId>,
495}
496
497#[cfg(test)]
498mod test {
499 use super::*;
500
501 #[test]
502 #[allow(unused_variables)]
503 fn test_collapse_edge() {
504 let mut mesh_graph = MeshGraph::new();
505
506 let face1 = mesh_graph
507 .add_face_from_positions(
508 Vec3::new(0.0, 0.0, 0.0),
509 Vec3::new(0.0, 4.0, 0.0),
510 Vec3::new(1.0, 2.0, 0.0),
511 )
512 .face_id;
513
514 let he1 = mesh_graph.faces[face1]
515 .halfedges(&mesh_graph)
516 .collect::<Vec<_>>()[1];
517
518 let face2 = mesh_graph
519 .add_face_from_halfedge_and_position(he1, Vec3::new(2.0, 4.0, 0.0))
520 .unwrap()
521 .face_id;
522
523 let he2 = mesh_graph.faces[face2]
524 .halfedges(&mesh_graph)
525 .collect::<Vec<_>>()[2];
526
527 let face3 = mesh_graph
528 .add_face_from_halfedge_and_position(he2, Vec3::new(3.0, 2.0, 0.0))
529 .unwrap()
530 .face_id;
531
532 let he3 = mesh_graph.faces[face3]
533 .halfedges(&mesh_graph)
534 .collect::<Vec<_>>()[1];
535
536 let face4 = mesh_graph
537 .add_face_from_halfedge_and_position(he3, Vec3::new(4.0, 4.0, 0.0))
538 .unwrap()
539 .face_id;
540
541 let he4 = mesh_graph.faces[face4]
542 .halfedges(&mesh_graph)
543 .nth(2)
544 .unwrap();
545
546 let face5 = mesh_graph
547 .add_face_from_halfedge_and_position(he4, Vec3::new(4.0, 0.0, 0.0))
548 .unwrap()
549 .face_id;
550
551 let he5 = mesh_graph.faces[face5]
552 .halfedges(&mesh_graph)
553 .nth(2)
554 .unwrap();
555
556 let face6 = mesh_graph
557 .add_face_from_halfedge_and_position(he5, Vec3::new(2.0, 0.0, 0.0))
558 .unwrap()
559 .face_id;
560
561 let he6 = mesh_graph.faces[face6]
562 .halfedges(&mesh_graph)
563 .nth(2)
564 .unwrap();
565
566 let he3 = mesh_graph.faces[face3]
567 .halfedges(&mesh_graph)
568 .nth(2)
569 .unwrap();
570
571 let face7 = mesh_graph
572 .add_face_from_halfedges(he6, he3)
573 .unwrap()
574 .face_id;
575
576 let he7 = mesh_graph.faces[face7]
577 .halfedges(&mesh_graph)
578 .next()
579 .unwrap();
580
581 let he1 = mesh_graph.faces[face1]
582 .halfedges(&mesh_graph)
583 .nth(2)
584 .unwrap();
585
586 mesh_graph.add_face_from_halfedges(he1, he7).unwrap();
587
588 assert_eq!(mesh_graph.vertices.len(), 8);
589 assert_eq!(mesh_graph.halfedges.len(), 30);
590 assert_eq!(mesh_graph.faces.len(), 8);
591
592 #[cfg(feature = "rerun")]
593 mesh_graph.log_rerun();
594
595 let edge_to_collapse = mesh_graph.faces[face3]
596 .halfedges(&mesh_graph)
597 .nth(2)
598 .unwrap();
599
600 let start_v_id = mesh_graph.halfedges[edge_to_collapse]
601 .start_vertex(&mesh_graph)
602 .unwrap();
603
604 assert_eq!(mesh_graph.outgoing_halfedges[start_v_id].len(), 5);
605
606 let CollapseEdge {
607 removed_vertices,
608 removed_halfedges,
609 removed_faces,
610 added_vertices,
611 } = mesh_graph.collapse_edge(edge_to_collapse);
612
613 #[cfg(feature = "rerun")]
614 {
615 mesh_graph.log_rerun();
616 crate::RR.flush_blocking().unwrap();
617 }
618
619 assert_eq!(removed_vertices.len(), 1);
620 assert_eq!(removed_halfedges.len(), 6);
621 assert_eq!(removed_faces.len(), 2);
622
623 assert_eq!(mesh_graph.vertices.len(), 7);
624 assert_eq!(mesh_graph.halfedges.len(), 24);
625 assert_eq!(mesh_graph.faces.len(), 6);
626
627 assert_eq!(mesh_graph.outgoing_halfedges[start_v_id].len(), 6);
628 }
629
630 #[cfg(feature = "gltf")]
631 #[test]
632 fn test_can_collapse_edge() {
633 use crate::{integrations::gltf, utils::get_tracing_subscriber};
634
635 get_tracing_subscriber();
636 let mut meshgraph = gltf::load("src/ops/glb/can_collapse_edge.glb").unwrap();
637
638 #[cfg(feature = "rerun")]
639 meshgraph.log_rerun();
640
641 let mut v_top_id = VertexId::default();
642 let mut v_bottom_id = VertexId::default();
643
644 for (v_id, pos) in &meshgraph.positions {
645 if pos.x == 0.0 {
646 if pos.y == -1.0 {
647 v_top_id = v_id;
648 } else if pos.y == 1.0 {
649 v_bottom_id = v_id;
650 }
651 }
652 }
653
654 if v_top_id == VertexId::default() {
655 panic!("No top vertex found");
656 }
657
658 if v_bottom_id == VertexId::default() {
659 panic!("No bottom vertex found");
660 }
661
662 let he_id = meshgraph.halfedge_from_to(v_top_id, v_bottom_id).unwrap();
663
664 let result = meshgraph.can_collapse_edge(he_id);
665
666 assert!(result);
667
668 #[cfg(feature = "rerun")]
669 crate::RR.flush_blocking().unwrap();
670 }
671
672 #[cfg(feature = "gltf")]
673 #[test]
674 fn test_cannot_collapse_edge() {
675 use crate::{integrations::gltf, utils::get_tracing_subscriber};
676
677 get_tracing_subscriber();
678 let mut meshgraph = gltf::load("src/ops/glb/cannot_collapse_edge.glb").unwrap();
679
680 #[cfg(feature = "rerun")]
681 meshgraph.log_rerun();
682
683 let mut v_top_id = VertexId::default();
684 let mut v_bottom_id = VertexId::default();
685
686 for (v_id, pos) in &meshgraph.positions {
687 if pos.x == 0.0 {
688 if pos.y == -1.0 {
689 v_top_id = v_id;
690 } else if pos.y == 1.0 {
691 v_bottom_id = v_id;
692 }
693 }
694 }
695
696 if v_top_id == VertexId::default() {
697 panic!("No top vertex found");
698 }
699
700 if v_bottom_id == VertexId::default() {
701 panic!("No bottom vertex found");
702 }
703
704 let he_id = meshgraph.halfedge_from_to(v_top_id, v_bottom_id).unwrap();
705
706 let result = meshgraph.can_collapse_edge(he_id);
707
708 assert!(!result);
709
710 #[cfg(feature = "rerun")]
711 crate::RR.flush_blocking().unwrap();
712 }
713}