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 #[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 .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 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 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 ¤t_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 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 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 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 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 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 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 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 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 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 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 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 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 if let Some(he) = self.halfedges.get(he_id) {
1164 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 if let Some(he) = self.halfedges.get(he_id) {
1186 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 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 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 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 Closed,
1331 Open,
1333 AlreadyConnected,
1335}
1336
1337#[derive(Debug)]
1339struct ConnectPair {
1340 start_cap: ConnectPairCap,
1342
1343 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 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#[derive(Debug, Clone)]
1431struct Pairing {
1432 single_range_idx: usize,
1435
1436 single_idx_in_range: usize,
1438
1439 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 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#[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}