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, AddOrGetEdge, FaceId, HalfedgeId, MeshGraph, Vertex, VertexId, error_none,
12    ops::{add::AddFace, collapse::CollapseEdge},
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        // halfedges' existence already checked in `one_ring()`.
49        let one_ring_v_ids1 = vertex1
50            .one_ring(self)
51            .map(|he_id| self.halfedges[he_id].end_vertex)
52            .collect_vec();
53        let mut one_ring_v_ids2 = vertex2
54            .one_ring(self)
55            .map(|he_id| self.halfedges[he_id].end_vertex)
56            .collect_vec();
57        one_ring_v_ids2.reverse();
58
59        if one_ring_v_ids1.len() < 3 || one_ring_v_ids2.len() < 3 {
60            error!("One rings are too small");
61            return result;
62        }
63
64        let one_ring_set1 = HashSet::<VertexId>::from_iter(one_ring_v_ids1.iter().copied());
65        let one_ring_set2 = HashSet::<VertexId>::from_iter(one_ring_v_ids2.iter().copied());
66        let shared_v_ids =
67            HashSet::<VertexId>::from_iter(one_ring_set1.intersection(&one_ring_set2).copied());
68
69        if self.check_and_flip_single_shared_he(
70            &one_ring_v_ids1,
71            &one_ring_v_ids2,
72            &shared_v_ids,
73            flip_threshold_sqr,
74            &mut result,
75        ) {
76            return result;
77        }
78
79        self.remove_neighbour_faces(&vertex1, &vertex2, &mut result);
80
81        #[cfg(feature = "rerun")]
82        self.log_rerun();
83
84        let range_pairs_to_connect =
85            self.compute_range_pairs_to_connect(&one_ring_v_ids1, &one_ring_v_ids2, &shared_v_ids);
86
87        if range_pairs_to_connect.is_empty() {
88            error!("No range pairs to connect");
89            return result;
90        }
91
92        tracing::info!("Range pairs to connect: {range_pairs_to_connect:#?}");
93
94        let planned_faces =
95            self.plan_new_faces(&range_pairs_to_connect, &one_ring_v_ids1, &one_ring_v_ids2);
96
97        self.add_planned_faces(
98            planned_faces,
99            &one_ring_set1,
100            &one_ring_set2,
101            marked_halfedges,
102            &mut result,
103        );
104
105        #[cfg(feature = "rerun")]
106        self.log_rerun();
107
108        self.cleanup_bookkeeping(
109            &one_ring_v_ids1,
110            &one_ring_v_ids2,
111            marked_vertices,
112            marked_halfedges,
113            &mut result,
114        );
115
116        tracing::info!("smoothing vertices");
117        self.smooth_vertices(
118            one_ring_v_ids1
119                .iter()
120                .chain(&one_ring_v_ids2)
121                .chain(&result.added_vertices)
122                .copied(),
123        );
124
125        result
126    }
127
128    fn cleanup_bookkeeping(
129        &mut self,
130        one_ring_v_ids1: &[VertexId],
131        one_ring_v_ids2: &[VertexId],
132        marked_vertices: &mut HashSet<VertexId>,
133        marked_halfedges: &mut HashSet<HalfedgeId>,
134        result: &mut MergeVerticesOneRing,
135    ) {
136        for vertex_id in one_ring_v_ids1.iter().chain(one_ring_v_ids2).copied() {
137            if !self.vertices.contains_key(vertex_id) {
138                continue;
139            }
140
141            let cleanup = self.make_vertex_neighborhood_manifold(vertex_id);
142
143            if !cleanup.added_vertices.is_empty() {
144                marked_vertices.insert(vertex_id);
145            }
146
147            result.added_vertices.extend(cleanup.added_vertices.clone());
148            marked_vertices.extend(cleanup.added_vertices);
149
150            for removed_v_id in cleanup.removed_vertices {
151                if result.added_vertices.contains(&removed_v_id) {
152                    result.added_vertices.retain(|&v_id| v_id != removed_v_id);
153                } else {
154                    result.removed_vertices.push(removed_v_id);
155                }
156
157                marked_vertices.remove(&removed_v_id);
158            }
159
160            for removed_he_id in cleanup.removed_halfedges {
161                if result.added_halfedges.contains(&removed_he_id) {
162                    result
163                        .added_halfedges
164                        .retain(|&he_id| he_id != removed_he_id);
165                } else {
166                    result.removed_halfedges.push(removed_he_id);
167                }
168
169                marked_halfedges.remove(&removed_he_id);
170            }
171
172            for removed_face_id in cleanup.removed_faces {
173                if result.added_faces.contains(&removed_face_id) {
174                    result
175                        .added_faces
176                        .retain(|&face_id| face_id != removed_face_id);
177                } else {
178                    result.removed_faces.push(removed_face_id);
179                }
180            }
181        }
182    }
183
184    fn add_planned_faces(
185        &mut self,
186        planned_faces: Vec<PlannedFace>,
187        one_ring_set1: &HashSet<VertexId>,
188        one_ring_set2: &HashSet<VertexId>,
189        marked_halfedges: &mut HashSet<HalfedgeId>,
190        result: &mut MergeVerticesOneRing,
191    ) {
192        let mut prev_he = None;
193
194        for planned_face in planned_faces {
195            let inserted = if let Some(prev_he) = prev_he {
196                planned_face.add_to_meshgraph_and_he(self, prev_he)
197            } else {
198                planned_face.add_to_meshgraph(self)
199            };
200
201            let inserted = unwrap_or_return!(inserted, "Failed to add face");
202
203            prev_he = inserted.0;
204            let inserted = inserted.1;
205
206            for he_id in inserted.halfedge_ids.iter().copied() {
207                // just inserted => must exist
208                let he = self.halfedges[he_id];
209                let start_v_id =
210                    unwrap_or_return!(he.start_vertex(self), "Couldn't find start vertex");
211                let end_v_id = he.end_vertex;
212
213                if one_ring_set1.contains(&start_v_id) && one_ring_set2.contains(&end_v_id)
214                    || one_ring_set2.contains(&start_v_id) && one_ring_set1.contains(&end_v_id)
215                {
216                    marked_halfedges.insert(he_id);
217                }
218            }
219
220            result.added_halfedges.extend(inserted.halfedge_ids);
221            result.added_faces.push(inserted.face_id);
222        }
223    }
224
225    fn remove_neighbour_faces(
226        &mut self,
227        vertex1: &Vertex,
228        vertex2: &Vertex,
229        result: &mut MergeVerticesOneRing,
230    ) {
231        let face_ids1 = vertex1.faces(self).collect_vec();
232        let face_ids2 = vertex2.faces(self).collect_vec();
233
234        for face_id in face_ids1.into_iter().chain(face_ids2) {
235            let (del_v, del_he) = self.remove_face(face_id);
236
237            result.removed_faces.push(face_id);
238            result.removed_vertices.extend(del_v);
239            result.removed_halfedges.extend(del_he);
240        }
241    }
242
243    #[instrument(skip_all)]
244    fn flip_and_collapse_single_shared_edge_if_below_threshold(
245        &mut self,
246        single_shared_he_id: HalfedgeId,
247        flip_threshold_sqr: f32,
248        result: &mut MergeVerticesOneRing,
249    ) -> bool {
250        let he = self.halfedges[single_shared_he_id];
251
252        let twin_id = unwrap_or_return!(he.twin, "Twin not found", false);
253        let twin = unwrap_or_return!(self.halfedges.get(twin_id), "Twin not found", false);
254
255        let other_v_id1 =
256            unwrap_or_return!(he.opposite_vertex(self), "Opposite vertex not found", false);
257        let other_v_id2 = unwrap_or_return!(
258            twin.opposite_vertex(self),
259            "Opposite vertex not found",
260            false
261        );
262
263        let pos1 = *unwrap_or_return!(self.positions.get(other_v_id1), "Position not found", false);
264        let pos2 = *unwrap_or_return!(self.positions.get(other_v_id2), "Position not found", false);
265
266        if pos1.distance_squared(pos2) <= flip_threshold_sqr {
267            tracing::info!("Flipping edge {single_shared_he_id:?}");
268
269            self.flip_edge(single_shared_he_id);
270
271            let CollapseEdge {
272                removed_vertices,
273                removed_halfedges,
274                removed_faces,
275                added_vertices,
276            } = self.collapse_edge(single_shared_he_id);
277
278            result.added_vertices.extend(added_vertices);
279            result.removed_vertices.extend(removed_vertices);
280            result.removed_halfedges.extend(removed_halfedges);
281            result.removed_faces.extend(removed_faces);
282
283            true
284        } else {
285            false
286        }
287    }
288
289    fn plan_new_faces(
290        &self,
291        range_pairs_to_connect: &[ConnectPair],
292        one_ring_v_ids1: &[VertexId],
293        one_ring_v_ids2: &[VertexId],
294    ) -> Vec<PlannedFace> {
295        let mut planned_faces = Vec::with_capacity(one_ring_v_ids1.len());
296
297        for range_pair_to_connect in range_pairs_to_connect {
298            let start_pairing_index = planned_faces.len();
299
300            let pairings = range_pair_to_connect.compute_pairings();
301
302            tracing::info!("Pairings: {:#?}", pairings);
303
304            if pairings.is_empty() {
305                continue;
306            }
307
308            let mut prev_single_v_id = None;
309            let mut prev_other_v_id = None;
310
311            if !range_pair_to_connect.closed {
312                let last_pairing = pairings.last().unwrap();
313
314                let (s, o) = last_pairing.last_pair([one_ring_v_ids1, one_ring_v_ids2]);
315
316                prev_single_v_id = Some(s);
317                prev_other_v_id = Some(o);
318            }
319
320            for pairing in pairings {
321                if let Some(prev_single_v_id) = prev_single_v_id
322                    && let Some(prev_other_v_id) = prev_other_v_id
323                {
324                    // add the quad between the previous and current pairings
325                    let (single_v_id, other_v_id) =
326                        pairing.first_pair([one_ring_v_ids1, one_ring_v_ids2]);
327
328                    let face_order = if prev_single_v_id == prev_other_v_id {
329                        PlannedFaceOrder::Start
330                    } else if single_v_id == other_v_id {
331                        PlannedFaceOrder::End
332                    } else {
333                        PlannedFaceOrder::Middle
334                    };
335
336                    // make sure the triangle vertices are CCW
337                    if pairing.single_range_idx == 0 {
338                        if prev_single_v_id != prev_other_v_id {
339                            planned_faces.push(PlannedFace::new(
340                                prev_single_v_id,
341                                single_v_id,
342                                prev_other_v_id,
343                                face_order,
344                            ));
345                        }
346                        if single_v_id != other_v_id {
347                            planned_faces.push(PlannedFace::new(
348                                prev_other_v_id,
349                                single_v_id,
350                                other_v_id,
351                                face_order,
352                            ));
353                        }
354                    } else {
355                        if prev_single_v_id != prev_other_v_id {
356                            planned_faces.push(PlannedFace::new(
357                                prev_single_v_id,
358                                prev_other_v_id,
359                                single_v_id,
360                                face_order,
361                            ));
362                        }
363                        if single_v_id != other_v_id {
364                            planned_faces.push(PlannedFace::new(
365                                prev_other_v_id,
366                                other_v_id,
367                                single_v_id,
368                                face_order,
369                            ));
370                        }
371                    }
372                }
373
374                // add the faces of the current pairing
375                planned_faces.extend(pairing.fill_faces([one_ring_v_ids1, one_ring_v_ids2]));
376
377                let (single_v_id, other_v_id) =
378                    pairing.last_pair([one_ring_v_ids1, one_ring_v_ids2]);
379
380                prev_single_v_id = Some(single_v_id);
381                prev_other_v_id = Some(other_v_id);
382            }
383
384            planned_faces[start_pairing_index].order = PlannedFaceOrder::Start;
385
386            let len = planned_faces.len();
387            planned_faces[len - 1].order = if len - 1 == start_pairing_index {
388                PlannedFaceOrder::Single
389            } else {
390                PlannedFaceOrder::End
391            };
392        }
393
394        planned_faces
395    }
396
397    #[instrument(skip_all)]
398    fn check_and_flip_single_shared_he(
399        &mut self,
400        one_ring_v_ids1: &[VertexId],
401        one_ring_v_ids2: &[VertexId],
402        shared_v_ids: &HashSet<VertexId>,
403        flip_threshold_sqr: f32,
404        result: &mut MergeVerticesOneRing,
405    ) -> bool {
406        if shared_v_ids.len() == 2 {
407            let (first_v_id, second_v_id) = shared_v_ids.iter().copied().collect_tuple().unwrap();
408
409            let first_pos1 = one_ring_v_ids1
410                .iter()
411                .position(|v_id| *v_id == first_v_id)
412                .unwrap() as i64;
413            let second_pos1 = one_ring_v_ids1
414                .iter()
415                .position(|v_id| *v_id == second_v_id)
416                .unwrap() as i64;
417
418            let first_pos2 = one_ring_v_ids2
419                .iter()
420                .position(|v_id| *v_id == first_v_id)
421                .unwrap() as i64;
422            let second_pos2 = one_ring_v_ids2
423                .iter()
424                .position(|v_id| *v_id == second_v_id)
425                .unwrap() as i64;
426
427            if (first_pos1 - second_pos1).abs() != 1 || (first_pos2 - second_pos2).abs() != 1 {
428                return false;
429            }
430
431            #[cfg(feature = "rerun")]
432            self.log_verts_rerun("common_one_ring", &[first_v_id, second_v_id]);
433
434            if let Some(he_id) = self.halfedge_from_to(first_v_id, second_v_id) {
435                #[cfg(feature = "rerun")]
436                self.log_he_rerun("common_one_ring_he", he_id);
437
438                return self.flip_and_collapse_single_shared_edge_if_below_threshold(
439                    he_id,
440                    flip_threshold_sqr,
441                    result,
442                );
443            }
444        }
445
446        false
447    }
448
449    #[instrument(skip_all)]
450    fn compute_range_pairs_to_connect(
451        &mut self,
452        one_ring_v_ids1: &[VertexId],
453        one_ring_v_ids2: &[VertexId],
454        shared_v_ids: &HashSet<VertexId>,
455    ) -> Vec<ConnectPair> {
456        let mut range_pairs_to_connect = vec![];
457
458        let mut connected_v_ids = HashSet::new();
459
460        let (orig_start_idx1, orig_start_idx2) = unwrap_or_return!(
461            self.find_start_indices(
462                one_ring_v_ids1,
463                one_ring_v_ids2,
464                shared_v_ids,
465                &connected_v_ids
466            ),
467            "Couldn't find start indices",
468            range_pairs_to_connect
469        );
470
471        tracing::info!(
472            "start idx1: {}, start idx2: {}",
473            orig_start_idx1,
474            orig_start_idx2
475        );
476
477        let len1 = one_ring_v_ids1.len();
478        let len2 = one_ring_v_ids2.len();
479
480        let mut start_idx1 = orig_start_idx1;
481        let mut start_idx2 = orig_start_idx2;
482
483        let mut end_idx1 = (start_idx1 + 1) % len1;
484        let mut end_idx2 = (start_idx2 + 1) % len2;
485
486        #[cfg(feature = "rerun")]
487        {
488            self.log_verts_w_labels_rerun(
489                "pairs_start_idx",
490                &[one_ring_v_ids1[start_idx1], one_ring_v_ids2[start_idx2]],
491                &["1", "2"],
492            );
493            self.log_vert_rerun("pairs_end_idx1", one_ring_v_ids1[end_idx1]);
494            self.log_vert_rerun("pairs_end_idx2", one_ring_v_ids2[end_idx2]);
495        }
496        let mut v_id1;
497        let mut v_id2;
498
499        while end_idx1 != orig_start_idx1 {
500            v_id1 = one_ring_v_ids1[end_idx1];
501            v_id2 = one_ring_v_ids2[end_idx2];
502
503            let mut shared = false;
504
505            if shared_v_ids.contains(&v_id1) {
506                while v_id2 != v_id1 {
507                    end_idx2 = (end_idx2 + 1) % len2;
508                    v_id2 = one_ring_v_ids2[end_idx2];
509
510                    #[cfg(feature = "rerun")]
511                    self.log_vert_rerun("pairs_end_idx2", v_id2);
512                }
513
514                shared = true;
515            } else if shared_v_ids.contains(&v_id2) {
516                while v_id1 != v_id2 {
517                    end_idx1 = (end_idx1 + 1) % len1;
518                    v_id1 = one_ring_v_ids1[end_idx1];
519
520                    #[cfg(feature = "rerun")]
521                    self.log_vert_rerun("pairs_end_idx1", v_id1);
522                }
523
524                shared = true;
525            }
526
527            if shared {
528                range_pairs_to_connect.push(ConnectPair::new(
529                    start_idx1..=end_idx1,
530                    len1,
531                    start_idx2..=end_idx2,
532                    len2,
533                    true,
534                ));
535
536                let mut idx = start_idx1;
537                while idx != end_idx1 {
538                    connected_v_ids.insert(one_ring_v_ids1[idx]);
539                    idx = (idx + 1) % len1;
540                }
541                idx = start_idx2;
542                while idx != end_idx2 {
543                    connected_v_ids.insert(one_ring_v_ids2[idx]);
544                    idx = (idx + 1) % len2;
545                }
546
547                if let Some((idx1, idx2)) = self.find_start_indices(
548                    one_ring_v_ids1,
549                    one_ring_v_ids2,
550                    shared_v_ids,
551                    &connected_v_ids,
552                ) {
553                    start_idx1 = idx1;
554                    start_idx2 = idx2;
555                } else {
556                    return range_pairs_to_connect;
557                }
558
559                end_idx1 = (start_idx1 + 1) % len1;
560                end_idx2 = (start_idx2 + 1) % len2;
561
562                #[cfg(feature = "rerun")]
563                {
564                    self.log_verts_w_labels_rerun(
565                        "pairs_start_idx",
566                        &[one_ring_v_ids1[start_idx1], one_ring_v_ids2[start_idx2]],
567                        &["1", "2"],
568                    );
569                    self.log_vert_rerun("pairs_end_idx1", one_ring_v_ids1[end_idx1]);
570                    self.log_vert_rerun("pairs_end_idx2", one_ring_v_ids2[end_idx2]);
571                }
572
573                if start_idx1 == orig_start_idx1 {
574                    break;
575                }
576            } else {
577                end_idx1 = (end_idx1 + 1) % len1;
578                end_idx2 = (end_idx2 + 1) % len2;
579
580                #[cfg(feature = "rerun")]
581                {
582                    self.log_vert_rerun("pairs_end_idx1", one_ring_v_ids1[end_idx1]);
583                    self.log_vert_rerun("pairs_end_idx2", one_ring_v_ids2[end_idx2]);
584                }
585            }
586        }
587
588        let diff1 = (start_idx1 as i32 - end_idx1 as i32).unsigned_abs() as usize;
589        let diff1 = diff1.min(len1 - diff1);
590
591        let diff2 = (start_idx2 as i32 - end_idx2 as i32).unsigned_abs() as usize;
592        let diff2 = diff2.min(len2 - diff2);
593
594        let closed = !shared_v_ids.is_empty();
595
596        if range_pairs_to_connect.is_empty() {
597            let (end1, end2) = if closed {
598                (orig_start_idx1, orig_start_idx2)
599            } else {
600                (
601                    (orig_start_idx1 + len1 - 1).rem_euclid(len1),
602                    (orig_start_idx2 + len2 - 1).rem_euclid(len2),
603                )
604            };
605
606            range_pairs_to_connect.push(ConnectPair::new(
607                orig_start_idx1..=end1,
608                len1,
609                orig_start_idx2..=end2,
610                len2,
611                closed,
612            ));
613        } else if diff1 > 1 || diff2 > 1 || range_pairs_to_connect.is_empty() {
614            range_pairs_to_connect.push(ConnectPair::new(
615                start_idx1..=end_idx1,
616                len1,
617                start_idx2..=end_idx2,
618                len2,
619                closed,
620            ));
621        }
622
623        #[cfg(feature = "rerun")]
624        {
625            for range_pair in &range_pairs_to_connect {
626                let mut v1s = vec![];
627                let mut v2s = vec![];
628                let mut l1s = vec![];
629                let mut l2s = vec![];
630
631                for i in range_pair.ranges[0].clone() {
632                    let v_id = one_ring_v_ids1[i % len1];
633                    v1s.push(v_id);
634                    l1s.push(format!("{i} - {v_id:?}"));
635                }
636                for i in range_pair.ranges[1].clone() {
637                    let v_id = one_ring_v_ids2[i % len2];
638                    v2s.push(v_id);
639                    l2s.push(format!("{i} - {v_id:?}"));
640                }
641
642                self.log_verts_w_labels_rerun("range_pair_1", &v1s, &l1s);
643                self.log_verts_w_labels_rerun("range_pair_2", &v2s, &l2s);
644            }
645        }
646
647        range_pairs_to_connect
648    }
649
650    #[instrument(skip(self))]
651    fn find_shared_start_indices_from_ring(
652        &self,
653        one_ring_v_ids: &[VertexId],
654        other_one_ring_v_ids: &[VertexId],
655        shared_v_ids: &HashSet<VertexId>,
656        connected_v_ids: &HashSet<VertexId>,
657    ) -> Option<(usize, usize)> {
658        let len = one_ring_v_ids.len();
659
660        for (idx, &v_id1) in one_ring_v_ids.iter().enumerate() {
661            if self.vertices.contains_key(v_id1)
662                && !shared_v_ids.contains(&v_id1)
663                && !connected_v_ids.contains(&v_id1)
664            {
665                let mut start_idx = idx;
666                let mut start_v_id = &v_id1;
667
668                while self.vertices.contains_key(*start_v_id)
669                    && !shared_v_ids.contains(start_v_id)
670                    && !connected_v_ids.contains(start_v_id)
671                {
672                    start_idx = (start_idx + len - 1) % len;
673                    start_v_id = &one_ring_v_ids[start_idx];
674
675                    if start_idx == idx {
676                        return None;
677                    }
678                }
679
680                return Some((
681                    start_idx,
682                    other_one_ring_v_ids
683                        .iter()
684                        .position(|v_id| start_v_id == v_id)
685                        .unwrap(),
686                ));
687            }
688        }
689
690        None
691    }
692
693    #[instrument(skip(self))]
694    fn find_start_indices(
695        &self,
696        one_ring_v_ids1: &[VertexId],
697        one_ring_v_ids2: &[VertexId],
698        shared_v_ids: &HashSet<VertexId>,
699        connected_v_ids: &HashSet<VertexId>,
700    ) -> Option<(usize, usize)> {
701        if !shared_v_ids.is_empty() {
702            if let Some(indices) = self.find_shared_start_indices_from_ring(
703                one_ring_v_ids1,
704                one_ring_v_ids2,
705                shared_v_ids,
706                connected_v_ids,
707            ) {
708                Some(indices)
709            } else if let Some((idx2, idx1)) = self.find_shared_start_indices_from_ring(
710                one_ring_v_ids2,
711                one_ring_v_ids1,
712                shared_v_ids,
713                connected_v_ids,
714            ) {
715                Some((idx1, idx2))
716            } else {
717                None
718            }
719        } else {
720            for (idx1, &v_id1) in one_ring_v_ids1.iter().enumerate() {
721                for (idx2, &v_id2) in one_ring_v_ids2.iter().enumerate() {
722                    if self.halfedge_from_to(v_id1, v_id2).is_some() {
723                        tracing::info!(
724                            "Found common edge between vertices {idx1} (id {v_id1:?}) and {idx2} (id {v_id2:?})"
725                        );
726                        return Some((idx1, idx2));
727                    }
728                }
729            }
730
731            let first_v_id = one_ring_v_ids1[0];
732            let first_pos = *self
733                .positions
734                .get(first_v_id)
735                .or_else(error_none!("Position not found"))?;
736
737            let mut min_dist_sqr = f32::INFINITY;
738            let mut start_idx2 = 0;
739
740            for (idx, v_id) in one_ring_v_ids2.iter().enumerate() {
741                let pos = self
742                    .positions
743                    .get(*v_id)
744                    .or_else(error_none!("Position not found"))?;
745
746                let dist_sqr = pos.distance_squared(first_pos);
747
748                if dist_sqr < min_dist_sqr {
749                    min_dist_sqr = dist_sqr;
750                    start_idx2 = idx;
751                }
752            }
753
754            Some((0, start_idx2))
755        }
756    }
757}
758
759/// Two ranges of elements that need to be connected by faces.
760#[derive(Debug)]
761struct ConnectPair {
762    /// Wether the first and last indices in the two ranges reference the same vertex, thus forming a closed polygon.
763    closed: bool,
764
765    /// The pair of ranges of vertex indices that need to be connected.
766    ranges: [RangeInclusive<usize>; 2],
767}
768
769impl ConnectPair {
770    fn new(
771        mut range1: RangeInclusive<usize>,
772        len1: usize,
773        mut range2: RangeInclusive<usize>,
774        len2: usize,
775        closed: bool,
776    ) -> Self {
777        if range1.end() <= range1.start() {
778            range1 = (*range1.start())..=(*range1.end() + len1);
779        }
780        if range2.end() <= range2.start() {
781            range2 = (*range2.start())..=(*range2.end() + len2);
782        }
783
784        ConnectPair {
785            closed,
786            ranges: [range1, range2],
787        }
788    }
789
790    // This is a modified Bresenham algorithm usually used for drawing lines on a grid.
791    fn compute_pairings(&self) -> Vec<Pairing> {
792        let range1 = self.ranges[0].clone();
793        let range2 = self.ranges[1].clone();
794
795        let count1 = range1.clone().count() as i32;
796        let count2 = range2.clone().count() as i32;
797
798        let (single_range_idx, single_count, other_count, single_range, other_range) =
799            if count1 > count2 {
800                (1, count2, count1, range2, range1)
801            } else {
802                (0, count1, count2, range1, range2)
803            };
804
805        let mut single_idx_in_range = *single_range.start();
806
807        if single_count == 0 {
808            return vec![];
809        }
810        if single_count == 1 {
811            return vec![Pairing {
812                single_range_idx,
813                single_idx_in_range,
814                other_range,
815            }];
816        }
817
818        let mut error = 2 * single_count - other_count;
819
820        let mut other_start = *other_range.start();
821        let mut other_end = other_start;
822
823        let mut pairings = Vec::new();
824
825        while other_end <= *other_range.end() {
826            if error > 0 {
827                pairings.push(Pairing {
828                    single_range_idx,
829                    single_idx_in_range,
830                    other_range: other_start..=other_end,
831                });
832
833                other_start = other_end + 1;
834                single_idx_in_range += 1;
835                error += 2 * (single_count - other_count);
836            } else {
837                error += 2 * single_count;
838            }
839
840            other_end += 1;
841        }
842
843        if let Some(last_pairing) = pairings.last_mut() {
844            last_pairing.other_range = *last_pairing.other_range.start()..=*other_range.end();
845        }
846
847        pairings
848    }
849}
850
851/// Pairs one element from one range with one or more elements from the other range.
852#[derive(Debug)]
853struct Pairing {
854    /// The index of the range in which each single element is paired to one or more elements in the other range.
855    /// Can only be `0` or `1`.
856    single_range_idx: usize,
857
858    /// The index of the single element in the range referenced by `single_range_idx`.
859    single_idx_in_range: usize,
860
861    /// The range of elements in the other range that are paired to the single element referenced in `single_idx_in_range`.
862    other_range: RangeInclusive<usize>,
863}
864
865impl Pairing {
866    fn first_pair(&self, v_ids: [&[VertexId]; 2]) -> (VertexId, VertexId) {
867        let single_ids = &v_ids[self.single_range_idx];
868        let other_ids = &v_ids[1 - self.single_range_idx];
869
870        let single_v_id = single_ids[self.single_idx_in_range % single_ids.len()];
871        let other_v_id = other_ids[*self.other_range.start() % other_ids.len()];
872
873        (single_v_id, other_v_id)
874    }
875
876    fn last_pair(&self, v_ids: [&[VertexId]; 2]) -> (VertexId, VertexId) {
877        let single_ids = &v_ids[self.single_range_idx];
878        let other_ids = &v_ids[1 - self.single_range_idx];
879
880        let single_v_id = single_ids[self.single_idx_in_range % single_ids.len()];
881        let other_v_id = other_ids[*self.other_range.end() % other_ids.len()];
882
883        (single_v_id, other_v_id)
884    }
885
886    fn fill_faces(&self, v_ids: [&[VertexId]; 2]) -> Vec<PlannedFace> {
887        let single_ids = &v_ids[self.single_range_idx];
888        let other_ids = &v_ids[1 - self.single_range_idx];
889
890        let single_v_id = single_ids[self.single_idx_in_range % single_ids.len()];
891        let mut faces = Vec::<PlannedFace>::new();
892
893        let mut others = self.other_range.clone();
894
895        let mut prev_other_v_id = other_ids[others.next().unwrap() % other_ids.len()];
896        let mut face_order = PlannedFaceOrder::Middle;
897
898        if prev_other_v_id == single_v_id {
899            face_order = PlannedFaceOrder::Start;
900
901            if let Some(second_idx) = others.next() {
902                prev_other_v_id = other_ids[second_idx % other_ids.len()];
903            } else {
904                return faces;
905            }
906        }
907
908        for other_idx in others {
909            let other_v_id = other_ids[other_idx % other_ids.len()];
910
911            if other_v_id == single_v_id {
912                break;
913            }
914
915            // make sure the triangle vertices are CCW
916            if self.single_range_idx == 0 {
917                faces.push(PlannedFace::new(
918                    prev_other_v_id,
919                    single_v_id,
920                    other_v_id,
921                    face_order,
922                ));
923            } else {
924                faces.push(PlannedFace::new(
925                    prev_other_v_id,
926                    other_v_id,
927                    single_v_id,
928                    face_order,
929                ));
930            }
931
932            face_order = PlannedFaceOrder::Middle;
933            prev_other_v_id = other_v_id;
934        }
935
936        faces
937    }
938}
939
940#[derive(Debug)]
941struct PlannedFace {
942    order: PlannedFaceOrder,
943    v1: VertexId,
944    new_he_v1: VertexId,
945    new_he_v2: VertexId,
946}
947
948#[derive(Debug, Copy, Clone, Eq, PartialEq)]
949enum PlannedFaceOrder {
950    Start,
951    Middle,
952    End,
953    Single,
954}
955
956impl PlannedFace {
957    fn new(
958        v_id1: VertexId,
959        new_he_v_id1: VertexId,
960        new_he_v_id2: VertexId,
961        order: PlannedFaceOrder,
962    ) -> Self {
963        PlannedFace {
964            order,
965            v1: v_id1,
966            new_he_v1: new_he_v_id1,
967            new_he_v2: new_he_v_id2,
968        }
969    }
970
971    #[instrument(skip(mesh_graph))]
972    fn add_to_meshgraph(
973        &self,
974        mesh_graph: &mut MeshGraph,
975    ) -> Option<(Option<HalfedgeId>, AddFace)> {
976        #[cfg(feature = "rerun")]
977        mesh_graph.log_verts_w_labels_rerun(
978            "add_to_meshgraph",
979            &[self.v1, self.new_he_v1, self.new_he_v2],
980            &["1", "2", "3"],
981        );
982
983        let add_or_get_edge = mesh_graph.add_or_get_edge(self.v1, self.new_he_v1);
984
985        let (start_to_end_he_id, twin_he_id) = match self.order {
986            PlannedFaceOrder::Start => {
987                let AddEdge {
988                    start_to_end_he_id,
989                    twin_he_id,
990                } = mesh_graph.add_edge(self.new_he_v1, self.new_he_v2);
991
992                (start_to_end_he_id, twin_he_id)
993            }
994            PlannedFaceOrder::Single => (
995                mesh_graph.halfedge_from_to(self.new_he_v1, self.new_he_v2)?,
996                mesh_graph.halfedge_from_to(self.new_he_v2, self.new_he_v1)?,
997            ),
998            _ => {
999                error!("Invalid face order {:?}", self.order);
1000                return None;
1001            }
1002        };
1003
1004        let he = mesh_graph
1005            .halfedges
1006            .get(add_or_get_edge.start_to_end_he_id)
1007            .or_else(error_none!("Halfedge not found"))?;
1008
1009        let add_or_get_edge = if he.is_boundary() {
1010            add_or_get_edge
1011        } else {
1012            let AddEdge {
1013                start_to_end_he_id,
1014                twin_he_id,
1015            } = mesh_graph.add_edge(self.v1, self.new_he_v1);
1016
1017            AddOrGetEdge {
1018                start_to_end_he_id,
1019                twin_he_id,
1020                new_start_to_end: true,
1021                new_twin: true,
1022            }
1023        };
1024
1025        let mut add_face = mesh_graph
1026            .add_face_from_halfedges(add_or_get_edge.start_to_end_he_id, start_to_end_he_id)?;
1027
1028        add_face.halfedge_ids.push(start_to_end_he_id);
1029        add_face.halfedge_ids.push(twin_he_id);
1030
1031        if add_or_get_edge.new_start_to_end {
1032            add_face
1033                .halfedge_ids
1034                .push(add_or_get_edge.start_to_end_he_id);
1035        }
1036        if add_or_get_edge.new_twin {
1037            add_face.halfedge_ids.push(add_or_get_edge.twin_he_id);
1038        }
1039
1040        Some((
1041            if matches!(self.order, PlannedFaceOrder::Start) {
1042                Some(twin_he_id)
1043            } else {
1044                None
1045            },
1046            add_face,
1047        ))
1048    }
1049
1050    #[instrument(skip(mesh_graph))]
1051    fn add_to_meshgraph_and_he(
1052        &self,
1053        mesh_graph: &mut MeshGraph,
1054        existing_he_id: HalfedgeId,
1055    ) -> Option<(Option<HalfedgeId>, AddFace)> {
1056        #[cfg(feature = "rerun")]
1057        {
1058            mesh_graph.log_he_rerun("add_meshgraph_and_he", existing_he_id);
1059            mesh_graph.log_verts_w_labels_rerun(
1060                "add_to_meshgraph_and_he",
1061                &[self.v1, self.new_he_v1, self.new_he_v2],
1062                &["1", "2", "3"],
1063            );
1064        }
1065
1066        match self.order {
1067            PlannedFaceOrder::Middle => {
1068                let AddEdge {
1069                    start_to_end_he_id,
1070                    twin_he_id,
1071                } = mesh_graph.add_edge(self.new_he_v1, self.new_he_v2);
1072
1073                let mut add_face =
1074                    mesh_graph.add_face_from_halfedges(existing_he_id, start_to_end_he_id)?;
1075                add_face.halfedge_ids.push(start_to_end_he_id);
1076                add_face.halfedge_ids.push(twin_he_id);
1077
1078                Some((Some(twin_he_id), add_face))
1079            }
1080            PlannedFaceOrder::End => {
1081                let end_he = mesh_graph
1082                    .halfedge_from_to(self.new_he_v1, self.new_he_v2)
1083                    .or_else(error_none!("Final halfedge not found"))?;
1084
1085                let add_face = mesh_graph.add_face_from_halfedges(existing_he_id, end_he)?;
1086
1087                Some((None, add_face))
1088            }
1089            _ => {
1090                error!("Invalid face order {:?}", self.order);
1091                None
1092            }
1093        }
1094    }
1095}