mesh_graph/ops/
collapse.rs

1use hashbrown::{HashMap, HashSet};
2use itertools::Itertools;
3
4use crate::{FaceId, HalfedgeId, MeshGraph, Selection, SelectionOps, VertexId};
5
6impl MeshGraph {
7    /// Collapses edges until all edges have a length above the minimum length.
8    ///
9    /// This will schedule necessary updates to the QBVH but you have to call
10    /// `refit()` and maybe `rebalance()` after the operation.
11    pub fn collapse_until_edges_above_min_length(
12        &mut self,
13        min_length_squared: f32,
14        selection: &mut Selection,
15    ) {
16        let mut dedup_halfedges = HashSet::new();
17
18        for he in selection.resolve_to_halfedges(self) {
19            let twin = self.halfedges[he].twin;
20            let twin_already_in = twin
21                .map(|twin| dedup_halfedges.contains(&twin))
22                .unwrap_or_default();
23
24            if !twin_already_in {
25                dedup_halfedges.insert(he);
26            }
27        }
28
29        let mut halfedges_to_collapse = dedup_halfedges
30            .into_iter()
31            .filter_map(|he| {
32                let len = self.halfedges[he].length_squared(self);
33                (len < min_length_squared).then_some((he, len))
34            })
35            .collect::<HashMap<_, _>>();
36
37        while !halfedges_to_collapse.is_empty() {
38            let mut min_len = f32::MAX;
39            let mut min_he = HalfedgeId::default();
40
41            for (&he, &len) in &halfedges_to_collapse {
42                if len < min_len {
43                    min_len = len;
44                    min_he = he;
45                }
46            }
47
48            let start_vertex = self.halfedges[min_he].start_vertex(self);
49
50            let (verts, halfedges, faces) = self.collapse_edge(min_he);
51
52            #[cfg(feature = "rerun")]
53            {
54                crate::RR
55                    .log("meshgraph/face/disolve", &rerun::Clear::recursive())
56                    .unwrap();
57                crate::RR
58                    .log("meshgraph/halfedge/disolve", &rerun::Clear::recursive())
59                    .unwrap();
60                crate::RR
61                    .log("meshgraph/vertex/disolve", &rerun::Clear::recursive())
62                    .unwrap();
63                self.log_rerun();
64            }
65
66            halfedges_to_collapse.remove(&min_he);
67
68            for vert in verts {
69                selection.remove(vert);
70            }
71            for halfedge in halfedges {
72                selection.remove(halfedge);
73                halfedges_to_collapse.remove(&halfedge);
74            }
75            for face in faces {
76                selection.remove(face);
77            }
78
79            for halfedge_id in self.vertices[start_vertex].outgoing_halfedges(self) {
80                let halfedge = &self.halfedges[halfedge_id];
81
82                let len = halfedge.length_squared(self);
83
84                if len < min_length_squared {
85                    halfedges_to_collapse.insert(halfedge_id, len);
86                } else {
87                    halfedges_to_collapse.remove(&halfedge_id);
88                }
89
90                if let Some(twin) = halfedge.twin {
91                    halfedges_to_collapse.remove(&twin);
92                }
93
94                if let Some(face) = halfedge.face {
95                    self.qbvh.pre_update_or_insert(self.faces[face]);
96                }
97                selection.insert(halfedge_id);
98            }
99
100            #[cfg(feature = "rerun")]
101            {
102                self.log_rerun();
103            }
104        }
105    }
106
107    /// Disolve an edge in the mesh graph.
108    ///
109    /// This moves the start vertex of the edge to the center of the edge
110    /// and removes the end vertex and the adjacent and opposite faces.
111    /// Returns the vertex, halfedges and faces that were removed.
112    pub fn collapse_edge(
113        &mut self,
114        halfedge_id: HalfedgeId,
115    ) -> (Vec<VertexId>, Vec<HalfedgeId>, Vec<FaceId>) {
116        #[cfg(feature = "rerun")]
117        {
118            self.log_he_rerun("disolve/he", halfedge_id);
119        }
120
121        // TODO : consider border vertices
122
123        let mut removed_halfedges = Vec::new();
124        let mut removed_faces = Vec::new();
125
126        let he = self.halfedges[halfedge_id];
127
128        let start_vert = he.start_vertex(self);
129        let end_vert = he.end_vertex;
130
131        #[cfg(feature = "rerun")]
132        {
133            self.log_he_rerun("disolve/remove_disolved", halfedge_id);
134            self.log_he_rerun("disolve/remove_twin", he.twin());
135            self.log_vert_rerun("disolve/remove_end", end_vert);
136        }
137
138        let end_outgoing_halfedges = self.vertices[end_vert].outgoing_halfedges(self);
139        let end_incoming_halfedges = self.vertices[end_vert].incoming_halfedges(self);
140
141        let center = (self.positions[start_vert] + self.positions[end_vert]) * 0.5;
142
143        let (face_id, halfedges) = self.remove_halfedge_face(halfedge_id);
144        removed_faces.push(face_id);
145        removed_halfedges.extend(halfedges);
146        removed_halfedges.push(halfedge_id);
147
148        let twin = he.twin();
149        if !self.halfedges[twin].is_boundary() {
150            let (face_id, halfedges) = self.remove_halfedge_face(twin);
151            removed_faces.push(face_id);
152            removed_halfedges.extend(halfedges);
153        }
154        removed_halfedges.push(twin);
155
156        for end_incoming_he in end_incoming_halfedges {
157            if let Some(end_incoming_he_mut) = self.halfedges.get_mut(end_incoming_he) {
158                end_incoming_he_mut.end_vertex = start_vert;
159            }
160        }
161
162        self.vertices.remove(end_vert);
163        self.positions.remove(end_vert);
164        if let Some(normals) = &mut self.vertex_normals {
165            normals.remove(end_vert);
166        }
167        self.halfedges.remove(halfedge_id);
168        self.halfedges.remove(twin);
169        removed_halfedges.push(twin);
170
171        self.positions[start_vert] = center;
172
173        self.vertices[start_vert].outgoing_halfedge = end_outgoing_halfedges
174            .into_iter()
175            .find(|he_id| self.halfedges.contains_key(*he_id));
176
177        #[cfg(feature = "rerun")]
178        {
179            self.log_he_rerun(
180                "disolve/outgoing",
181                self.vertices[start_vert].outgoing_halfedge.unwrap(),
182            );
183        }
184
185        let mut removed_vertices = vec![end_vert];
186
187        // Remove flaps (= faces that share all their vertices)
188        let mut face_tuples: Vec<_> = self.vertices[start_vert]
189            .faces(self)
190            .into_iter()
191            .circular_tuple_windows()
192            .collect();
193
194        while let Some((face_id1, face_id2)) = face_tuples.pop() {
195            if self.faces_share_all_vertices(face_id1, face_id2) {
196                let (vs, hes) = self.delete_face(face_id1);
197                removed_vertices.extend(vs);
198                removed_halfedges.extend(hes);
199                removed_faces.push(face_id1);
200
201                let (vs, hes) = self.delete_face(face_id2);
202                removed_vertices.extend(vs);
203                removed_halfedges.extend(hes);
204                removed_faces.push(face_id2);
205
206                // one of the faces in the next tuple was removed, so we need to remove it.
207                face_tuples.pop();
208            }
209        }
210
211        (removed_vertices, removed_halfedges, removed_faces)
212    }
213
214    /// Remove a halfedge face and re-connecting the adjacent halfedges.
215    /// Only works on manifold triangle meshes.
216    fn remove_halfedge_face(&mut self, halfedge_id: HalfedgeId) -> (FaceId, [HalfedgeId; 2]) {
217        let he = self.halfedges[halfedge_id];
218
219        let face_id = he
220            .face
221            .expect("only called from disolve_edge() when there is a face");
222
223        let next_he_id = he.next.unwrap();
224        let prev_he_id = he.prev(self).unwrap();
225
226        let next_twin_id = self.halfedges[next_he_id].twin;
227        let prev_twin_id = self.halfedges[prev_he_id].twin;
228
229        let next_he = self.halfedges[next_he_id];
230        let prev_he = self.halfedges[prev_he_id];
231
232        let next_end_v_id = next_he.end_vertex;
233        let prev_end_v_id = prev_he.end_vertex;
234
235        self.vertices[next_end_v_id].outgoing_halfedge = next_he.next;
236        self.vertices[prev_end_v_id].outgoing_halfedge = prev_he.next;
237
238        let prev_start_v_id = prev_he.start_vertex(self);
239        let prev_start_v = self.vertices[prev_start_v_id];
240
241        if prev_start_v.outgoing_halfedge == Some(prev_he_id) {
242            self.vertices[prev_start_v_id].outgoing_halfedge = prev_he
243                .ccw_rotated_neighbour(self)
244                .or_else(|| prev_he.cw_rotated_neighbour(self));
245        }
246
247        #[cfg(feature = "rerun")]
248        {
249            self.log_face_rerun("disolve/remove_face", face_id);
250            self.log_he_rerun("disolve/remove_next", next_he_id);
251            self.log_he_rerun("disolve/remove_prev", prev_he_id);
252        }
253
254        self.qbvh.remove(self.faces[face_id]);
255
256        self.halfedges.remove(next_he_id);
257        self.halfedges.remove(prev_he_id);
258        self.faces.remove(face_id);
259
260        if let Some(next_twin) = next_twin_id {
261            self.halfedges[next_twin].twin = prev_twin_id;
262        }
263
264        if let Some(prev_twin) = prev_twin_id {
265            self.halfedges[prev_twin].twin = next_twin_id;
266        }
267
268        (face_id, [next_he_id, prev_he_id])
269    }
270}