mesh_graph/ops/
collapse.rs

1use glam::Vec3;
2use hashbrown::{HashMap, HashSet};
3use itertools::Itertools;
4use tracing::{error, instrument};
5
6use crate::{
7    FaceId, HalfedgeId, MeshGraph, Selection, SelectionOps, VertexId, error_none,
8    utils::unwrap_or_return,
9};
10
11impl MeshGraph {
12    /// Collapses edges until all edges have a length above the minimum length.
13    ///
14    /// This will schedule necessary updates to the BVH but you have to call
15    /// `refit()` after the operation.
16    #[instrument(skip(self))]
17    pub fn collapse_until_edges_above_min_length(
18        &mut self,
19        min_length_squared: f32,
20        selection: &mut Selection,
21    ) {
22        let mut dedup_halfedges = HashSet::new();
23
24        for he in selection.resolve_to_halfedges(self) {
25            let twin = self
26                .halfedges
27                .get(he)
28                .or_else(error_none!("Halfedge not found"))
29                .and_then(|he| he.twin.or_else(error_none!("Twin missing")));
30            let twin_already_in = twin
31                .map(|twin| dedup_halfedges.contains(&twin))
32                .unwrap_or_default();
33
34            if !twin_already_in {
35                dedup_halfedges.insert(he);
36            }
37        }
38
39        let mut halfedges_to_collapse = dedup_halfedges
40            .into_iter()
41            .filter_map(|he| {
42                let len = self.halfedges[he].length_squared(self);
43                (len < min_length_squared).then_some((he, len))
44            })
45            .collect::<HashMap<_, _>>();
46
47        while !halfedges_to_collapse.is_empty() {
48            let mut min_len = f32::MAX;
49            let mut min_he_id = None;
50
51            for (&he_id, &len) in &halfedges_to_collapse {
52                if len < min_len && self.can_collapse_edge(he_id, min_length_squared) {
53                    min_len = len;
54                    min_he_id = Some(he_id);
55                }
56            }
57
58            if min_he_id.is_none() {
59                // couldn't find a valid halfedge to collapse
60                break;
61            }
62            let min_he_id = min_he_id.unwrap();
63
64            let start_vertex = self.halfedges[min_he_id].start_vertex(self);
65
66            let (verts, halfedges, faces) = self.collapse_edge(min_he_id);
67
68            #[cfg(feature = "rerun")]
69            {
70                crate::RR
71                    .log("meshgraph/face/collapse", &rerun::Clear::recursive())
72                    .unwrap();
73                crate::RR
74                    .log("meshgraph/halfedge/collapse", &rerun::Clear::recursive())
75                    .unwrap();
76                crate::RR
77                    .log("meshgraph/vertex/collapse", &rerun::Clear::recursive())
78                    .unwrap();
79                self.log_rerun();
80            }
81
82            halfedges_to_collapse.remove(&min_he_id);
83
84            for vert in verts {
85                selection.remove(vert);
86            }
87            for halfedge in halfedges {
88                selection.remove(halfedge);
89                halfedges_to_collapse.remove(&halfedge);
90            }
91            for face in faces {
92                selection.remove(face);
93            }
94
95            if let Some(start_vertex) = start_vertex {
96                let outgoing_halfedges = self
97                    .vertices
98                    .get(start_vertex)
99                    .or_else(error_none!("Vertex for start vertex not found"))
100                    .map(|vertex| vertex.outgoing_halfedges(self).collect::<Vec<_>>())
101                    .unwrap_or_default();
102
103                for halfedge_id in outgoing_halfedges {
104                    if let Some(halfedge) = self.halfedges.get(halfedge_id) {
105                        let len = halfedge.length_squared(self);
106
107                        if len < min_length_squared {
108                            halfedges_to_collapse.insert(halfedge_id, len);
109                        } else {
110                            halfedges_to_collapse.remove(&halfedge_id);
111                        }
112
113                        if let Some(twin) = halfedge.twin {
114                            halfedges_to_collapse.remove(&twin);
115                        }
116
117                        if let Some(face_id) = halfedge.face {
118                            if let Some(face) = self.faces.get(face_id) {
119                                self.bvh.insert_or_update_partially(
120                                    face.aabb(self),
121                                    face.index,
122                                    0.0,
123                                );
124                            } else {
125                                error!("Face not found. BVH will not be updated.");
126                            }
127                        }
128                        selection.insert(halfedge_id);
129                    } else {
130                        error!("Halfedge not found");
131                    }
132                }
133            } else {
134                error!("Start vertex not found")
135            }
136
137            #[cfg(feature = "rerun")]
138            {
139                self.log_rerun();
140            }
141        }
142    }
143
144    #[inline]
145    fn can_collapse_edge(&mut self, halfedge_id: HalfedgeId, min_length_squared: f32) -> bool {
146        self.can_collapse_edge_inner(halfedge_id, min_length_squared)
147            .is_some()
148    }
149
150    #[instrument(skip(self))]
151    fn can_collapse_edge_inner(
152        &mut self,
153        halfedge_id: HalfedgeId,
154        min_length_squared: f32,
155    ) -> Option<()> {
156        // TODO : consider boundary edge
157        //
158        //          end_vertex
159        //  .            .            .
160        // ( ) ◀─────── ( ) ───────▶ ( )
161        //  '      3     '     2      '
162        //            ╱ ▲ │ ╲
163        //          4╱  │ │  ╲1
164        //          ╱   │ │0  ╲
165        //         ╱    │ │    ╲
166        //        ▼     │ │     ▼
167        //       .      │ │      .
168        //      ( )   he│ │twin ( )
169        //       '      │ │      '
170        //        ▲     │ │     ▲
171        //         ╲    │ │    ╱
172        //          ╲  0│ │   ╱
173        //          1╲  │ │  ╱4
174        //            ╲ │ ▼ ╱
175        //  .      2     .     3      .
176        // ( ) ◀─────── ( ) ───────▶ ( )
177        //  '            '            '
178        //         start_vertex
179
180        let he = self
181            .halfedges
182            .get(halfedge_id)
183            .or_else(error_none!("Halfedge not found"))?;
184
185        let start_vertex_id = he.start_vertex(self)?;
186        self.vertices
187            .get_mut(start_vertex_id)
188            .or_else(error_none!("Start vertex not found"))?
189            .outgoing_halfedge = Some(halfedge_id);
190
191        // just made sure that this exists
192        let start_outgoing_vertex_ids = self.vertices[start_vertex_id]
193            .outgoing_halfedges(self)
194            .skip(2)
195            .filter_map(|he_id| {
196                Some(
197                    self.halfedges
198                        .get(he_id)
199                        .or_else(error_none!("Halfedge not found"))?
200                        .end_vertex,
201                )
202            })
203            .collect_vec();
204
205        let twin_id = he.twin.or_else(error_none!("Twin halfedge not found"))?;
206
207        let end_vertex_id = he.end_vertex;
208        self.vertices
209            .get_mut(end_vertex_id)
210            .or_else(error_none!("End vertex not found"))?
211            .outgoing_halfedge = Some(twin_id);
212
213        // just made sure that this exists
214        let end_outgoing_vertex_ids = self.vertices[end_vertex_id]
215            .outgoing_halfedges(self)
216            .skip(2)
217            .filter_map(|he_id| {
218                Some(
219                    self.halfedges
220                        .get(he_id)
221                        .or_else(error_none!("Halfedge not found"))?
222                        .end_vertex,
223                )
224            })
225            .collect_vec();
226
227        let neighbours = start_outgoing_vertex_ids
228            .into_iter()
229            .chain(end_outgoing_vertex_ids)
230            .collect_vec();
231
232        if neighbours.is_empty() {
233            error!("Vertex has no neighbours");
234            return None;
235        }
236
237        let start_pos = self
238            .positions
239            .get(start_vertex_id)
240            .or_else(error_none!("Start position not found"))?;
241
242        let end_pos = self
243            .positions
244            .get(end_vertex_id)
245            .or_else(error_none!("End position not found"))?;
246
247        let center = (start_pos + end_pos) * 0.5;
248
249        let mut prev_diff = (self
250            .positions
251            .get(neighbours[neighbours.len() - 1])
252            .or_else(error_none!("Last neighbour position not found"))?
253            - center)
254            .normalize();
255
256        let mut normal = Vec3::ZERO;
257
258        if let Some(face_id) = he.face {
259            let face = self
260                .faces
261                .get(face_id)
262                .or_else(error_none!("Face not found"))?;
263            normal += face.normal(self)?;
264        }
265
266        if let Some(face_id) = self
267            .halfedges
268            .get(twin_id)
269            .or_else(error_none!("Face not found"))?
270            .face
271        {
272            let face = self
273                .faces
274                .get(face_id)
275                .or_else(error_none!("Face not found"))?;
276            normal += face.normal(self)?;
277        }
278
279        if normal == Vec3::ZERO {
280            error!("Halfedge as no adjacent faces");
281            return None;
282        }
283
284        normal = normal.normalize();
285
286        // give a bit of leeway to the length of the neighbour edge
287        let max_length_squared = min_length_squared * 5.0;
288
289        for neighbour_id in neighbours {
290            let diff = self
291                .positions
292                .get(neighbour_id)
293                .or_else(error_none!("Neighbour position not found"))?
294                - center;
295
296            if diff.length_squared() > max_length_squared {
297                // neighbour edge would be too long
298                return None;
299            }
300
301            if diff.normalize().cross(prev_diff).dot(normal) < 0.1 {
302                // degenerate or reversed triangle
303                return None;
304            }
305
306            prev_diff = diff;
307        }
308
309        Some(())
310    }
311
312    /// Collapse an edge in the mesh graph.
313    ///
314    /// This moves the start vertex of the edge to the center of the edge
315    /// and removes the end vertex and the adjacent and opposite faces.
316    ///
317    /// It also performs a cleanup afterwards to remove flaps (faces that share the same vertices).
318    ///
319    /// Returns the vertices, halfedges and faces that were removed.
320    #[instrument(skip(self))]
321    pub fn collapse_edge(
322        &mut self,
323        halfedge_id: HalfedgeId,
324    ) -> (Vec<VertexId>, Vec<HalfedgeId>, Vec<FaceId>) {
325        #[cfg(feature = "rerun")]
326        {
327            self.log_he_rerun("collapse/he", halfedge_id);
328        }
329
330        // TODO : consider border vertices
331
332        let mut removed_vertices = Vec::new();
333        let mut removed_halfedges = Vec::new();
334        let mut removed_faces = Vec::new();
335
336        let he = *unwrap_or_return!(
337            self.halfedges.get(halfedge_id),
338            "Halfedge not found",
339            (removed_vertices, removed_halfedges, removed_faces)
340        );
341
342        let start_vert_id = unwrap_or_return!(
343            he.start_vertex(self),
344            "Start vertex not found",
345            (removed_vertices, removed_halfedges, removed_faces)
346        );
347        let end_vert_id = he.end_vertex;
348
349        #[cfg(feature = "rerun")]
350        {
351            self.log_he_rerun("collapse/remove_collapsed", halfedge_id);
352            if let Some(twin) = he.twin {
353                self.log_he_rerun("collapse/remove_twin", twin);
354            }
355            self.log_vert_rerun("collapse/remove_end", end_vert_id);
356        }
357
358        let end_outgoing_halfedges = self.vertices[end_vert_id]
359            .outgoing_halfedges(self)
360            .collect::<Vec<_>>();
361        let end_incoming_halfedges = self.vertices[end_vert_id]
362            .incoming_halfedges(self)
363            .collect::<Vec<_>>();
364
365        let start_pos = *unwrap_or_return!(
366            self.positions.get(start_vert_id),
367            "Start position not found",
368            (removed_vertices, removed_halfedges, removed_faces)
369        );
370        let end_pos = *unwrap_or_return!(
371            self.positions.get(end_vert_id),
372            "End position not found",
373            (removed_vertices, removed_halfedges, removed_faces)
374        );
375
376        let center_pos = (start_pos + end_pos) * 0.5;
377
378        if !he.is_boundary() {
379            let (face_id, halfedges) = unwrap_or_return!(
380                self.remove_halfedge_face(halfedge_id),
381                "Could not remove face",
382                (removed_vertices, removed_halfedges, removed_faces)
383            );
384            removed_faces.push(face_id);
385            removed_halfedges.extend(halfedges);
386        }
387        removed_halfedges.push(halfedge_id);
388
389        let twin_id = unwrap_or_return!(
390            he.twin,
391            "Twin missing",
392            (removed_vertices, removed_halfedges, removed_faces)
393        );
394        let twin = unwrap_or_return!(
395            self.halfedges.get(twin_id),
396            "Halfedge not found",
397            (removed_vertices, removed_halfedges, removed_faces)
398        );
399
400        if !twin.is_boundary() {
401            let (face_id, halfedges) = unwrap_or_return!(
402                self.remove_halfedge_face(twin_id),
403                "Failed to remove halfedge face",
404                (removed_vertices, removed_halfedges, removed_faces)
405            );
406
407            removed_faces.push(face_id);
408            removed_halfedges.extend(halfedges);
409        }
410        removed_halfedges.push(twin_id);
411
412        for end_incoming_he in end_incoming_halfedges {
413            if let Some(end_incoming_he_mut) = self.halfedges.get_mut(end_incoming_he) {
414                end_incoming_he_mut.end_vertex = start_vert_id;
415            }
416        }
417
418        self.vertices.remove(end_vert_id);
419        self.positions.remove(end_vert_id);
420        if let Some(normals) = &mut self.vertex_normals {
421            normals.remove(end_vert_id);
422        }
423        self.halfedges.remove(halfedge_id);
424        self.halfedges.remove(twin_id);
425        removed_halfedges.push(twin_id);
426
427        self.positions[start_vert_id] = center_pos;
428
429        self.vertices[start_vert_id].outgoing_halfedge = end_outgoing_halfedges
430            .into_iter()
431            .find(|he_id| self.halfedges.contains_key(*he_id))
432            .or_else(error_none!("No new outgoing halfedge found"));
433
434        #[cfg(feature = "rerun")]
435        {
436            self.log_he_rerun(
437                "collapse/outgoing",
438                self.vertices[start_vert_id].outgoing_halfedge.unwrap(),
439            );
440        }
441
442        removed_vertices.push(end_vert_id);
443
444        // Remove flaps (= faces that share all their vertices)
445        let mut face_tuples: Vec<_> = unwrap_or_return!(
446            self.vertices.get(start_vert_id),
447            "Start vertex not found",
448            (removed_vertices, removed_halfedges, removed_faces)
449        )
450        .faces(self)
451        .collect::<Vec<_>>()
452        .into_iter()
453        .circular_tuple_windows()
454        .collect();
455
456        let mut first = true;
457
458        while let Some((face_id1, face_id2)) = face_tuples.pop() {
459            #[cfg(feature = "rerun")]
460            {
461                self.log_rerun();
462            }
463
464            if self.faces_share_all_vertices(face_id1, face_id2) {
465                #[cfg(feature = "rerun")]
466                {
467                    if self.vertices[start_vert_id].faces(self).count() == 1 {
468                        self.log_vert_rerun("cleanup_delete", start_vert_id);
469                    }
470
471                    self.log_face_rerun("cleanup_delete/1", face_id1);
472                    self.log_face_rerun("cleanup_delete/2", face_id2);
473                }
474
475                let mut halfedges_of_faces = HashSet::new();
476                // face existence already checked by `self.faces_share_all_vertices`
477                halfedges_of_faces.extend(self.faces[face_id1].halfedges(self));
478                halfedges_of_faces.extend(self.faces[face_id2].halfedges(self));
479
480                let (vs, hes) = self.delete_face(face_id1);
481                for he_id in &hes {
482                    halfedges_of_faces.remove(he_id);
483                }
484                removed_vertices.extend(vs);
485                removed_halfedges.extend(hes);
486                removed_faces.push(face_id1);
487
488                let (vs, hes) = self.delete_face(face_id2);
489                for he_id in &hes {
490                    halfedges_of_faces.remove(he_id);
491                }
492                removed_vertices.extend(vs);
493                removed_halfedges.extend(hes);
494                removed_faces.push(face_id2);
495
496                // the twin edges of the neighboring faces of the deleted faces are still there
497                // we need to remove them and re-connect (twin) their twin edges
498                debug_assert_eq!(halfedges_of_faces.len(), 2);
499                let mut halfedges_of_faces = halfedges_of_faces.into_iter();
500                let he_id1 = halfedges_of_faces.next().unwrap();
501                let he_id2 = halfedges_of_faces.next().unwrap();
502
503                let he1 = unwrap_or_return!(
504                    self.halfedges.get(he_id1),
505                    "Halfedge 1 missing",
506                    (removed_vertices, removed_halfedges, removed_faces)
507                );
508                let twin_id1 = unwrap_or_return!(
509                    he1.twin,
510                    "Twin 1 missing",
511                    (removed_vertices, removed_halfedges, removed_faces)
512                );
513                let he2 = unwrap_or_return!(
514                    self.halfedges.get(he_id2),
515                    "Halfedge 2 missing",
516                    (removed_vertices, removed_halfedges, removed_faces)
517                );
518                let twin_id2 = unwrap_or_return!(
519                    he2.twin,
520                    "Twin 2 missing",
521                    (removed_vertices, removed_halfedges, removed_faces)
522                );
523
524                {
525                    let twin1 = unwrap_or_return!(
526                        self.halfedges.get_mut(twin_id1),
527                        "Twin 1 missing",
528                        (removed_vertices, removed_halfedges, removed_faces)
529                    );
530                    twin1.twin = Some(twin_id2);
531                }
532
533                {
534                    let twin2 = unwrap_or_return!(
535                        self.halfedges.get_mut(twin_id2),
536                        "Twin 2 missing",
537                        (removed_vertices, removed_halfedges, removed_faces)
538                    );
539                    twin2.twin = Some(twin_id1);
540                }
541
542                self.halfedges.remove(he_id1);
543                self.halfedges.remove(he_id2);
544                removed_halfedges.push(he_id1);
545                removed_halfedges.push(he_id2);
546
547                let new_outgoing_he_id = if self.halfedges[twin_id1].end_vertex == start_vert_id {
548                    twin_id2
549                } else {
550                    twin_id1
551                };
552
553                // Also update both vertices of the deleted halfedges
554                unwrap_or_return!(
555                    self.vertices.get_mut(start_vert_id),
556                    "Start vertex not found",
557                    (removed_vertices, removed_halfedges, removed_faces)
558                )
559                .outgoing_halfedge = Some(new_outgoing_he_id);
560
561                let new_outgoing_he = unwrap_or_return!(
562                    self.halfedges.get(new_outgoing_he_id),
563                    "New outgoing halfedge not found",
564                    (removed_vertices, removed_halfedges, removed_faces)
565                );
566                unwrap_or_return!(
567                    self.vertices.get_mut(new_outgoing_he.end_vertex),
568                    "End vertex not found",
569                    (removed_vertices, removed_halfedges, removed_faces)
570                )
571                .outgoing_halfedge = new_outgoing_he
572                    .twin
573                    .or_else(error_none!("New outgoing twin missing"));
574
575                // the next tuple contains one of the removed faces, so re remove it
576                face_tuples.pop();
577
578                // tuples wrap around so we need to remove the first element if the last element was removed
579                if first {
580                    face_tuples.remove(0);
581                }
582            }
583
584            first = false;
585        }
586
587        (removed_vertices, removed_halfedges, removed_faces)
588    }
589
590    /// Remove a halfedge face and re-connecting the adjacent halfedges.
591    /// Only works on manifold triangle meshes.
592    #[instrument(skip(self))]
593    fn remove_halfedge_face(
594        &mut self,
595        halfedge_id: HalfedgeId,
596    ) -> Option<(FaceId, [HalfedgeId; 2])> {
597        let he = self
598            .halfedges
599            .get(halfedge_id)
600            .or_else(error_none!("Halfedge not found"))?;
601
602        let face_id = he.face.or_else(error_none!("Face not found"))?;
603
604        let next_he_id = he.next.or_else(error_none!("Next halfedge is None"))?;
605        let prev_he_id = he
606            .prev(self)
607            .or_else(error_none!("Previous halfedge is None"))?;
608
609        let next_twin_id = self.halfedges[next_he_id]
610            .twin
611            .or_else(error_none!("Next twin halfedge not found"))?;
612        let prev_twin_id = self.halfedges[prev_he_id]
613            .twin
614            .or_else(error_none!("Previous twin halfedge not found"))?;
615
616        let next_he = self
617            .halfedges
618            .get(next_he_id)
619            .or_else(error_none!("Next halfedge not found"))?;
620        let prev_he = self
621            .halfedges
622            .get(prev_he_id)
623            .or_else(error_none!("Previous halfedge not found"))?;
624
625        let next_end_v_id = next_he.end_vertex;
626        let prev_end_v_id = prev_he.end_vertex;
627
628        self.vertices
629            .get_mut(next_end_v_id)
630            .or_else(error_none!("Next end vertex not found"))?
631            .outgoing_halfedge = next_he.next.or_else(error_none!("Next next is None"));
632        self.vertices
633            .get_mut(prev_end_v_id)
634            .or_else(error_none!("Previous end vertex not found"))?
635            .outgoing_halfedge = prev_he.next.or_else(error_none!("Previous next is None"));
636
637        let prev_start_v_id = prev_he
638            .start_vertex(self)
639            .or_else(error_none!("Previous start vertex ID not found"))?;
640        let prev_start_v = self
641            .vertices
642            .get(prev_start_v_id)
643            .or_else(error_none!("Previous start vertex not found"))?;
644
645        if prev_start_v.outgoing_halfedge == Some(prev_he_id) {
646            self.vertices[prev_start_v_id].outgoing_halfedge = prev_he
647                .ccw_rotated_neighbour(self)
648                .or_else(|| prev_he.cw_rotated_neighbour(self))
649                .or_else(error_none!(
650                    "Previous start vertex new outgoing halfedge not found"
651                ));
652        }
653
654        #[cfg(feature = "rerun")]
655        {
656            self.log_face_rerun("collapse/remove_face", face_id);
657            self.log_he_rerun("collapse/remove_next", next_he_id);
658            self.log_he_rerun("collapse/remove_prev", prev_he_id);
659        }
660
661        self.bvh.remove(
662            self.faces
663                .get(face_id)
664                .or_else(error_none!("Face not found"))?
665                .index,
666        );
667
668        self.halfedges.remove(next_he_id);
669        self.halfedges.remove(prev_he_id);
670        self.faces.remove(face_id);
671
672        self.halfedges
673            .get_mut(next_twin_id)
674            .or_else(error_none!("Next twin halfedge not found"))?
675            .twin = Some(prev_twin_id);
676
677        self.halfedges
678            .get_mut(prev_twin_id)
679            .or_else(error_none!("Previous twin halfedge not found"))?
680            .twin = Some(next_twin_id);
681
682        Some((face_id, [next_he_id, prev_he_id]))
683    }
684}
685
686// #[cfg(test)]
687// mod test {
688//     use super::*;
689
690//     #[test]
691//     fn test_collapse() {
692//         let mut mesh_graph = MeshGraph::new();
693
694//         mesh_graph.log_rerun();
695//     }
696// }