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            } else {
284                error!("Halfedge not found")
285            }
286        }
287
288        self.vertices.remove(end_vert_id);
289        self.positions.remove(end_vert_id);
290        if let Some(normals) = &mut self.vertex_normals {
291            normals.remove(end_vert_id);
292        }
293        self.halfedges.remove(halfedge_id);
294        self.halfedges.remove(twin_id);
295        removed_halfedges.push(twin_id);
296
297        self.positions[start_vert_id] = center_pos;
298
299        self.vertices[start_vert_id].outgoing_halfedge = end_outgoing_halfedges
300            .into_iter()
301            .find(|he_id| self.halfedges.contains_key(*he_id))
302            .or_else(error_none!("No new outgoing halfedge found"));
303
304        #[cfg(feature = "rerun")]
305        {
306            self.log_he_rerun(
307                "collapse/outgoing",
308                self.vertices[start_vert_id].outgoing_halfedge.unwrap(),
309            );
310        }
311
312        removed_vertices.push(end_vert_id);
313
314        // Remove flaps (= faces that share all their vertices)
315        let mut face_tuples: Vec<_> = unwrap_or_return!(
316            self.vertices.get(start_vert_id),
317            "Start vertex not found",
318            (removed_vertices, removed_halfedges, removed_faces)
319        )
320        .faces(self)
321        .collect::<Vec<_>>()
322        .into_iter()
323        .circular_tuple_windows()
324        .collect();
325
326        let mut first = true;
327
328        while let Some((face_id1, face_id2)) = face_tuples.pop() {
329            #[cfg(feature = "rerun")]
330            {
331                self.log_rerun();
332                crate::RR.flush_blocking();
333            }
334
335            if self.faces_share_all_vertices(face_id1, face_id2) {
336                #[cfg(feature = "rerun")]
337                {
338                    if self.vertices[start_vert_id].faces(self).count() == 1 {
339                        self.log_vert_rerun("cleanup_delete", start_vert_id);
340                    }
341
342                    self.log_face_rerun("cleanup_delete/1", face_id1);
343                    self.log_face_rerun("cleanup_delete/2", face_id2);
344                    crate::RR.flush_blocking();
345                }
346
347                let mut halfedges_of_faces = HashSet::new();
348                // face existence already checked by `self.faces_share_all_vertices`
349                halfedges_of_faces.extend(self.faces[face_id1].halfedges(self));
350                halfedges_of_faces.extend(self.faces[face_id2].halfedges(self));
351
352                let (vs, hes) = self.delete_face(face_id1);
353                for he_id in &hes {
354                    halfedges_of_faces.remove(he_id);
355                }
356                removed_vertices.extend(vs);
357                removed_halfedges.extend(hes);
358                removed_faces.push(face_id1);
359
360                let (vs, hes) = self.delete_face(face_id2);
361                for he_id in &hes {
362                    halfedges_of_faces.remove(he_id);
363                }
364                removed_vertices.extend(vs);
365                removed_halfedges.extend(hes);
366                removed_faces.push(face_id2);
367
368                // the twin edges of the neighboring faces of the deleted faces are still there
369                // we need to remove them and re-connect (twin) their twin edges
370                debug_assert_eq!(halfedges_of_faces.len(), 2);
371                let mut halfedges_of_faces = halfedges_of_faces.into_iter();
372                let he_id1 = halfedges_of_faces.next().unwrap();
373                let he_id2 = halfedges_of_faces.next().unwrap();
374
375                let he1 = unwrap_or_return!(
376                    self.halfedges.get(he_id1),
377                    "Halfedge 1 missing",
378                    (removed_vertices, removed_halfedges, removed_faces)
379                );
380                let twin_id1 = unwrap_or_return!(
381                    he1.twin,
382                    "Twin 1 missing",
383                    (removed_vertices, removed_halfedges, removed_faces)
384                );
385                let he2 = unwrap_or_return!(
386                    self.halfedges.get(he_id2),
387                    "Halfedge 2 missing",
388                    (removed_vertices, removed_halfedges, removed_faces)
389                );
390                let twin_id2 = unwrap_or_return!(
391                    he2.twin,
392                    "Twin 2 missing",
393                    (removed_vertices, removed_halfedges, removed_faces)
394                );
395
396                {
397                    let twin1 = unwrap_or_return!(
398                        self.halfedges.get_mut(twin_id1),
399                        "Twin 1 missing",
400                        (removed_vertices, removed_halfedges, removed_faces)
401                    );
402                    twin1.twin = Some(twin_id2);
403                }
404
405                {
406                    let twin2 = unwrap_or_return!(
407                        self.halfedges.get_mut(twin_id2),
408                        "Twin 2 missing",
409                        (removed_vertices, removed_halfedges, removed_faces)
410                    );
411                    twin2.twin = Some(twin_id1);
412                }
413
414                self.halfedges.remove(he_id1);
415                self.halfedges.remove(he_id2);
416                removed_halfedges.push(he_id1);
417                removed_halfedges.push(he_id2);
418
419                let new_outgoing_he_id = if self.halfedges[twin_id1].end_vertex == start_vert_id {
420                    twin_id2
421                } else {
422                    twin_id1
423                };
424
425                // Also update both vertices of the deleted halfedges
426                unwrap_or_return!(
427                    self.vertices.get_mut(start_vert_id),
428                    "Start vertex not found",
429                    (removed_vertices, removed_halfedges, removed_faces)
430                )
431                .outgoing_halfedge = Some(new_outgoing_he_id);
432
433                let new_outgoing_he = unwrap_or_return!(
434                    self.halfedges.get(new_outgoing_he_id),
435                    "New outgoing halfedge not found",
436                    (removed_vertices, removed_halfedges, removed_faces)
437                );
438                unwrap_or_return!(
439                    self.vertices.get_mut(new_outgoing_he.end_vertex),
440                    "End vertex not found",
441                    (removed_vertices, removed_halfedges, removed_faces)
442                )
443                .outgoing_halfedge = new_outgoing_he
444                    .twin
445                    .or_else(error_none!("New outgoing twin missing"));
446
447                // the next tuple contains one of the removed faces, so re remove it
448                face_tuples.pop();
449
450                // tuples wrap around so we need to remove the first element if the last element was removed
451                if first {
452                    face_tuples.remove(0);
453                }
454            }
455
456            first = false;
457        }
458
459        self.check_vertex_faces_for_overlapping(start_vert_id, normal.unwrap());
460
461        (removed_vertices, removed_halfedges, removed_faces)
462    }
463
464    /// Remove a halfedge face and re-connecting the adjacent halfedges.
465    /// Only works on manifold triangle meshes.
466    #[instrument(skip(self))]
467    fn remove_halfedge_face(
468        &mut self,
469        halfedge_id: HalfedgeId,
470    ) -> Option<(FaceId, [HalfedgeId; 2])> {
471        let he = self
472            .halfedges
473            .get(halfedge_id)
474            .or_else(error_none!("Halfedge not found"))?;
475
476        let face_id = he.face.or_else(error_none!("Face not found"))?;
477
478        let next_he_id = he.next.or_else(error_none!("Next halfedge is None"))?;
479        let prev_he_id = he
480            .prev(self)
481            .or_else(error_none!("Previous halfedge is None"))?;
482
483        let next_twin_id = self.halfedges[next_he_id]
484            .twin
485            .or_else(error_none!("Next twin halfedge not found"))?;
486        let prev_twin_id = self.halfedges[prev_he_id]
487            .twin
488            .or_else(error_none!("Previous twin halfedge not found"))?;
489
490        let next_he = self
491            .halfedges
492            .get(next_he_id)
493            .or_else(error_none!("Next halfedge not found"))?;
494        let prev_he = self
495            .halfedges
496            .get(prev_he_id)
497            .or_else(error_none!("Previous halfedge not found"))?;
498
499        let next_end_v_id = next_he.end_vertex;
500        let prev_end_v_id = prev_he.end_vertex;
501
502        self.vertices
503            .get_mut(next_end_v_id)
504            .or_else(error_none!("Next end vertex not found"))?
505            .outgoing_halfedge = next_he.next.or_else(error_none!("Next next is None"));
506        self.vertices
507            .get_mut(prev_end_v_id)
508            .or_else(error_none!("Previous end vertex not found"))?
509            .outgoing_halfedge = prev_he.next.or_else(error_none!("Previous next is None"));
510
511        let prev_start_v_id = prev_he
512            .start_vertex(self)
513            .or_else(error_none!("Previous start vertex ID not found"))?;
514        let prev_start_v = self
515            .vertices
516            .get(prev_start_v_id)
517            .or_else(error_none!("Previous start vertex not found"))?;
518
519        if prev_start_v.outgoing_halfedge == Some(prev_he_id) {
520            self.vertices[prev_start_v_id].outgoing_halfedge = prev_he
521                .ccw_rotated_neighbour(self)
522                .or_else(|| prev_he.cw_rotated_neighbour(self))
523                .or_else(error_none!(
524                    "Previous start vertex new outgoing halfedge not found"
525                ));
526        }
527
528        #[cfg(feature = "rerun")]
529        {
530            self.log_face_rerun("collapse/remove_face", face_id);
531            self.log_he_rerun("collapse/remove_next", next_he_id);
532            self.log_he_rerun("collapse/remove_prev", prev_he_id);
533        }
534
535        self.bvh.remove(
536            self.faces
537                .get(face_id)
538                .or_else(error_none!("Face not found"))?
539                .index,
540        );
541
542        self.halfedges.remove(next_he_id);
543        self.halfedges.remove(prev_he_id);
544        self.faces.remove(face_id);
545
546        self.halfedges
547            .get_mut(next_twin_id)
548            .or_else(error_none!("Next twin halfedge not found"))?
549            .twin = Some(prev_twin_id);
550
551        self.halfedges
552            .get_mut(prev_twin_id)
553            .or_else(error_none!("Previous twin halfedge not found"))?
554            .twin = Some(next_twin_id);
555
556        Some((face_id, [next_he_id, prev_he_id]))
557    }
558
559    #[instrument(skip(self))]
560    fn check_vertex_faces_for_overlapping(&mut self, vertex_id: VertexId, normal: Vec3) {
561        let vertex = *unwrap_or_return!(self.vertices.get(vertex_id), "Vertex not found");
562        let pos = *unwrap_or_return!(self.positions.get(vertex_id), "Position not found");
563
564        let neighbours = vertex.neighbours(self).collect::<Vec<_>>();
565
566        if neighbours.is_empty() {
567            error!("Vertex has no neighbours");
568            return;
569        }
570
571        let mut prev_diff = (unwrap_or_return!(
572            self.positions.get(neighbours[neighbours.len() - 1]),
573            "Last neighbour position not found"
574        ) - pos)
575            .normalize();
576
577        for neighbour_id in neighbours {
578            let mut diff = (unwrap_or_return!(
579                self.positions.get(neighbour_id),
580                "Neighbour position not found"
581            ) - pos)
582                .normalize();
583
584            // smooth out degenerate faces
585            if diff.cross(prev_diff).dot(normal) < 0.1 {
586                let mut avg_pos = Vec3::ZERO;
587
588                let n_neighbours = unwrap_or_return!(
589                    self.vertices.get(neighbour_id),
590                    "Neighbour vertex not found"
591                )
592                .neighbours(self)
593                .collect::<Vec<_>>();
594
595                let len = n_neighbours.len();
596
597                if len == 0 {
598                    error!("Neighbour vertex has no neighbours");
599                    continue;
600                }
601
602                for n_id in n_neighbours {
603                    avg_pos += *unwrap_or_return!(
604                        self.positions.get(n_id),
605                        "Neighbour's neighbour position not found"
606                    );
607                }
608                avg_pos /= len as f32;
609
610                // already checked above
611                self.positions[neighbour_id] = avg_pos;
612                diff = (avg_pos - pos).normalize();
613            }
614
615            prev_diff = diff;
616        }
617    }
618}
619
620// #[cfg(test)]
621// mod test {
622//     use super::*;
623
624//     #[test]
625//     fn test_collapse() {
626//         let mut mesh_graph = MeshGraph::new();
627
628//         mesh_graph.log_rerun();
629//     }
630// }