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