Skip to main content

mesh_graph/ops/merge_one_ring/
mod.rs

1#[cfg(test)]
2mod tests;
3
4use std::{f32, ops::RangeInclusive};
5
6use hashbrown::HashSet;
7use itertools::Itertools;
8use tracing::{error, instrument};
9
10use crate::{
11    AddEdge, FaceId, HalfedgeId, MeshGraph, Vertex, VertexId, error_none,
12    ops::{add::AddFace, collapse::CollapseEdge, edit::MergeVertices},
13    utils::unwrap_or_return,
14};
15
16#[derive(Default)]
17pub struct MergeVerticesOneRing {
18    pub removed_vertices: Vec<VertexId>,
19    pub removed_halfedges: Vec<HalfedgeId>,
20    pub removed_faces: Vec<FaceId>,
21
22    pub added_vertices: Vec<VertexId>,
23    pub added_halfedges: Vec<HalfedgeId>,
24    pub added_faces: Vec<FaceId>,
25}
26
27impl MeshGraph {
28    /// Merge two vertices by connecting their 1-rings.
29    ///
30    /// The vertices are deleted on top of everything that is returned in the `removed_...` fields.
31    ///
32    /// See [Freestyle: Sculpting meshes with self-adaptive topology DOI 10.1016/j.cag.2011.03.033](https://inria.hal.science/inria-00606516v1/document)
33    /// Chapters 3.2 and 5.1
34    #[instrument(skip(self, marked_halfedges, marked_vertices))]
35    pub fn merge_vertices_one_rings(
36        &mut self,
37        vertex_id1: VertexId,
38        vertex_id2: VertexId,
39        flip_threshold_sqr: f32,
40        marked_halfedges: &mut HashSet<HalfedgeId>,
41        marked_vertices: &mut HashSet<VertexId>,
42    ) -> MergeVerticesOneRing {
43        let mut result = MergeVerticesOneRing::default();
44
45        let vertex1 = *unwrap_or_return!(self.vertices.get(vertex_id1), "Vertex not found", result);
46        let vertex2 = *unwrap_or_return!(self.vertices.get(vertex_id2), "Vertex not found", result);
47
48        let one_ring_he_ids1 = vertex1.one_ring(self).collect_vec();
49        let mut one_ring_he_ids2 = vertex2
50            .one_ring(self)
51            // halfedges are reversed, so twin halfedges are actually the next halfedges.
52            // also existence is checked in `one_ring()`.
53            .filter_map(|he_id| {
54                self.halfedges[he_id]
55                    .twin
56                    .or_else(error_none!("Twin not found"))
57            })
58            .collect_vec();
59        one_ring_he_ids2.reverse();
60
61        if one_ring_he_ids1.len() < 3 || one_ring_he_ids2.len() < 3 {
62            error!(
63                "One rings are too small. One ring of {vertex_id1:?} = {}; one ring of {vertex_id2:?} = {}",
64                one_ring_he_ids1.len(),
65                one_ring_he_ids2.len()
66            );
67            return result;
68        }
69
70        // halfedges' existence already checked in `one_ring()`.
71        let mut one_ring_v_ids1 = one_ring_he_ids1
72            .iter()
73            .map(|he_id| self.halfedges[*he_id].end_vertex)
74            .collect_vec();
75        one_ring_v_ids1.rotate_right(1);
76        let mut one_ring_v_ids2 = one_ring_he_ids2
77            .iter()
78            .map(|he_id| self.halfedges[*he_id].end_vertex)
79            .collect_vec();
80        one_ring_v_ids2.rotate_right(1);
81
82        if one_ring_v_ids1.len() < 3 || one_ring_v_ids2.len() < 3 {
83            error!("One rings are too small");
84            return result;
85        }
86
87        let one_ring_he_set1 = HashSet::<HalfedgeId>::from_iter(one_ring_he_ids1.iter().copied());
88        let one_ring_he_set2 = HashSet::<HalfedgeId>::from_iter(one_ring_he_ids2.iter().copied());
89        let shared_he_ids = HashSet::<HalfedgeId>::from_iter(
90            one_ring_he_set1.intersection(&one_ring_he_set2).copied(),
91        );
92
93        let one_ring_v_set1 = HashSet::<VertexId>::from_iter(one_ring_v_ids1.iter().copied());
94        let one_ring_v_set2 = HashSet::<VertexId>::from_iter(one_ring_v_ids2.iter().copied());
95        let shared_v_ids =
96            HashSet::<VertexId>::from_iter(one_ring_v_set1.intersection(&one_ring_v_set2).copied());
97
98        if self.check_and_flip_single_shared_he(&shared_he_ids, flip_threshold_sqr, &mut result) {
99            return result;
100        }
101
102        self.remove_neighbor_faces(&vertex1, &vertex2, &mut result);
103
104        #[cfg(feature = "rerun")]
105        self.log_rerun();
106
107        let (already_connected_face_ids, connected_v_ids, connected_he_ids) = unwrap_or_return!(
108            self.find_already_connected_pairings(
109                &one_ring_v_ids1,
110                &one_ring_v_ids2,
111                &shared_v_ids,
112            ),
113            "Error in find_already_connected_pairings",
114            result
115        );
116
117        #[cfg(feature = "rerun")]
118        {
119            self.log_faces_rerun("already_connected", &already_connected_face_ids);
120            self.log_hes_rerun("already_connected", &connected_he_ids);
121        }
122
123        let range_pairs_to_connect = self.compute_range_pairs_to_connect(
124            &one_ring_v_ids1,
125            &one_ring_v_ids2,
126            &one_ring_he_ids1,
127            &one_ring_he_ids2,
128            &shared_v_ids,
129            &shared_he_ids,
130            connected_v_ids,
131            connected_he_ids,
132        );
133
134        if range_pairs_to_connect.is_empty() {
135            error!("No range pairs to connect");
136            return result;
137        }
138
139        tracing::info!("Range pairs to connect: {range_pairs_to_connect:#?}");
140
141        let planned_faces =
142            self.plan_new_faces(&range_pairs_to_connect, &one_ring_v_ids1, &one_ring_v_ids2);
143
144        for face_id in already_connected_face_ids {
145            let (v_ids, he_ids) = self.remove_face(face_id);
146            result.removed_faces.push(face_id);
147            result.removed_halfedges.extend(he_ids);
148            result.removed_vertices.extend(v_ids);
149        }
150
151        self.add_planned_faces(
152            planned_faces,
153            &one_ring_v_set1,
154            &one_ring_v_set2,
155            marked_halfedges,
156            &mut result,
157        );
158
159        #[cfg(feature = "rerun")]
160        self.log_rerun();
161
162        self.cleanup_bookkeeping(
163            &one_ring_v_ids1,
164            &one_ring_v_ids2,
165            marked_vertices,
166            marked_halfedges,
167            &mut result,
168        );
169
170        self.merge_remaining_unconnected(
171            one_ring_v_ids1.clone(),
172            one_ring_v_ids2.clone(),
173            &mut result,
174        );
175
176        self.smooth_vertices(
177            one_ring_v_ids1
178                .iter()
179                .chain(&one_ring_v_ids2)
180                .chain(&result.added_vertices)
181                .copied(),
182        );
183
184        result
185    }
186
187    fn merge_remaining_unconnected(
188        &mut self,
189        mut one_ring_v_ids1: Vec<VertexId>,
190        mut one_ring_v_ids2: Vec<VertexId>,
191        result: &mut MergeVerticesOneRing,
192    ) {
193        while let Some(v_id) = one_ring_v_ids1.pop() {
194            let Some(vert) = self.vertices.get(v_id) else {
195                continue;
196            };
197
198            let mut vertices = vec![];
199
200            for he_id in vert.boundary_halfedgdes(self) {
201                let Some(he) = self.halfedges.get(he_id) else {
202                    error!("Halfedge not found: {he_id:?}");
203                    continue;
204                };
205
206                vertices.push(he.end_vertex);
207            }
208
209            if !vertices.is_empty() {
210                one_ring_v_ids1.retain(|v_id| !vertices.contains(v_id));
211                one_ring_v_ids2.retain(|v_id| !vertices.contains(v_id));
212
213                let MergeVertices {
214                    removed_vertices,
215                    removed_halfedges,
216                    removed_faces,
217                } = self.merge_vertices(vertices);
218
219                result.removed_vertices.extend(removed_vertices);
220                result.removed_halfedges.extend(removed_halfedges);
221                result.removed_faces.extend(removed_faces);
222            }
223        }
224    }
225
226    fn find_already_connected_pairings(
227        &mut self,
228        one_ring_v_ids1: &[VertexId],
229        one_ring_v_ids2: &[VertexId],
230        shared_v_ids: &HashSet<VertexId>,
231    ) -> Option<(Vec<FaceId>, HashSet<VertexId>, HashSet<HalfedgeId>)> {
232        let mut connected_v_ids = HashSet::new();
233        let mut connected_he_ids = HashSet::new();
234        let mut already_connected_face_ids = vec![];
235
236        for ((idx1, &v_id1), (idx2, &v_id2)) in one_ring_v_ids1
237            .iter()
238            .enumerate()
239            .cartesian_product(one_ring_v_ids2.iter().enumerate())
240        {
241            if shared_v_ids.contains(&v_id1) || shared_v_ids.contains(&v_id2) {
242                continue;
243            }
244
245            if v_id1 == v_id2 {
246                connected_v_ids.insert(v_id1);
247                continue;
248            }
249
250            if connected_v_ids.contains(&v_id1) && connected_v_ids.contains(&v_id2) {
251                continue;
252            }
253
254            if let Some(he_id) = self.halfedge_from_to(v_id1, v_id2) {
255                let pairing = self.find_triangle_fan(
256                    he_id,
257                    idx1,
258                    idx2,
259                    one_ring_v_ids1,
260                    one_ring_v_ids2,
261                    &mut connected_he_ids,
262                    &mut already_connected_face_ids,
263                )?;
264
265                #[cfg(feature = "rerun")]
266                pairing.log_rerun(
267                    "already_connected_init",
268                    [one_ring_v_ids1, one_ring_v_ids2],
269                    self,
270                );
271
272                let mut connected_pairings = self.find_connected_triangle_fans(
273                    one_ring_v_ids1,
274                    one_ring_v_ids2,
275                    &pairing,
276                    -1,
277                    &mut connected_he_ids,
278                    &mut already_connected_face_ids,
279                );
280
281                connected_pairings.push(pairing.clone());
282
283                connected_pairings.extend(self.find_connected_triangle_fans(
284                    one_ring_v_ids1,
285                    one_ring_v_ids2,
286                    &pairing,
287                    1,
288                    &mut connected_he_ids,
289                    &mut already_connected_face_ids,
290                ));
291
292                for pairing in &connected_pairings {
293                    for v_id in pairing.all_vertex_ids([one_ring_v_ids1, one_ring_v_ids2]) {
294                        connected_v_ids.insert(v_id);
295                    }
296                }
297            }
298        }
299
300        Some((
301            already_connected_face_ids,
302            connected_v_ids,
303            connected_he_ids,
304        ))
305    }
306
307    fn find_connected_triangle_fans(
308        &self,
309        one_ring_v_ids1: &[VertexId],
310        one_ring_v_ids2: &[VertexId],
311        pairing: &Pairing,
312        idx_step: i32,
313        connected_he_ids: &mut HashSet<HalfedgeId>,
314        connected_face_ids: &mut Vec<FaceId>,
315    ) -> Vec<Pairing> {
316        let (single_v_ids, other_v_ids) = if pairing.single_range_idx == 0 {
317            (one_ring_v_ids1, one_ring_v_ids2)
318        } else {
319            (one_ring_v_ids2, one_ring_v_ids1)
320        };
321
322        let next_single_idx = ((pairing.single_idx_in_range + single_v_ids.len()) as i32 + idx_step)
323            as usize
324            % single_v_ids.len();
325        let next_v_id = single_v_ids[next_single_idx];
326
327        let mut other_idx = if idx_step < 0 {
328            *pairing.other_range.start()
329        } else {
330            *pairing.other_range.end()
331        };
332        other_idx %= other_v_ids.len();
333
334        let current_single_v_id = single_v_ids[pairing.single_idx_in_range];
335
336        let mut pairings = vec![];
337
338        if self
339            .face_with_vertices(next_v_id, current_single_v_id, other_v_ids[other_idx])
340            .is_some()
341        {
342            // swap single range and other range
343            let mut current_pairing = Pairing::new_triangle(
344                1 - pairing.single_range_idx,
345                other_idx,
346                next_single_idx,
347                pairing.single_idx_in_range,
348                single_v_ids.len(),
349            );
350
351            self.extend_triangle_pairing_fan(
352                one_ring_v_ids1,
353                one_ring_v_ids2,
354                &mut current_pairing,
355                connected_he_ids,
356                connected_face_ids,
357            );
358
359            #[cfg(feature = "rerun")]
360            current_pairing.log_rerun(
361                format!("extended/step_{}", idx_step),
362                [one_ring_v_ids1, one_ring_v_ids2],
363                self,
364            );
365
366            let next_pairings = self.find_connected_triangle_fans(
367                one_ring_v_ids1,
368                one_ring_v_ids2,
369                &current_pairing,
370                idx_step,
371                connected_he_ids,
372                connected_face_ids,
373            );
374
375            pairings.push(current_pairing);
376            pairings.extend(next_pairings);
377        }
378
379        pairings
380    }
381
382    #[allow(clippy::too_many_arguments)]
383    fn find_triangle_fan(
384        &self,
385        he_id: HalfedgeId,
386        idx1: usize,
387        idx2: usize,
388        one_ring_v_ids1: &[VertexId],
389        one_ring_v_ids2: &[VertexId],
390        connected_he_ids: &mut HashSet<HalfedgeId>,
391        connected_face_ids: &mut Vec<FaceId>,
392    ) -> Option<Pairing> {
393        let he = self
394            .halfedges
395            .get(he_id)
396            .or_else(error_none!("Halfedge not found"))?;
397
398        let mut current_pairing = Pairing {
399            single_range_idx: 0,
400            single_idx_in_range: idx1,
401            other_range: idx2..=idx2,
402        };
403
404        // might not exist. we removed some faces (neighbors).
405        let Some(opposite_v_id) = he.opposite_vertex(self) else {
406            return Some(current_pairing);
407        };
408
409        if !self.create_triangle_pairing(
410            one_ring_v_ids1,
411            one_ring_v_ids2,
412            idx1,
413            idx2,
414            opposite_v_id,
415            &mut current_pairing,
416        ) {
417            let twin_id = he.twin.or_else(error_none!("Twin not found"))?;
418            let twin = self
419                .halfedges
420                .get(twin_id)
421                .or_else(error_none!("Twin not found"))?;
422
423            // might not exist. we removed some faces (neighbors).
424            let Some(opposite_v_id) = twin.opposite_vertex(self) else {
425                return Some(current_pairing);
426            };
427
428            if !self.create_triangle_pairing(
429                one_ring_v_ids1,
430                one_ring_v_ids2,
431                idx1,
432                idx2,
433                opposite_v_id,
434                &mut current_pairing,
435            ) {
436                // Return single halfedge current pairing
437                return Some(current_pairing);
438            }
439        }
440
441        self.extend_triangle_pairing_fan(
442            one_ring_v_ids1,
443            one_ring_v_ids2,
444            &mut current_pairing,
445            connected_he_ids,
446            connected_face_ids,
447        );
448
449        Some(current_pairing)
450    }
451
452    fn add_face_to_connected_he_ids(
453        &self,
454        face_id: FaceId,
455        connected_he_ids: &mut HashSet<HalfedgeId>,
456    ) {
457        let face = unwrap_or_return!(
458            self.faces.get(face_id),
459            "failed to get start face for pairing fan"
460        );
461        connected_he_ids.extend(
462            face.halfedges(self)
463                .filter_map(|he_id| {
464                    let he = self
465                        .halfedges
466                        .get(he_id)
467                        .or_else(error_none!("Halfedge not found"))?;
468                    Some([
469                        he_id,
470                        he.twin.or_else(error_none!("Twin halfedge not found"))?,
471                    ])
472                })
473                .flatten(),
474        );
475    }
476
477    fn extend_triangle_pairing_fan(
478        &self,
479        one_ring_v_ids1: &[VertexId],
480        one_ring_v_ids2: &[VertexId],
481        current_pairing: &mut Pairing,
482        connected_he_ids: &mut HashSet<HalfedgeId>,
483        connected_face_ids: &mut Vec<FaceId>,
484    ) {
485        let (single_ids, other_ids) = if current_pairing.single_range_idx == 0 {
486            (one_ring_v_ids1, one_ring_v_ids2)
487        } else {
488            (one_ring_v_ids2, one_ring_v_ids1)
489        };
490
491        let other_len = other_ids.len();
492        let single_v_id = single_ids[current_pairing.single_idx_in_range % single_ids.len()];
493
494        // Walk backwards from the start of other_range
495        let mut range_start = *current_pairing.other_range.start();
496
497        let start_face_id = unwrap_or_return!(
498            self.face_with_vertices(
499                single_v_id,
500                other_ids[range_start % other_len],
501                other_ids[*current_pairing.other_range.end() % other_len],
502            ),
503            "failed to find start face for pairing fan"
504        );
505        self.add_face_to_connected_he_ids(start_face_id, connected_he_ids);
506        connected_face_ids.push(start_face_id);
507
508        loop {
509            // Wrap backwards (using saturating to avoid underflow; handle wrap via modular arithmetic)
510            let prev_idx = (range_start + other_len - 1) % other_len;
511            let prev_v_id = other_ids[prev_idx];
512
513            let boundary_v_id = other_ids[range_start % other_len];
514            if let Some(face_id) = self.face_with_vertices(single_v_id, prev_v_id, boundary_v_id) {
515                // Adjust range_start to go one step back; keep values un-modded so the
516                // range arithmetic stays consistent with how ConnectPair::new handles wrapping.
517                if range_start == 0 {
518                    range_start = other_len - 1;
519                } else {
520                    range_start -= 1;
521                }
522                current_pairing.other_range = range_start..=*current_pairing.other_range.end();
523
524                self.add_face_to_connected_he_ids(face_id, connected_he_ids);
525                connected_face_ids.push(face_id);
526            } else {
527                break;
528            }
529        }
530
531        // Walk forwards from the end of other_range
532        let mut range_end = *current_pairing.other_range.end();
533        loop {
534            let next_idx = (range_end + 1) % other_len;
535            let next_v_id = other_ids[next_idx];
536
537            let boundary_v_id = other_ids[range_end % other_len];
538            if let Some(face_id) = self.face_with_vertices(single_v_id, boundary_v_id, next_v_id) {
539                range_end += 1;
540                current_pairing.other_range = *current_pairing.other_range.start()..=range_end;
541
542                self.add_face_to_connected_he_ids(face_id, connected_he_ids);
543                connected_face_ids.push(face_id);
544            } else {
545                break;
546            }
547        }
548    }
549
550    fn create_triangle_pairing(
551        &self,
552        one_ring_v_ids1: &[VertexId],
553        one_ring_v_ids2: &[VertexId],
554        idx1: usize,
555        idx2: usize,
556        third_v_id: VertexId,
557        current_pairing: &mut Pairing,
558    ) -> bool {
559        let (idx1, idx2) =
560            if let Some(idx) = one_ring_v_ids1.iter().position(|v_id| *v_id == third_v_id) {
561                current_pairing.single_range_idx = 1;
562                current_pairing.single_idx_in_range = idx2;
563                idx1.min(idx)..=idx1.max(idx);
564
565                (idx, idx1)
566            } else if let Some(idx) = one_ring_v_ids2.iter().position(|v_id| *v_id == third_v_id) {
567                (idx, idx2)
568            } else {
569                return false;
570            };
571
572        let other_len = if current_pairing.single_range_idx == 0 {
573            one_ring_v_ids2.len()
574        } else {
575            one_ring_v_ids1.len()
576        };
577
578        *current_pairing = Pairing::new_triangle(
579            current_pairing.single_range_idx,
580            current_pairing.single_idx_in_range,
581            idx1,
582            idx2,
583            other_len,
584        );
585
586        true
587    }
588
589    fn cleanup_bookkeeping(
590        &mut self,
591        one_ring_v_ids1: &[VertexId],
592        one_ring_v_ids2: &[VertexId],
593        marked_vertices: &mut HashSet<VertexId>,
594        marked_halfedges: &mut HashSet<HalfedgeId>,
595        result: &mut MergeVerticesOneRing,
596    ) {
597        for vertex_id in one_ring_v_ids1.iter().chain(one_ring_v_ids2).copied() {
598            if !self.vertices.contains_key(vertex_id) {
599                continue;
600            }
601
602            let cleanup = self.make_vertex_neighborhood_manifold(vertex_id);
603
604            if !cleanup.added_vertices.is_empty() {
605                marked_vertices.insert(vertex_id);
606            }
607
608            result.added_vertices.extend(cleanup.added_vertices.clone());
609            marked_vertices.extend(cleanup.added_vertices);
610
611            for removed_v_id in cleanup.removed_vertices {
612                if result.added_vertices.contains(&removed_v_id) {
613                    result.added_vertices.retain(|&v_id| v_id != removed_v_id);
614                } else {
615                    result.removed_vertices.push(removed_v_id);
616                }
617
618                marked_vertices.remove(&removed_v_id);
619            }
620
621            for removed_he_id in cleanup.removed_halfedges {
622                if result.added_halfedges.contains(&removed_he_id) {
623                    result
624                        .added_halfedges
625                        .retain(|&he_id| he_id != removed_he_id);
626                } else {
627                    result.removed_halfedges.push(removed_he_id);
628                }
629
630                marked_halfedges.remove(&removed_he_id);
631            }
632
633            for removed_face_id in cleanup.removed_faces {
634                if result.added_faces.contains(&removed_face_id) {
635                    result
636                        .added_faces
637                        .retain(|&face_id| face_id != removed_face_id);
638                } else {
639                    result.removed_faces.push(removed_face_id);
640                }
641            }
642        }
643    }
644
645    fn add_planned_faces(
646        &mut self,
647        planned_faces: Vec<PlannedFace>,
648        one_ring_set1: &HashSet<VertexId>,
649        one_ring_set2: &HashSet<VertexId>,
650        marked_halfedges: &mut HashSet<HalfedgeId>,
651        result: &mut MergeVerticesOneRing,
652    ) {
653        let mut prev_he = None;
654
655        for planned_face in planned_faces {
656            let inserted = if let Some(prev_he) = prev_he {
657                planned_face.add_to_mesh_graph_and_he(self, prev_he)
658            } else {
659                planned_face.add_to_mesh_graph(self)
660            };
661
662            let Some(inserted) = inserted else {
663                // depending on the topology inserting a face can fail sometimes. this is fine.
664                continue;
665            };
666
667            prev_he = inserted.0;
668            let inserted = inserted.1;
669
670            for he_id in inserted.halfedge_ids.iter().copied() {
671                // just inserted => must exist
672                let he = self.halfedges[he_id];
673                let start_v_id =
674                    unwrap_or_return!(he.start_vertex(self), "Couldn't find start vertex");
675                let end_v_id = he.end_vertex;
676
677                if one_ring_set1.contains(&start_v_id) && one_ring_set2.contains(&end_v_id)
678                    || one_ring_set2.contains(&start_v_id) && one_ring_set1.contains(&end_v_id)
679                {
680                    marked_halfedges.insert(he_id);
681                }
682            }
683
684            result.added_halfedges.extend(inserted.halfedge_ids);
685            result.added_faces.push(inserted.face_id);
686        }
687    }
688
689    fn remove_neighbor_faces(
690        &mut self,
691        vertex1: &Vertex,
692        vertex2: &Vertex,
693        result: &mut MergeVerticesOneRing,
694    ) {
695        let face_ids1 = vertex1.faces(self).collect_vec();
696        let face_ids2 = vertex2.faces(self).collect_vec();
697
698        for face_id in face_ids1.into_iter().chain(face_ids2) {
699            let (del_v, del_he) = self.remove_face(face_id);
700
701            result.removed_faces.push(face_id);
702            result.removed_vertices.extend(del_v);
703            result.removed_halfedges.extend(del_he);
704        }
705    }
706
707    #[instrument(skip_all)]
708    fn flip_and_collapse_single_shared_edge_if_below_threshold(
709        &mut self,
710        single_shared_he_id: HalfedgeId,
711        flip_threshold_sqr: f32,
712        result: &mut MergeVerticesOneRing,
713    ) -> bool {
714        let he = self.halfedges[single_shared_he_id];
715
716        let twin_id = unwrap_or_return!(he.twin, "Twin not found", false);
717        let twin = unwrap_or_return!(self.halfedges.get(twin_id), "Twin not found", false);
718
719        let other_v_id1 =
720            unwrap_or_return!(he.opposite_vertex(self), "Opposite vertex not found", false);
721        let other_v_id2 = unwrap_or_return!(
722            twin.opposite_vertex(self),
723            "Opposite vertex not found",
724            false
725        );
726
727        let pos1 = *unwrap_or_return!(self.positions.get(other_v_id1), "Position not found", false);
728        let pos2 = *unwrap_or_return!(self.positions.get(other_v_id2), "Position not found", false);
729
730        if pos1.distance_squared(pos2) <= flip_threshold_sqr {
731            tracing::info!("Flipping edge {single_shared_he_id:?}");
732
733            self.flip_edge(single_shared_he_id);
734
735            let CollapseEdge {
736                removed_vertices,
737                removed_halfedges,
738                removed_faces,
739                added_vertices,
740            } = self.collapse_edge(single_shared_he_id);
741
742            result.added_vertices.extend(added_vertices);
743            result.removed_vertices.extend(removed_vertices);
744            result.removed_halfedges.extend(removed_halfedges);
745            result.removed_faces.extend(removed_faces);
746
747            true
748        } else {
749            false
750        }
751    }
752
753    fn plan_new_faces(
754        &self,
755        range_pairs_to_connect: &[ConnectPair],
756        one_ring_v_ids1: &[VertexId],
757        one_ring_v_ids2: &[VertexId],
758    ) -> Vec<PlannedFace> {
759        let mut planned_faces = Vec::with_capacity(one_ring_v_ids1.len());
760
761        for range_pair_to_connect in range_pairs_to_connect {
762            let start_pairing_index = planned_faces.len();
763
764            let pairings = range_pair_to_connect.compute_pairings();
765
766            tracing::info!("Pairings: {:#?}", pairings);
767
768            if pairings.is_empty() {
769                continue;
770            }
771
772            let mut prev_single_v_id = None;
773            let mut prev_other_v_id = None;
774
775            if matches!(range_pair_to_connect.start_cap, ConnectPairCap::Open) {
776                let last_pairing = pairings.last().unwrap();
777
778                let (s, o) = last_pairing.last_pair([one_ring_v_ids1, one_ring_v_ids2]);
779
780                prev_single_v_id = Some(s);
781                prev_other_v_id = Some(o);
782            }
783
784            for pairing in pairings {
785                if let Some(prev_single_v_id) = prev_single_v_id
786                    && let Some(prev_other_v_id) = prev_other_v_id
787                {
788                    // add the quad between the previous and current pairings
789                    let (single_v_id, other_v_id) =
790                        pairing.first_pair([one_ring_v_ids1, one_ring_v_ids2]);
791
792                    let face_order = if prev_single_v_id == prev_other_v_id {
793                        PlannedFaceOrder::Start
794                    } else if single_v_id == other_v_id {
795                        PlannedFaceOrder::End
796                    } else {
797                        PlannedFaceOrder::Middle
798                    };
799
800                    // make sure the triangle vertices are CCW
801                    if pairing.single_range_idx == 0 {
802                        if prev_single_v_id != prev_other_v_id {
803                            planned_faces.push(PlannedFace::new(
804                                prev_single_v_id,
805                                single_v_id,
806                                prev_other_v_id,
807                                face_order,
808                            ));
809                        }
810                        if single_v_id != other_v_id {
811                            planned_faces.push(PlannedFace::new(
812                                prev_other_v_id,
813                                single_v_id,
814                                other_v_id,
815                                face_order,
816                            ));
817                        }
818                    } else {
819                        if prev_single_v_id != prev_other_v_id {
820                            planned_faces.push(PlannedFace::new(
821                                prev_single_v_id,
822                                prev_other_v_id,
823                                single_v_id,
824                                face_order,
825                            ));
826                        }
827                        if single_v_id != other_v_id {
828                            planned_faces.push(PlannedFace::new(
829                                prev_other_v_id,
830                                other_v_id,
831                                single_v_id,
832                                face_order,
833                            ));
834                        }
835                    }
836                }
837
838                // add the faces of the current pairing
839                planned_faces.extend(pairing.fill_faces([one_ring_v_ids1, one_ring_v_ids2]));
840
841                let (single_v_id, other_v_id) =
842                    pairing.last_pair([one_ring_v_ids1, one_ring_v_ids2]);
843
844                prev_single_v_id = Some(single_v_id);
845                prev_other_v_id = Some(other_v_id);
846            }
847
848            if !matches!(
849                range_pair_to_connect.start_cap,
850                ConnectPairCap::AlreadyConnected
851            ) {
852                planned_faces[start_pairing_index].order = PlannedFaceOrder::Start;
853            }
854
855            let len = planned_faces.len();
856            planned_faces[len - 1].order = if len - 1 == start_pairing_index {
857                PlannedFaceOrder::Single
858            } else {
859                PlannedFaceOrder::End
860            };
861        }
862
863        planned_faces
864    }
865
866    #[instrument(skip_all)]
867    fn check_and_flip_single_shared_he(
868        &mut self,
869        shared_he_ids: &HashSet<HalfedgeId>,
870        flip_threshold_sqr: f32,
871        result: &mut MergeVerticesOneRing,
872    ) -> bool {
873        if shared_he_ids.len() == 1 {
874            let shared_he_id = shared_he_ids.iter().copied().next().unwrap();
875
876            #[cfg(feature = "rerun")]
877            self.log_he_rerun("common_one_ring_he", shared_he_id);
878
879            return self.flip_and_collapse_single_shared_edge_if_below_threshold(
880                shared_he_id,
881                flip_threshold_sqr,
882                result,
883            );
884        }
885
886        false
887    }
888
889    #[instrument(skip_all)]
890    #[allow(clippy::too_many_arguments)]
891    fn compute_range_pairs_to_connect(
892        &mut self,
893        one_ring_v_ids1: &[VertexId],
894        one_ring_v_ids2: &[VertexId],
895        one_ring_he_ids1: &[HalfedgeId],
896        one_ring_he_ids2: &[HalfedgeId],
897        shared_v_ids: &HashSet<VertexId>,
898        shared_he_ids: &HashSet<HalfedgeId>,
899        mut connected_v_ids: HashSet<VertexId>,
900        mut connected_he_ids: HashSet<HalfedgeId>,
901    ) -> Vec<ConnectPair> {
902        let mut range_pairs_to_connect = vec![];
903
904        let (orig_start_idx1, orig_start_idx2, orig_start_cap) = unwrap_or_return!(
905            self.find_start_indices(
906                one_ring_v_ids1,
907                one_ring_v_ids2,
908                one_ring_he_ids1,
909                one_ring_he_ids2,
910                shared_v_ids,
911                shared_he_ids,
912                &connected_v_ids,
913                &connected_he_ids,
914            ),
915            "Couldn't find start indices",
916            range_pairs_to_connect
917        );
918
919        tracing::info!(
920            "start idx1: {}, start idx2: {}",
921            orig_start_idx1,
922            orig_start_idx2
923        );
924
925        let len1 = one_ring_v_ids1.len();
926        let len2 = one_ring_v_ids2.len();
927
928        let mut start_idx1 = orig_start_idx1;
929        let mut start_idx2 = orig_start_idx2;
930        let mut start_cap = orig_start_cap;
931
932        let mut end_idx1 = (start_idx1 + 1) % len1;
933        let mut end_idx2 = (start_idx2 + 1) % len2;
934
935        #[cfg(feature = "rerun")]
936        {
937            self.log_verts_w_labels_rerun(
938                "pairs_start_idx",
939                &[one_ring_v_ids1[start_idx1], one_ring_v_ids2[start_idx2]],
940                &["1", "2"],
941            );
942            self.log_vert_rerun("pairs_end_idx1", one_ring_v_ids1[end_idx1]);
943            self.log_vert_rerun("pairs_end_idx2", one_ring_v_ids2[end_idx2]);
944        }
945        let mut v_id1;
946        let mut v_id2;
947
948        while end_idx1 != orig_start_idx1 {
949            v_id1 = one_ring_v_ids1[end_idx1];
950            v_id2 = one_ring_v_ids2[end_idx2];
951
952            if !self.vertices.contains_key(v_id1) || !self.vertices.contains_key(v_id2) {
953                break;
954            }
955
956            let mut end_cap = ConnectPairCap::Open;
957
958            if shared_v_ids.contains(&v_id1) {
959                while v_id2 != v_id1 {
960                    end_idx2 = (end_idx2 + 1) % len2;
961                    v_id2 = one_ring_v_ids2[end_idx2];
962
963                    #[cfg(feature = "rerun")]
964                    self.log_vert_rerun("pairs_end_idx2", v_id2);
965                }
966
967                end_cap = ConnectPairCap::Closed;
968            } else if shared_v_ids.contains(&v_id2) {
969                while v_id1 != v_id2 {
970                    end_idx1 = (end_idx1 + 1) % len1;
971                    v_id1 = one_ring_v_ids1[end_idx1];
972
973                    #[cfg(feature = "rerun")]
974                    self.log_vert_rerun("pairs_end_idx1", v_id1);
975                }
976
977                end_cap = ConnectPairCap::Closed;
978            } else if connected_v_ids.contains(&v_id1) {
979                while !connected_v_ids.contains(&v_id2) {
980                    end_idx2 = (end_idx2 + 1) % len2;
981                    v_id2 = one_ring_v_ids2[end_idx2];
982
983                    #[cfg(feature = "rerun")]
984                    self.log_vert_rerun("pairs_end_idx2", v_id2);
985                }
986
987                end_cap = ConnectPairCap::AlreadyConnected;
988            } else if connected_v_ids.contains(&v_id2) {
989                while !connected_v_ids.contains(&v_id1) {
990                    end_idx1 = (end_idx1 + 1) % len1;
991                    v_id1 = one_ring_v_ids1[end_idx1];
992
993                    #[cfg(feature = "rerun")]
994                    self.log_vert_rerun("pairs_end_idx1", v_id1);
995                }
996
997                end_cap = ConnectPairCap::AlreadyConnected;
998            }
999
1000            if !matches!(end_cap, ConnectPairCap::Open) {
1001                range_pairs_to_connect.push(ConnectPair::new(
1002                    start_idx1..=end_idx1,
1003                    len1,
1004                    start_idx2..=end_idx2,
1005                    len2,
1006                    start_cap,
1007                ));
1008
1009                self.remember_range_pair_connections(
1010                    start_idx1,
1011                    end_idx1,
1012                    start_idx2,
1013                    end_idx2,
1014                    len1,
1015                    len2,
1016                    one_ring_v_ids1,
1017                    one_ring_v_ids2,
1018                    one_ring_he_ids1,
1019                    one_ring_he_ids2,
1020                    &mut connected_v_ids,
1021                    &mut connected_he_ids,
1022                );
1023
1024                if let Some((idx1, idx2, start)) = self.find_start_indices(
1025                    one_ring_v_ids1,
1026                    one_ring_v_ids2,
1027                    one_ring_he_ids1,
1028                    one_ring_he_ids2,
1029                    shared_v_ids,
1030                    shared_he_ids,
1031                    &connected_v_ids,
1032                    &connected_he_ids,
1033                ) {
1034                    start_idx1 = idx1;
1035                    start_idx2 = idx2;
1036                    start_cap = start;
1037                } else {
1038                    return range_pairs_to_connect;
1039                }
1040
1041                end_idx1 = (start_idx1 + 1) % len1;
1042                end_idx2 = (start_idx2 + 1) % len2;
1043
1044                #[cfg(feature = "rerun")]
1045                {
1046                    self.log_verts_w_labels_rerun(
1047                        "pairs_start_idx",
1048                        &[one_ring_v_ids1[start_idx1], one_ring_v_ids2[start_idx2]],
1049                        &["1", "2"],
1050                    );
1051                    self.log_vert_rerun("pairs_end_idx1", one_ring_v_ids1[end_idx1]);
1052                    self.log_vert_rerun("pairs_end_idx2", one_ring_v_ids2[end_idx2]);
1053                }
1054
1055                if start_idx1 == orig_start_idx1 {
1056                    break;
1057                }
1058            } else {
1059                end_idx1 = (end_idx1 + 1) % len1;
1060                end_idx2 = (end_idx2 + 1) % len2;
1061
1062                #[cfg(feature = "rerun")]
1063                {
1064                    self.log_vert_rerun("pairs_end_idx1", one_ring_v_ids1[end_idx1]);
1065                    self.log_vert_rerun("pairs_end_idx2", one_ring_v_ids2[end_idx2]);
1066                }
1067            }
1068        }
1069
1070        let diff1 = (start_idx1 as i32 - end_idx1 as i32).unsigned_abs() as usize;
1071        let diff1 = diff1.min(len1 - diff1);
1072
1073        let diff2 = (start_idx2 as i32 - end_idx2 as i32).unsigned_abs() as usize;
1074        let diff2 = diff2.min(len2 - diff2);
1075
1076        let cap = if shared_v_ids.is_empty() {
1077            ConnectPairCap::Open
1078        } else {
1079            ConnectPairCap::Closed
1080        };
1081
1082        if range_pairs_to_connect.is_empty() {
1083            let (end1, end2) = if matches!(cap, ConnectPairCap::Closed) {
1084                (orig_start_idx1, orig_start_idx2)
1085            } else {
1086                (
1087                    (orig_start_idx1 + len1 - 1).rem_euclid(len1),
1088                    (orig_start_idx2 + len2 - 1).rem_euclid(len2),
1089                )
1090            };
1091
1092            range_pairs_to_connect.push(ConnectPair::new(
1093                orig_start_idx1..=end1,
1094                len1,
1095                orig_start_idx2..=end2,
1096                len2,
1097                cap,
1098            ));
1099        } else if diff1 > 1 || diff2 > 1 || range_pairs_to_connect.is_empty() {
1100            range_pairs_to_connect.push(ConnectPair::new(
1101                start_idx1..=end_idx1,
1102                len1,
1103                start_idx2..=end_idx2,
1104                len2,
1105                cap,
1106            ));
1107        }
1108
1109        #[cfg(feature = "rerun")]
1110        {
1111            for range_pair in &range_pairs_to_connect {
1112                let mut v1s = vec![];
1113                let mut v2s = vec![];
1114                let mut l1s = vec![];
1115                let mut l2s = vec![];
1116
1117                for i in range_pair.ranges[0].clone() {
1118                    let v_id = one_ring_v_ids1[i % len1];
1119                    v1s.push(v_id);
1120                    l1s.push(format!("{i} - {v_id:?}"));
1121                }
1122                for i in range_pair.ranges[1].clone() {
1123                    let v_id = one_ring_v_ids2[i % len2];
1124                    v2s.push(v_id);
1125                    l2s.push(format!("{i} - {v_id:?}"));
1126                }
1127
1128                self.log_verts_w_labels_rerun("range_pair_1", &v1s, &l1s);
1129                self.log_verts_w_labels_rerun("range_pair_2", &v2s, &l2s);
1130            }
1131        }
1132
1133        range_pairs_to_connect
1134    }
1135
1136    #[instrument(skip(self))]
1137    #[allow(clippy::too_many_arguments)]
1138    fn remember_range_pair_connections(
1139        &mut self,
1140        start_idx1: usize,
1141        end_idx1: usize,
1142        start_idx2: usize,
1143        end_idx2: usize,
1144        len1: usize,
1145        len2: usize,
1146        one_ring_v_ids1: &[VertexId],
1147        one_ring_v_ids2: &[VertexId],
1148        one_ring_he_ids1: &[HalfedgeId],
1149        one_ring_he_ids2: &[HalfedgeId],
1150        connected_v_ids: &mut HashSet<VertexId>,
1151        connected_he_ids: &mut HashSet<HalfedgeId>,
1152    ) {
1153        let mut idx = start_idx1;
1154        loop {
1155            connected_v_ids.insert(one_ring_v_ids1[idx]);
1156            if idx == end_idx1 {
1157                break;
1158            }
1159            let he_id = one_ring_he_ids1[idx];
1160            connected_he_ids.insert(he_id);
1161
1162            // could have been deleted by neighbor face deletion
1163            if let Some(he) = self.halfedges.get(he_id) {
1164                // halfedge existence already checked in `one_ring()`
1165                if let Some(twin_id) = he.twin {
1166                    connected_he_ids.insert(twin_id);
1167                } else {
1168                    error!("halfedge {:?} has no twin", he_id);
1169                }
1170            }
1171
1172            idx = (idx + 1) % len1;
1173        }
1174
1175        idx = start_idx2;
1176        loop {
1177            connected_v_ids.insert(one_ring_v_ids2[idx]);
1178            if idx == end_idx2 {
1179                break;
1180            }
1181            let he_id = one_ring_he_ids2[idx];
1182            connected_he_ids.insert(he_id);
1183
1184            // could have been deleted by neighbor face deletion
1185            if let Some(he) = self.halfedges.get(he_id) {
1186                // halfedge existence already checked in `one_ring()`
1187                if let Some(twin_id) = he.twin {
1188                    connected_he_ids.insert(twin_id);
1189                } else {
1190                    error!("halfedge {:?} has no twin", he_id);
1191                }
1192            }
1193
1194            idx = (idx + 1) % len2;
1195        }
1196    }
1197
1198    #[instrument(skip(self))]
1199    #[allow(clippy::too_many_arguments)]
1200    fn find_shared_start_indices_from_ring(
1201        &self,
1202        one_ring_v_ids: &[VertexId],
1203        other_one_ring_v_ids: &[VertexId],
1204        one_ring_he_ids: &[HalfedgeId],
1205        other_one_ring_he_ids: &[HalfedgeId],
1206        shared_v_ids: &HashSet<VertexId>,
1207        shared_he_ids: &HashSet<HalfedgeId>,
1208        connected_v_ids: &HashSet<VertexId>,
1209        connected_he_ids: &HashSet<HalfedgeId>,
1210    ) -> Option<(usize, usize, ConnectPairCap)> {
1211        let len = one_ring_v_ids.len();
1212        let other_len = other_one_ring_v_ids.len();
1213
1214        debug_assert_eq!(len, one_ring_he_ids.len());
1215        debug_assert_eq!(other_len, other_one_ring_he_ids.len());
1216
1217        for (he_idx, &he_id1) in one_ring_he_ids.iter().enumerate() {
1218            if self.halfedges.contains_key(he_id1)
1219                && !shared_he_ids.contains(&he_id1)
1220                && !connected_he_ids.contains(&he_id1)
1221            {
1222                // walk backwards until we find a connection between the two one-rings
1223
1224                let mut start_idx = he_idx;
1225                let mut start_he_id = &one_ring_he_ids[start_idx];
1226
1227                let mut start_v_id = &one_ring_v_ids[start_idx];
1228
1229                while self.halfedges.contains_key(*start_he_id)
1230                    && !shared_he_ids.contains(start_he_id)
1231                    && !connected_he_ids.contains(start_he_id)
1232                    && self.vertices.contains_key(*start_v_id)
1233                    && !shared_v_ids.contains(start_v_id)
1234                    && !connected_v_ids.contains(start_v_id)
1235                {
1236                    start_idx = (start_idx + len - 1) % len;
1237                    start_he_id = &one_ring_he_ids[start_idx];
1238                    start_v_id = &one_ring_v_ids[start_idx];
1239
1240                    if start_idx == he_idx {
1241                        return None;
1242                    }
1243                }
1244
1245                // find the corresponding vertex in the other one-ring
1246
1247                // either it's the same vertex ...
1248                if let Some(other_idx) = other_one_ring_v_ids
1249                    .iter()
1250                    .position(|v_id| start_v_id == v_id)
1251                {
1252                    return Some((start_idx, other_idx, ConnectPairCap::Closed));
1253                }
1254
1255                // ... or it's a different vertex that is connected by a halfedge
1256                for (other_idx, &other_v_id) in other_one_ring_v_ids.iter().enumerate() {
1257                    if self.halfedge_from_to(*start_v_id, other_v_id).is_some() {
1258                        let other_he_id = &other_one_ring_he_ids[other_idx];
1259
1260                        if self.halfedges.contains_key(*other_he_id)
1261                            && !shared_he_ids.contains(other_he_id)
1262                            && !connected_he_ids.contains(other_he_id)
1263                        {
1264                            return Some((start_idx, other_idx, ConnectPairCap::AlreadyConnected));
1265                        }
1266                    }
1267                }
1268            }
1269        }
1270
1271        None
1272    }
1273
1274    #[allow(clippy::too_many_arguments)]
1275    #[instrument(skip(self))]
1276    fn find_start_indices(
1277        &self,
1278        one_ring_v_ids1: &[VertexId],
1279        one_ring_v_ids2: &[VertexId],
1280        one_ring_he_ids1: &[HalfedgeId],
1281        one_ring_he_ids2: &[HalfedgeId],
1282        shared_v_ids: &HashSet<VertexId>,
1283        shared_he_ids: &HashSet<HalfedgeId>,
1284        connected_v_ids: &HashSet<VertexId>,
1285        connected_he_ids: &HashSet<HalfedgeId>,
1286    ) -> Option<(usize, usize, ConnectPairCap)> {
1287        if !shared_v_ids.is_empty() || !connected_v_ids.is_empty() {
1288            self.find_shared_start_indices_from_ring(
1289                one_ring_v_ids1,
1290                one_ring_v_ids2,
1291                one_ring_he_ids1,
1292                one_ring_he_ids2,
1293                shared_v_ids,
1294                shared_he_ids,
1295                connected_v_ids,
1296                connected_he_ids,
1297            )
1298        } else {
1299            let first_v_id = one_ring_v_ids1[0];
1300            let first_pos = *self
1301                .positions
1302                .get(first_v_id)
1303                .or_else(error_none!("Position not found"))?;
1304
1305            let mut min_dist_sqr = f32::INFINITY;
1306            let mut start_idx2 = 0;
1307
1308            for (idx, v_id) in one_ring_v_ids2.iter().enumerate() {
1309                let pos = self
1310                    .positions
1311                    .get(*v_id)
1312                    .or_else(error_none!("Position not found"))?;
1313
1314                let dist_sqr = pos.distance_squared(first_pos);
1315
1316                if dist_sqr < min_dist_sqr {
1317                    min_dist_sqr = dist_sqr;
1318                    start_idx2 = idx;
1319                }
1320            }
1321
1322            Some((0, start_idx2, ConnectPairCap::Open))
1323        }
1324    }
1325}
1326
1327#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1328enum ConnectPairCap {
1329    /// Indices reference the same vertex, thus forming a closed polygon.
1330    Closed,
1331    /// Indices reference different vertices, thus forming an open polygon.
1332    Open,
1333    /// Indices are already connected by neighboring faces, so no need to connect them again.
1334    AlreadyConnected,
1335}
1336
1337/// Two ranges of elements that need to be connected by faces.
1338#[derive(Debug)]
1339struct ConnectPair {
1340    /// Wether the first indices in the two ranges reference the same vertex, thus forming a closed polygon.
1341    start_cap: ConnectPairCap,
1342
1343    /// The pair of ranges of vertex indices that need to be connected.
1344    ranges: [RangeInclusive<usize>; 2],
1345}
1346
1347impl ConnectPair {
1348    fn new(
1349        mut range1: RangeInclusive<usize>,
1350        len1: usize,
1351        mut range2: RangeInclusive<usize>,
1352        len2: usize,
1353        start_cap: ConnectPairCap,
1354    ) -> Self {
1355        if range1.end() <= range1.start() {
1356            range1 = (*range1.start())..=(*range1.end() + len1);
1357        }
1358        if range2.end() <= range2.start() {
1359            range2 = (*range2.start())..=(*range2.end() + len2);
1360        }
1361
1362        ConnectPair {
1363            start_cap,
1364            ranges: [range1, range2],
1365        }
1366    }
1367
1368    // This is a modified Bresenham algorithm usually used for drawing lines on a grid.
1369    fn compute_pairings(&self) -> Vec<Pairing> {
1370        let range1 = self.ranges[0].clone();
1371        let range2 = self.ranges[1].clone();
1372
1373        let count1 = range1.clone().count() as i32;
1374        let count2 = range2.clone().count() as i32;
1375
1376        let (single_range_idx, single_count, other_count, single_range, other_range) =
1377            if count1 > count2 {
1378                (1, count2, count1, range2, range1)
1379            } else {
1380                (0, count1, count2, range1, range2)
1381            };
1382
1383        let mut single_idx_in_range = *single_range.start();
1384
1385        if single_count == 0 {
1386            return vec![];
1387        }
1388        if single_count == 1 {
1389            return vec![Pairing {
1390                single_range_idx,
1391                single_idx_in_range,
1392                other_range,
1393            }];
1394        }
1395
1396        let mut error = 2 * single_count - other_count;
1397
1398        let mut other_start = *other_range.start();
1399        let mut other_end = other_start;
1400
1401        let mut pairings = Vec::new();
1402
1403        while other_end <= *other_range.end() {
1404            if error > 0 {
1405                pairings.push(Pairing {
1406                    single_range_idx,
1407                    single_idx_in_range,
1408                    other_range: other_start..=other_end,
1409                });
1410
1411                other_start = other_end + 1;
1412                single_idx_in_range += 1;
1413                error += 2 * (single_count - other_count);
1414            } else {
1415                error += 2 * single_count;
1416            }
1417
1418            other_end += 1;
1419        }
1420
1421        if let Some(last_pairing) = pairings.last_mut() {
1422            last_pairing.other_range = *last_pairing.other_range.start()..=*other_range.end();
1423        }
1424
1425        pairings
1426    }
1427}
1428
1429/// Pairs one element from one range with one or more elements from the other range.
1430#[derive(Debug, Clone)]
1431struct Pairing {
1432    /// The index of the range in which each single element is paired to one or more elements in the other range.
1433    /// Can only be `0` or `1`.
1434    single_range_idx: usize,
1435
1436    /// The index of the single element in the range referenced by `single_range_idx`.
1437    single_idx_in_range: usize,
1438
1439    /// The range of elements in the other range that are paired to the single element referenced in `single_idx_in_range`.
1440    other_range: RangeInclusive<usize>,
1441}
1442
1443impl Pairing {
1444    fn new_triangle(
1445        single_range_idx: usize,
1446        single_idx_in_range: usize,
1447        other_idx1: usize,
1448        other_idx2: usize,
1449        other_len: usize,
1450    ) -> Self {
1451        let other_range = if (other_idx1 as i32 - other_idx2 as i32).abs() == 1 {
1452            other_idx1.min(other_idx2)..=other_idx1.max(other_idx2)
1453        } else {
1454            other_idx1.max(other_idx2)..=other_idx1.min(other_idx2) + other_len
1455        };
1456
1457        Self {
1458            single_range_idx,
1459            single_idx_in_range,
1460            other_range,
1461        }
1462    }
1463
1464    fn all_vertex_ids(&self, v_ids: [&[VertexId]; 2]) -> Vec<VertexId> {
1465        let single_ids = &v_ids[self.single_range_idx];
1466        let other_ids = &v_ids[1 - self.single_range_idx];
1467
1468        let mut all_ids = vec![];
1469
1470        all_ids.push(single_ids[self.single_idx_in_range % single_ids.len()]);
1471
1472        let mut idx = *self.other_range.start() % other_ids.len();
1473        all_ids.push(other_ids[idx]);
1474        while idx != *self.other_range.end() % other_ids.len() {
1475            idx += 1;
1476            idx %= other_ids.len();
1477            all_ids.push(other_ids[idx]);
1478        }
1479
1480        all_ids
1481    }
1482
1483    fn first_pair(&self, v_ids: [&[VertexId]; 2]) -> (VertexId, VertexId) {
1484        let single_ids = &v_ids[self.single_range_idx];
1485        let other_ids = &v_ids[1 - self.single_range_idx];
1486
1487        let single_v_id = single_ids[self.single_idx_in_range % single_ids.len()];
1488        let other_v_id = other_ids[*self.other_range.start() % other_ids.len()];
1489
1490        (single_v_id, other_v_id)
1491    }
1492
1493    fn last_pair(&self, v_ids: [&[VertexId]; 2]) -> (VertexId, VertexId) {
1494        let single_ids = &v_ids[self.single_range_idx];
1495        let other_ids = &v_ids[1 - self.single_range_idx];
1496
1497        let single_v_id = single_ids[self.single_idx_in_range % single_ids.len()];
1498        let other_v_id = other_ids[*self.other_range.end() % other_ids.len()];
1499
1500        (single_v_id, other_v_id)
1501    }
1502
1503    fn fill_faces(&self, v_ids: [&[VertexId]; 2]) -> Vec<PlannedFace> {
1504        let single_ids = v_ids[self.single_range_idx];
1505        let other_ids = v_ids[1 - self.single_range_idx];
1506
1507        let single_v_id = single_ids[self.single_idx_in_range % single_ids.len()];
1508        let mut faces = Vec::<PlannedFace>::new();
1509
1510        let mut others = self.other_range.clone();
1511
1512        if others.start() == others.end() {
1513            return faces;
1514        }
1515
1516        let mut prev_other_v_id = other_ids[others.next().unwrap() % other_ids.len()];
1517        let mut face_order = PlannedFaceOrder::Middle;
1518
1519        if prev_other_v_id == single_v_id {
1520            face_order = PlannedFaceOrder::Start;
1521
1522            if let Some(second_idx) = others.next() {
1523                prev_other_v_id = other_ids[second_idx % other_ids.len()];
1524            } else {
1525                return faces;
1526            }
1527        }
1528
1529        for other_idx in others {
1530            let other_v_id = other_ids[other_idx % other_ids.len()];
1531
1532            if other_v_id == single_v_id {
1533                break;
1534            }
1535
1536            // make sure the triangle vertices are CCW
1537            if self.single_range_idx == 0 {
1538                faces.push(PlannedFace::new(
1539                    prev_other_v_id,
1540                    single_v_id,
1541                    other_v_id,
1542                    face_order,
1543                ));
1544            } else {
1545                faces.push(PlannedFace::new(
1546                    prev_other_v_id,
1547                    other_v_id,
1548                    single_v_id,
1549                    face_order,
1550                ));
1551            }
1552
1553            face_order = PlannedFaceOrder::Middle;
1554            prev_other_v_id = other_v_id;
1555        }
1556
1557        faces
1558    }
1559
1560    #[cfg(feature = "rerun")]
1561    fn log_rerun(
1562        &self,
1563        label: impl AsRef<str>,
1564        v_ids: [&[VertexId]; 2],
1565        mesh_graph: &MeshGraph,
1566    ) -> Option<()> {
1567        use crate::utils::vec3_array;
1568
1569        let single_v_ids = v_ids[self.single_range_idx];
1570        let other_v_ids = v_ids[1 - self.single_range_idx];
1571
1572        let single_v_id = single_v_ids[self.single_idx_in_range];
1573        let single_pos = vec3_array(mesh_graph.positions.get(single_v_id)?);
1574
1575        let mut other_pos = vec![];
1576        let mut idx = self.other_range.start() % other_v_ids.len();
1577
1578        other_pos.push(vec3_array(mesh_graph.positions.get(other_v_ids[idx])?));
1579        while idx != *self.other_range.end() % other_v_ids.len() {
1580            idx += 1;
1581            idx %= other_v_ids.len();
1582            other_pos.push(vec3_array(mesh_graph.positions.get(other_v_ids[idx])?));
1583        }
1584
1585        crate::RR
1586            .log(
1587                format!("pairing/{}/single", label.as_ref()),
1588                &rerun::Points3D::new([single_pos.clone()]),
1589            )
1590            .unwrap();
1591        crate::RR
1592            .log(
1593                format!("pairing/{}/other", label.as_ref()),
1594                &rerun::LineStrips3D::new(
1595                    other_pos
1596                        .into_iter()
1597                        .map(|other_pos| [single_pos.clone(), other_pos]),
1598                ),
1599            )
1600            .unwrap();
1601
1602        Some(())
1603    }
1604}
1605
1606/// Represents a triangle that is planned to be added to the mesh graph.
1607///
1608/// v1 ()◀───────────────() new_he_v2
1609///      ╲              ▲
1610///       ╲            ╱
1611///        ╲          ╱
1612///         ╲        ╱
1613///          ╲      ╱
1614///           ╲    ╱
1615///            ▼  ╱
1616///             ()
1617///         new_he_v1
1618///
1619#[derive(Debug)]
1620struct PlannedFace {
1621    order: PlannedFaceOrder,
1622    v1: VertexId,
1623    new_he_v1: VertexId,
1624    new_he_v2: VertexId,
1625}
1626
1627#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1628enum PlannedFaceOrder {
1629    Start,
1630    Middle,
1631    End,
1632    Single,
1633}
1634
1635impl PlannedFace {
1636    fn new(
1637        v_id1: VertexId,
1638        new_he_v_id1: VertexId,
1639        new_he_v_id2: VertexId,
1640        order: PlannedFaceOrder,
1641    ) -> Self {
1642        PlannedFace {
1643            order,
1644            v1: v_id1,
1645            new_he_v1: new_he_v_id1,
1646            new_he_v2: new_he_v_id2,
1647        }
1648    }
1649
1650    #[instrument(skip(mesh_graph))]
1651    fn add_to_mesh_graph(
1652        &self,
1653        mesh_graph: &mut MeshGraph,
1654    ) -> Option<(Option<HalfedgeId>, AddFace)> {
1655        #[cfg(feature = "rerun")]
1656        self.log_rerun("add_to_mesh_graph", mesh_graph);
1657
1658        let add_or_get_edge1 = mesh_graph.add_or_get_boundary_edge(self.v1, self.new_he_v1)?;
1659        let add_or_get_edge2 =
1660            mesh_graph.add_or_get_boundary_edge(self.new_he_v1, self.new_he_v2)?;
1661
1662        let mut add_face = mesh_graph.add_face_from_halfedges(
1663            add_or_get_edge1.start_to_end_he_id,
1664            add_or_get_edge2.start_to_end_he_id,
1665        )?;
1666
1667        if add_or_get_edge1.new_start_to_end {
1668            add_face
1669                .halfedge_ids
1670                .push(add_or_get_edge1.start_to_end_he_id);
1671        }
1672        if add_or_get_edge1.new_twin {
1673            add_face.halfedge_ids.push(add_or_get_edge1.twin_he_id);
1674        }
1675        if add_or_get_edge2.new_start_to_end {
1676            add_face
1677                .halfedge_ids
1678                .push(add_or_get_edge2.start_to_end_he_id);
1679        }
1680        if add_or_get_edge2.new_twin {
1681            add_face.halfedge_ids.push(add_or_get_edge2.twin_he_id);
1682        }
1683
1684        Some((
1685            if matches!(
1686                self.order,
1687                PlannedFaceOrder::Start | PlannedFaceOrder::Middle
1688            ) {
1689                Some(add_or_get_edge2.twin_he_id)
1690            } else {
1691                None
1692            },
1693            add_face,
1694        ))
1695    }
1696
1697    #[instrument(skip(mesh_graph))]
1698    fn add_to_mesh_graph_and_he(
1699        &self,
1700        mesh_graph: &mut MeshGraph,
1701        existing_he_id: HalfedgeId,
1702    ) -> Option<(Option<HalfedgeId>, AddFace)> {
1703        #[cfg(feature = "rerun")]
1704        self.log_rerun("add_to_mesh_graph_and_he", mesh_graph);
1705
1706        match self.order {
1707            PlannedFaceOrder::Middle => {
1708                let AddEdge {
1709                    start_to_end_he_id,
1710                    twin_he_id,
1711                } = mesh_graph.add_edge(self.new_he_v1, self.new_he_v2)?;
1712
1713                let mut add_face =
1714                    mesh_graph.add_face_from_halfedges(existing_he_id, start_to_end_he_id)?;
1715                add_face.halfedge_ids.push(start_to_end_he_id);
1716                add_face.halfedge_ids.push(twin_he_id);
1717
1718                Some((Some(twin_he_id), add_face))
1719            }
1720            PlannedFaceOrder::End => {
1721                let add_or_get_edge =
1722                    mesh_graph.add_or_get_boundary_edge(self.new_he_v1, self.new_he_v2)?;
1723
1724                let mut add_face = mesh_graph
1725                    .add_face_from_halfedges(existing_he_id, add_or_get_edge.start_to_end_he_id)?;
1726                if add_or_get_edge.new_start_to_end {
1727                    add_face
1728                        .halfedge_ids
1729                        .push(add_or_get_edge.start_to_end_he_id);
1730                }
1731                if add_or_get_edge.new_twin {
1732                    add_face.halfedge_ids.push(add_or_get_edge.twin_he_id);
1733                }
1734
1735                Some((None, add_face))
1736            }
1737            _ => {
1738                error!("Invalid face order {:?}", self.order);
1739                None
1740            }
1741        }
1742    }
1743
1744    #[cfg(feature = "rerun")]
1745    fn log_rerun(&self, label: &str, mesh_graph: &MeshGraph) {
1746        use crate::{RR, utils::vec3_array};
1747
1748        let (positions, labels): (Vec<_>, Vec<_>) =
1749            [(self.v1, "1"), (self.new_he_v1, "2"), (self.new_he_v2, "3")]
1750                .into_iter()
1751                .filter_map(|(v_id, labl)| {
1752                    mesh_graph.positions.get(v_id).and_then(|a| Some((a, labl)))
1753                })
1754                .unzip();
1755
1756        RR.log(
1757            format!("meshgraph/planned_face/{label}/positions"),
1758            &rerun::Points3D::new(positions.iter().map(vec3_array)).with_labels(labels),
1759        )
1760        .unwrap();
1761
1762        RR.log(
1763            format!("meshgraph/planned_face/{label}/edges"),
1764            &rerun::Arrows3D::from_vectors(
1765                positions
1766                    .iter()
1767                    .circular_array_windows()
1768                    .map(|[a, b]| vec3_array(*b - *a)),
1769            )
1770            .with_origins(positions.iter().map(vec3_array)),
1771        )
1772        .unwrap();
1773    }
1774}