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 let mut steps = 0;
511 loop {
512 let prev_idx = (range_start + other_len - 1) % other_len;
514 let prev_v_id = other_ids[prev_idx];
515
516 let boundary_v_id = other_ids[range_start % other_len];
517 if let Some(face_id) = self.face_with_vertices(single_v_id, prev_v_id, boundary_v_id) {
518 if range_start == 0 {
521 range_start = other_len - 1;
522 } else {
523 range_start -= 1;
524 }
525 current_pairing.other_range = range_start..=*current_pairing.other_range.end();
526
527 self.add_face_to_connected_he_ids(face_id, connected_he_ids);
528 connected_face_ids.push(face_id);
529
530 steps += 1;
531 if steps >= other_len {
532 error!("Pairing fan wrapped around the entire one ring");
533 break;
534 }
535 } else {
536 break;
537 }
538 }
539
540 let mut range_end = *current_pairing.other_range.end();
542 let mut steps = 0;
545 loop {
546 let next_idx = (range_end + 1) % other_len;
547 let next_v_id = other_ids[next_idx];
548
549 let boundary_v_id = other_ids[range_end % other_len];
550 if let Some(face_id) = self.face_with_vertices(single_v_id, boundary_v_id, next_v_id) {
551 range_end += 1;
552 current_pairing.other_range = *current_pairing.other_range.start()..=range_end;
553
554 self.add_face_to_connected_he_ids(face_id, connected_he_ids);
555 connected_face_ids.push(face_id);
556
557 steps += 1;
558 if steps >= other_len {
559 error!("Pairing fan wrapped around the entire one ring");
560 break;
561 }
562 } else {
563 break;
564 }
565 }
566 }
567
568 fn create_triangle_pairing(
569 &self,
570 one_ring_v_ids1: &[VertexId],
571 one_ring_v_ids2: &[VertexId],
572 idx1: usize,
573 idx2: usize,
574 third_v_id: VertexId,
575 current_pairing: &mut Pairing,
576 ) -> bool {
577 let (idx1, idx2) =
578 if let Some(idx) = one_ring_v_ids1.iter().position(|v_id| *v_id == third_v_id) {
579 current_pairing.single_range_idx = 1;
580 current_pairing.single_idx_in_range = idx2;
581 idx1.min(idx)..=idx1.max(idx);
582
583 (idx, idx1)
584 } else if let Some(idx) = one_ring_v_ids2.iter().position(|v_id| *v_id == third_v_id) {
585 (idx, idx2)
586 } else {
587 return false;
588 };
589
590 let other_len = if current_pairing.single_range_idx == 0 {
591 one_ring_v_ids2.len()
592 } else {
593 one_ring_v_ids1.len()
594 };
595
596 *current_pairing = Pairing::new_triangle(
597 current_pairing.single_range_idx,
598 current_pairing.single_idx_in_range,
599 idx1,
600 idx2,
601 other_len,
602 );
603
604 true
605 }
606
607 fn cleanup_bookkeeping(
608 &mut self,
609 one_ring_v_ids1: &[VertexId],
610 one_ring_v_ids2: &[VertexId],
611 marked_vertices: &mut HashSet<VertexId>,
612 marked_halfedges: &mut HashSet<HalfedgeId>,
613 result: &mut MergeVerticesOneRing,
614 ) {
615 for vertex_id in one_ring_v_ids1.iter().chain(one_ring_v_ids2).copied() {
616 if !self.vertices.contains_key(vertex_id) {
617 continue;
618 }
619
620 let cleanup = self.make_vertex_neighborhood_manifold(vertex_id);
621
622 if !cleanup.added_vertices.is_empty() {
623 marked_vertices.insert(vertex_id);
624 }
625
626 result.added_vertices.extend(cleanup.added_vertices.clone());
627 marked_vertices.extend(cleanup.added_vertices);
628
629 for removed_v_id in cleanup.removed_vertices {
630 if result.added_vertices.contains(&removed_v_id) {
631 result.added_vertices.retain(|&v_id| v_id != removed_v_id);
632 } else {
633 result.removed_vertices.push(removed_v_id);
634 }
635
636 marked_vertices.remove(&removed_v_id);
637 }
638
639 for removed_he_id in cleanup.removed_halfedges {
640 if result.added_halfedges.contains(&removed_he_id) {
641 result
642 .added_halfedges
643 .retain(|&he_id| he_id != removed_he_id);
644 } else {
645 result.removed_halfedges.push(removed_he_id);
646 }
647
648 marked_halfedges.remove(&removed_he_id);
649 }
650
651 for removed_face_id in cleanup.removed_faces {
652 if result.added_faces.contains(&removed_face_id) {
653 result
654 .added_faces
655 .retain(|&face_id| face_id != removed_face_id);
656 } else {
657 result.removed_faces.push(removed_face_id);
658 }
659 }
660 }
661 }
662
663 fn add_planned_faces(
664 &mut self,
665 planned_faces: Vec<PlannedFace>,
666 one_ring_set1: &HashSet<VertexId>,
667 one_ring_set2: &HashSet<VertexId>,
668 marked_halfedges: &mut HashSet<HalfedgeId>,
669 result: &mut MergeVerticesOneRing,
670 ) {
671 let mut prev_he = None;
672
673 for planned_face in planned_faces {
674 let inserted = if let Some(prev_he) = prev_he {
675 planned_face.add_to_mesh_graph_and_he(self, prev_he)
676 } else {
677 planned_face.add_to_mesh_graph(self)
678 };
679
680 let Some(inserted) = inserted else {
681 continue;
683 };
684
685 prev_he = inserted.0;
686 let inserted = inserted.1;
687
688 for he_id in inserted.halfedge_ids.iter().copied() {
689 let he = self.halfedges[he_id];
691 let start_v_id =
692 unwrap_or_return!(he.start_vertex(self), "Couldn't find start vertex");
693 let end_v_id = he.end_vertex;
694
695 if one_ring_set1.contains(&start_v_id) && one_ring_set2.contains(&end_v_id)
696 || one_ring_set2.contains(&start_v_id) && one_ring_set1.contains(&end_v_id)
697 {
698 marked_halfedges.insert(he_id);
699 }
700 }
701
702 result.added_halfedges.extend(inserted.halfedge_ids);
703 result.added_faces.push(inserted.face_id);
704 }
705 }
706
707 fn remove_neighbor_faces(
708 &mut self,
709 vertex1: &Vertex,
710 vertex2: &Vertex,
711 result: &mut MergeVerticesOneRing,
712 ) {
713 let face_ids1 = vertex1.faces(self).collect_vec();
714 let face_ids2 = vertex2.faces(self).collect_vec();
715
716 for face_id in face_ids1.into_iter().chain(face_ids2) {
717 let (del_v, del_he) = self.remove_face(face_id);
718
719 result.removed_faces.push(face_id);
720 result.removed_vertices.extend(del_v);
721 result.removed_halfedges.extend(del_he);
722 }
723 }
724
725 #[instrument(skip_all)]
726 fn flip_and_collapse_single_shared_edge_if_below_threshold(
727 &mut self,
728 single_shared_he_id: HalfedgeId,
729 flip_threshold_sqr: f32,
730 result: &mut MergeVerticesOneRing,
731 ) -> bool {
732 let he = self.halfedges[single_shared_he_id];
733
734 let twin_id = unwrap_or_return!(he.twin, "Twin not found", false);
735 let twin = unwrap_or_return!(self.halfedges.get(twin_id), "Twin not found", false);
736
737 let other_v_id1 =
738 unwrap_or_return!(he.opposite_vertex(self), "Opposite vertex not found", false);
739 let other_v_id2 = unwrap_or_return!(
740 twin.opposite_vertex(self),
741 "Opposite vertex not found",
742 false
743 );
744
745 let pos1 = *unwrap_or_return!(self.positions.get(other_v_id1), "Position not found", false);
746 let pos2 = *unwrap_or_return!(self.positions.get(other_v_id2), "Position not found", false);
747
748 if pos1.distance_squared(pos2) <= flip_threshold_sqr {
749 tracing::info!("Flipping edge {single_shared_he_id:?}");
750
751 self.flip_edge(single_shared_he_id);
752
753 let CollapseEdge {
754 removed_vertices,
755 removed_halfedges,
756 removed_faces,
757 added_vertices,
758 } = self.collapse_edge(single_shared_he_id);
759
760 result.added_vertices.extend(added_vertices);
761 result.removed_vertices.extend(removed_vertices);
762 result.removed_halfedges.extend(removed_halfedges);
763 result.removed_faces.extend(removed_faces);
764
765 true
766 } else {
767 false
768 }
769 }
770
771 fn plan_new_faces(
772 &self,
773 range_pairs_to_connect: &[ConnectPair],
774 one_ring_v_ids1: &[VertexId],
775 one_ring_v_ids2: &[VertexId],
776 ) -> Vec<PlannedFace> {
777 let mut planned_faces = Vec::with_capacity(one_ring_v_ids1.len());
778
779 for range_pair_to_connect in range_pairs_to_connect {
780 let start_pairing_index = planned_faces.len();
781
782 let pairings = range_pair_to_connect.compute_pairings();
783
784 tracing::info!("Pairings: {:#?}", pairings);
785
786 if pairings.is_empty() {
787 continue;
788 }
789
790 let mut prev_single_v_id = None;
791 let mut prev_other_v_id = None;
792
793 if matches!(range_pair_to_connect.start_cap, ConnectPairCap::Open) {
794 let last_pairing = pairings.last().unwrap();
795
796 let (s, o) = last_pairing.last_pair([one_ring_v_ids1, one_ring_v_ids2]);
797
798 prev_single_v_id = Some(s);
799 prev_other_v_id = Some(o);
800 }
801
802 for pairing in pairings {
803 if let Some(prev_single_v_id) = prev_single_v_id
804 && let Some(prev_other_v_id) = prev_other_v_id
805 {
806 let (single_v_id, other_v_id) =
808 pairing.first_pair([one_ring_v_ids1, one_ring_v_ids2]);
809
810 let face_order = if prev_single_v_id == prev_other_v_id {
811 PlannedFaceOrder::Start
812 } else if single_v_id == other_v_id {
813 PlannedFaceOrder::End
814 } else {
815 PlannedFaceOrder::Middle
816 };
817
818 if pairing.single_range_idx == 0 {
820 if prev_single_v_id != prev_other_v_id {
821 planned_faces.push(PlannedFace::new(
822 prev_single_v_id,
823 single_v_id,
824 prev_other_v_id,
825 face_order,
826 ));
827 }
828 if single_v_id != other_v_id {
829 planned_faces.push(PlannedFace::new(
830 prev_other_v_id,
831 single_v_id,
832 other_v_id,
833 face_order,
834 ));
835 }
836 } else {
837 if prev_single_v_id != prev_other_v_id {
838 planned_faces.push(PlannedFace::new(
839 prev_single_v_id,
840 prev_other_v_id,
841 single_v_id,
842 face_order,
843 ));
844 }
845 if single_v_id != other_v_id {
846 planned_faces.push(PlannedFace::new(
847 prev_other_v_id,
848 other_v_id,
849 single_v_id,
850 face_order,
851 ));
852 }
853 }
854 }
855
856 planned_faces.extend(pairing.fill_faces([one_ring_v_ids1, one_ring_v_ids2]));
858
859 let (single_v_id, other_v_id) =
860 pairing.last_pair([one_ring_v_ids1, one_ring_v_ids2]);
861
862 prev_single_v_id = Some(single_v_id);
863 prev_other_v_id = Some(other_v_id);
864 }
865
866 if !matches!(
867 range_pair_to_connect.start_cap,
868 ConnectPairCap::AlreadyConnected
869 ) {
870 planned_faces[start_pairing_index].order = PlannedFaceOrder::Start;
871 }
872
873 let len = planned_faces.len();
874 planned_faces[len - 1].order = if len - 1 == start_pairing_index {
875 PlannedFaceOrder::Single
876 } else {
877 PlannedFaceOrder::End
878 };
879 }
880
881 planned_faces
882 }
883
884 #[instrument(skip_all)]
885 fn check_and_flip_single_shared_he(
886 &mut self,
887 shared_he_ids: &HashSet<HalfedgeId>,
888 flip_threshold_sqr: f32,
889 result: &mut MergeVerticesOneRing,
890 ) -> bool {
891 if shared_he_ids.len() == 1 {
892 let shared_he_id = shared_he_ids.iter().copied().next().unwrap();
893
894 #[cfg(feature = "rerun")]
895 self.log_he_rerun("common_one_ring_he", shared_he_id);
896
897 return self.flip_and_collapse_single_shared_edge_if_below_threshold(
898 shared_he_id,
899 flip_threshold_sqr,
900 result,
901 );
902 }
903
904 false
905 }
906
907 #[instrument(skip_all)]
908 #[allow(clippy::too_many_arguments)]
909 fn compute_range_pairs_to_connect(
910 &mut self,
911 one_ring_v_ids1: &[VertexId],
912 one_ring_v_ids2: &[VertexId],
913 one_ring_he_ids1: &[HalfedgeId],
914 one_ring_he_ids2: &[HalfedgeId],
915 shared_v_ids: &HashSet<VertexId>,
916 shared_he_ids: &HashSet<HalfedgeId>,
917 mut connected_v_ids: HashSet<VertexId>,
918 mut connected_he_ids: HashSet<HalfedgeId>,
919 ) -> Vec<ConnectPair> {
920 let mut range_pairs_to_connect = vec![];
921
922 let (orig_start_idx1, orig_start_idx2, orig_start_cap) = unwrap_or_return!(
923 self.find_start_indices(
924 one_ring_v_ids1,
925 one_ring_v_ids2,
926 one_ring_he_ids1,
927 one_ring_he_ids2,
928 shared_v_ids,
929 shared_he_ids,
930 &connected_v_ids,
931 &connected_he_ids,
932 ),
933 "Couldn't find start indices",
934 range_pairs_to_connect
935 );
936
937 tracing::info!(
938 "start idx1: {}, start idx2: {}",
939 orig_start_idx1,
940 orig_start_idx2
941 );
942
943 let len1 = one_ring_v_ids1.len();
944 let len2 = one_ring_v_ids2.len();
945
946 let mut start_idx1 = orig_start_idx1;
947 let mut start_idx2 = orig_start_idx2;
948 let mut start_cap = orig_start_cap;
949
950 let mut end_idx1 = (start_idx1 + 1) % len1;
951 let mut end_idx2 = (start_idx2 + 1) % len2;
952
953 #[cfg(feature = "rerun")]
954 {
955 self.log_verts_w_labels_rerun(
956 "pairs_start_idx",
957 &[one_ring_v_ids1[start_idx1], one_ring_v_ids2[start_idx2]],
958 &["1", "2"],
959 );
960 self.log_vert_rerun("pairs_end_idx1", one_ring_v_ids1[end_idx1]);
961 self.log_vert_rerun("pairs_end_idx2", one_ring_v_ids2[end_idx2]);
962 }
963 let mut v_id1;
964 let mut v_id2;
965
966 while end_idx1 != orig_start_idx1 {
967 v_id1 = one_ring_v_ids1[end_idx1];
968 v_id2 = one_ring_v_ids2[end_idx2];
969
970 if !self.vertices.contains_key(v_id1) || !self.vertices.contains_key(v_id2) {
971 break;
972 }
973
974 let mut end_cap = ConnectPairCap::Open;
975
976 if shared_v_ids.contains(&v_id1) {
977 let start_end_idx2 = end_idx2;
978 while v_id2 != v_id1 {
979 end_idx2 = (end_idx2 + 1) % len2;
980 v_id2 = one_ring_v_ids2[end_idx2];
981
982 #[cfg(feature = "rerun")]
983 self.log_vert_rerun("pairs_end_idx2", v_id2);
984
985 if end_idx2 == start_end_idx2 {
986 error!("Full cycle without finding shared vertex in one ring 2");
987 return range_pairs_to_connect;
988 }
989 }
990
991 end_cap = ConnectPairCap::Closed;
992 } else if shared_v_ids.contains(&v_id2) {
993 let start_end_idx1 = end_idx1;
994 while v_id1 != v_id2 {
995 end_idx1 = (end_idx1 + 1) % len1;
996 v_id1 = one_ring_v_ids1[end_idx1];
997
998 #[cfg(feature = "rerun")]
999 self.log_vert_rerun("pairs_end_idx1", v_id1);
1000
1001 if end_idx1 == start_end_idx1 {
1002 error!("Full cycle without finding shared vertex in one ring 1");
1003 return range_pairs_to_connect;
1004 }
1005 }
1006
1007 end_cap = ConnectPairCap::Closed;
1008 } else if connected_v_ids.contains(&v_id1) {
1009 let start_end_idx2 = end_idx2;
1010 while !connected_v_ids.contains(&v_id2) {
1011 end_idx2 = (end_idx2 + 1) % len2;
1012 v_id2 = one_ring_v_ids2[end_idx2];
1013
1014 #[cfg(feature = "rerun")]
1015 self.log_vert_rerun("pairs_end_idx2", v_id2);
1016
1017 if end_idx2 == start_end_idx2 {
1018 error!("Full cycle without finding connected vertex in one ring 2");
1019 return range_pairs_to_connect;
1020 }
1021 }
1022
1023 end_cap = ConnectPairCap::AlreadyConnected;
1024 } else if connected_v_ids.contains(&v_id2) {
1025 let start_end_idx1 = end_idx1;
1026 while !connected_v_ids.contains(&v_id1) {
1027 end_idx1 = (end_idx1 + 1) % len1;
1028 v_id1 = one_ring_v_ids1[end_idx1];
1029
1030 #[cfg(feature = "rerun")]
1031 self.log_vert_rerun("pairs_end_idx1", v_id1);
1032
1033 if end_idx1 == start_end_idx1 {
1034 error!("Full cycle without finding connected vertex in one ring 1");
1035 return range_pairs_to_connect;
1036 }
1037 }
1038
1039 end_cap = ConnectPairCap::AlreadyConnected;
1040 }
1041
1042 if !matches!(end_cap, ConnectPairCap::Open) {
1043 range_pairs_to_connect.push(ConnectPair::new(
1044 start_idx1..=end_idx1,
1045 len1,
1046 start_idx2..=end_idx2,
1047 len2,
1048 start_cap,
1049 ));
1050
1051 self.remember_range_pair_connections(
1052 start_idx1,
1053 end_idx1,
1054 start_idx2,
1055 end_idx2,
1056 len1,
1057 len2,
1058 one_ring_v_ids1,
1059 one_ring_v_ids2,
1060 one_ring_he_ids1,
1061 one_ring_he_ids2,
1062 &mut connected_v_ids,
1063 &mut connected_he_ids,
1064 );
1065
1066 if let Some((idx1, idx2, start)) = self.find_start_indices(
1067 one_ring_v_ids1,
1068 one_ring_v_ids2,
1069 one_ring_he_ids1,
1070 one_ring_he_ids2,
1071 shared_v_ids,
1072 shared_he_ids,
1073 &connected_v_ids,
1074 &connected_he_ids,
1075 ) {
1076 start_idx1 = idx1;
1077 start_idx2 = idx2;
1078 start_cap = start;
1079 } else {
1080 return range_pairs_to_connect;
1081 }
1082
1083 end_idx1 = (start_idx1 + 1) % len1;
1084 end_idx2 = (start_idx2 + 1) % len2;
1085
1086 #[cfg(feature = "rerun")]
1087 {
1088 self.log_verts_w_labels_rerun(
1089 "pairs_start_idx",
1090 &[one_ring_v_ids1[start_idx1], one_ring_v_ids2[start_idx2]],
1091 &["1", "2"],
1092 );
1093 self.log_vert_rerun("pairs_end_idx1", one_ring_v_ids1[end_idx1]);
1094 self.log_vert_rerun("pairs_end_idx2", one_ring_v_ids2[end_idx2]);
1095 }
1096
1097 if start_idx1 == orig_start_idx1 {
1098 break;
1099 }
1100 } else {
1101 end_idx1 = (end_idx1 + 1) % len1;
1102 end_idx2 = (end_idx2 + 1) % len2;
1103
1104 #[cfg(feature = "rerun")]
1105 {
1106 self.log_vert_rerun("pairs_end_idx1", one_ring_v_ids1[end_idx1]);
1107 self.log_vert_rerun("pairs_end_idx2", one_ring_v_ids2[end_idx2]);
1108 }
1109 }
1110 }
1111
1112 let diff1 = (start_idx1 as i32 - end_idx1 as i32).unsigned_abs() as usize;
1113 let diff1 = diff1.min(len1 - diff1);
1114
1115 let diff2 = (start_idx2 as i32 - end_idx2 as i32).unsigned_abs() as usize;
1116 let diff2 = diff2.min(len2 - diff2);
1117
1118 let cap = if shared_v_ids.is_empty() {
1119 ConnectPairCap::Open
1120 } else {
1121 ConnectPairCap::Closed
1122 };
1123
1124 if range_pairs_to_connect.is_empty() {
1125 let (end1, end2) = if matches!(cap, ConnectPairCap::Closed) {
1126 (orig_start_idx1, orig_start_idx2)
1127 } else {
1128 (
1129 (orig_start_idx1 + len1 - 1).rem_euclid(len1),
1130 (orig_start_idx2 + len2 - 1).rem_euclid(len2),
1131 )
1132 };
1133
1134 range_pairs_to_connect.push(ConnectPair::new(
1135 orig_start_idx1..=end1,
1136 len1,
1137 orig_start_idx2..=end2,
1138 len2,
1139 cap,
1140 ));
1141 } else if diff1 > 1 || diff2 > 1 || range_pairs_to_connect.is_empty() {
1142 range_pairs_to_connect.push(ConnectPair::new(
1143 start_idx1..=end_idx1,
1144 len1,
1145 start_idx2..=end_idx2,
1146 len2,
1147 cap,
1148 ));
1149 }
1150
1151 #[cfg(feature = "rerun")]
1152 {
1153 for range_pair in &range_pairs_to_connect {
1154 let mut v1s = vec![];
1155 let mut v2s = vec![];
1156 let mut l1s = vec![];
1157 let mut l2s = vec![];
1158
1159 for i in range_pair.ranges[0].clone() {
1160 let v_id = one_ring_v_ids1[i % len1];
1161 v1s.push(v_id);
1162 l1s.push(format!("{i} - {v_id:?}"));
1163 }
1164 for i in range_pair.ranges[1].clone() {
1165 let v_id = one_ring_v_ids2[i % len2];
1166 v2s.push(v_id);
1167 l2s.push(format!("{i} - {v_id:?}"));
1168 }
1169
1170 self.log_verts_w_labels_rerun("range_pair_1", &v1s, &l1s);
1171 self.log_verts_w_labels_rerun("range_pair_2", &v2s, &l2s);
1172 }
1173 }
1174
1175 range_pairs_to_connect
1176 }
1177
1178 #[instrument(skip(self))]
1179 #[allow(clippy::too_many_arguments)]
1180 fn remember_range_pair_connections(
1181 &mut self,
1182 start_idx1: usize,
1183 end_idx1: usize,
1184 start_idx2: usize,
1185 end_idx2: usize,
1186 len1: usize,
1187 len2: usize,
1188 one_ring_v_ids1: &[VertexId],
1189 one_ring_v_ids2: &[VertexId],
1190 one_ring_he_ids1: &[HalfedgeId],
1191 one_ring_he_ids2: &[HalfedgeId],
1192 connected_v_ids: &mut HashSet<VertexId>,
1193 connected_he_ids: &mut HashSet<HalfedgeId>,
1194 ) {
1195 let mut idx = start_idx1;
1196 loop {
1197 connected_v_ids.insert(one_ring_v_ids1[idx]);
1198 if idx == end_idx1 {
1199 break;
1200 }
1201 let he_id = one_ring_he_ids1[idx];
1202 connected_he_ids.insert(he_id);
1203
1204 if let Some(he) = self.halfedges.get(he_id) {
1206 if let Some(twin_id) = he.twin {
1208 connected_he_ids.insert(twin_id);
1209 } else {
1210 error!("halfedge {:?} has no twin", he_id);
1211 }
1212 }
1213
1214 idx = (idx + 1) % len1;
1215 }
1216
1217 idx = start_idx2;
1218 loop {
1219 connected_v_ids.insert(one_ring_v_ids2[idx]);
1220 if idx == end_idx2 {
1221 break;
1222 }
1223 let he_id = one_ring_he_ids2[idx];
1224 connected_he_ids.insert(he_id);
1225
1226 if let Some(he) = self.halfedges.get(he_id) {
1228 if let Some(twin_id) = he.twin {
1230 connected_he_ids.insert(twin_id);
1231 } else {
1232 error!("halfedge {:?} has no twin", he_id);
1233 }
1234 }
1235
1236 idx = (idx + 1) % len2;
1237 }
1238 }
1239
1240 #[instrument(skip(self))]
1241 #[allow(clippy::too_many_arguments)]
1242 fn find_shared_start_indices_from_ring(
1243 &self,
1244 one_ring_v_ids: &[VertexId],
1245 other_one_ring_v_ids: &[VertexId],
1246 one_ring_he_ids: &[HalfedgeId],
1247 other_one_ring_he_ids: &[HalfedgeId],
1248 shared_v_ids: &HashSet<VertexId>,
1249 shared_he_ids: &HashSet<HalfedgeId>,
1250 connected_v_ids: &HashSet<VertexId>,
1251 connected_he_ids: &HashSet<HalfedgeId>,
1252 ) -> Option<(usize, usize, ConnectPairCap)> {
1253 let len = one_ring_v_ids.len();
1254 let other_len = other_one_ring_v_ids.len();
1255
1256 debug_assert_eq!(len, one_ring_he_ids.len());
1257 debug_assert_eq!(other_len, other_one_ring_he_ids.len());
1258
1259 for (he_idx, &he_id1) in one_ring_he_ids.iter().enumerate() {
1260 if self.halfedges.contains_key(he_id1)
1261 && !shared_he_ids.contains(&he_id1)
1262 && !connected_he_ids.contains(&he_id1)
1263 {
1264 let mut start_idx = he_idx;
1267 let mut start_he_id = &one_ring_he_ids[start_idx];
1268
1269 let mut start_v_id = &one_ring_v_ids[start_idx];
1270
1271 while self.halfedges.contains_key(*start_he_id)
1272 && !shared_he_ids.contains(start_he_id)
1273 && !connected_he_ids.contains(start_he_id)
1274 && self.vertices.contains_key(*start_v_id)
1275 && !shared_v_ids.contains(start_v_id)
1276 && !connected_v_ids.contains(start_v_id)
1277 {
1278 start_idx = (start_idx + len - 1) % len;
1279 start_he_id = &one_ring_he_ids[start_idx];
1280 start_v_id = &one_ring_v_ids[start_idx];
1281
1282 if start_idx == he_idx {
1283 return None;
1284 }
1285 }
1286
1287 if let Some(other_idx) = other_one_ring_v_ids
1291 .iter()
1292 .position(|v_id| start_v_id == v_id)
1293 {
1294 return Some((start_idx, other_idx, ConnectPairCap::Closed));
1295 }
1296
1297 for (other_idx, &other_v_id) in other_one_ring_v_ids.iter().enumerate() {
1299 if self.halfedge_from_to(*start_v_id, other_v_id).is_some() {
1300 let other_he_id = &other_one_ring_he_ids[other_idx];
1301
1302 if self.halfedges.contains_key(*other_he_id)
1303 && !shared_he_ids.contains(other_he_id)
1304 && !connected_he_ids.contains(other_he_id)
1305 {
1306 return Some((start_idx, other_idx, ConnectPairCap::AlreadyConnected));
1307 }
1308 }
1309 }
1310 }
1311 }
1312
1313 None
1314 }
1315
1316 #[allow(clippy::too_many_arguments)]
1317 #[instrument(skip(self))]
1318 fn find_start_indices(
1319 &self,
1320 one_ring_v_ids1: &[VertexId],
1321 one_ring_v_ids2: &[VertexId],
1322 one_ring_he_ids1: &[HalfedgeId],
1323 one_ring_he_ids2: &[HalfedgeId],
1324 shared_v_ids: &HashSet<VertexId>,
1325 shared_he_ids: &HashSet<HalfedgeId>,
1326 connected_v_ids: &HashSet<VertexId>,
1327 connected_he_ids: &HashSet<HalfedgeId>,
1328 ) -> Option<(usize, usize, ConnectPairCap)> {
1329 if !shared_v_ids.is_empty() || !connected_v_ids.is_empty() {
1330 self.find_shared_start_indices_from_ring(
1331 one_ring_v_ids1,
1332 one_ring_v_ids2,
1333 one_ring_he_ids1,
1334 one_ring_he_ids2,
1335 shared_v_ids,
1336 shared_he_ids,
1337 connected_v_ids,
1338 connected_he_ids,
1339 )
1340 } else {
1341 let first_v_id = one_ring_v_ids1[0];
1342 let first_pos = *self
1343 .positions
1344 .get(first_v_id)
1345 .or_else(error_none!("Position not found"))?;
1346
1347 let mut min_dist_sqr = f32::INFINITY;
1348 let mut start_idx2 = 0;
1349
1350 for (idx, v_id) in one_ring_v_ids2.iter().enumerate() {
1351 let pos = self
1352 .positions
1353 .get(*v_id)
1354 .or_else(error_none!("Position not found"))?;
1355
1356 let dist_sqr = pos.distance_squared(first_pos);
1357
1358 if dist_sqr < min_dist_sqr {
1359 min_dist_sqr = dist_sqr;
1360 start_idx2 = idx;
1361 }
1362 }
1363
1364 Some((0, start_idx2, ConnectPairCap::Open))
1365 }
1366 }
1367}
1368
1369#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1370enum ConnectPairCap {
1371 Closed,
1373 Open,
1375 AlreadyConnected,
1377}
1378
1379#[derive(Debug)]
1381struct ConnectPair {
1382 start_cap: ConnectPairCap,
1384
1385 ranges: [RangeInclusive<usize>; 2],
1387}
1388
1389impl ConnectPair {
1390 fn new(
1391 mut range1: RangeInclusive<usize>,
1392 len1: usize,
1393 mut range2: RangeInclusive<usize>,
1394 len2: usize,
1395 start_cap: ConnectPairCap,
1396 ) -> Self {
1397 if range1.end() <= range1.start() {
1398 range1 = (*range1.start())..=(*range1.end() + len1);
1399 }
1400 if range2.end() <= range2.start() {
1401 range2 = (*range2.start())..=(*range2.end() + len2);
1402 }
1403
1404 ConnectPair {
1405 start_cap,
1406 ranges: [range1, range2],
1407 }
1408 }
1409
1410 fn compute_pairings(&self) -> Vec<Pairing> {
1412 let range1 = self.ranges[0].clone();
1413 let range2 = self.ranges[1].clone();
1414
1415 let count1 = range1.clone().count() as i32;
1416 let count2 = range2.clone().count() as i32;
1417
1418 let (single_range_idx, single_count, other_count, single_range, other_range) =
1419 if count1 > count2 {
1420 (1, count2, count1, range2, range1)
1421 } else {
1422 (0, count1, count2, range1, range2)
1423 };
1424
1425 let mut single_idx_in_range = *single_range.start();
1426
1427 if single_count == 0 {
1428 return vec![];
1429 }
1430 if single_count == 1 {
1431 return vec![Pairing {
1432 single_range_idx,
1433 single_idx_in_range,
1434 other_range,
1435 }];
1436 }
1437
1438 let mut error = 2 * single_count - other_count;
1439
1440 let mut other_start = *other_range.start();
1441 let mut other_end = other_start;
1442
1443 let mut pairings = Vec::new();
1444
1445 while other_end <= *other_range.end() {
1446 if error > 0 {
1447 pairings.push(Pairing {
1448 single_range_idx,
1449 single_idx_in_range,
1450 other_range: other_start..=other_end,
1451 });
1452
1453 other_start = other_end + 1;
1454 single_idx_in_range += 1;
1455 error += 2 * (single_count - other_count);
1456 } else {
1457 error += 2 * single_count;
1458 }
1459
1460 other_end += 1;
1461 }
1462
1463 if let Some(last_pairing) = pairings.last_mut() {
1464 last_pairing.other_range = *last_pairing.other_range.start()..=*other_range.end();
1465 }
1466
1467 pairings
1468 }
1469}
1470
1471#[derive(Debug, Clone)]
1473struct Pairing {
1474 single_range_idx: usize,
1477
1478 single_idx_in_range: usize,
1480
1481 other_range: RangeInclusive<usize>,
1483}
1484
1485impl Pairing {
1486 fn new_triangle(
1487 single_range_idx: usize,
1488 single_idx_in_range: usize,
1489 other_idx1: usize,
1490 other_idx2: usize,
1491 other_len: usize,
1492 ) -> Self {
1493 let other_range = if (other_idx1 as i32 - other_idx2 as i32).abs() == 1 {
1494 other_idx1.min(other_idx2)..=other_idx1.max(other_idx2)
1495 } else {
1496 other_idx1.max(other_idx2)..=other_idx1.min(other_idx2) + other_len
1497 };
1498
1499 Self {
1500 single_range_idx,
1501 single_idx_in_range,
1502 other_range,
1503 }
1504 }
1505
1506 fn all_vertex_ids(&self, v_ids: [&[VertexId]; 2]) -> Vec<VertexId> {
1507 let single_ids = &v_ids[self.single_range_idx];
1508 let other_ids = &v_ids[1 - self.single_range_idx];
1509
1510 let mut all_ids = vec![];
1511
1512 all_ids.push(single_ids[self.single_idx_in_range % single_ids.len()]);
1513
1514 let mut idx = *self.other_range.start() % other_ids.len();
1515 all_ids.push(other_ids[idx]);
1516 while idx != *self.other_range.end() % other_ids.len() {
1517 idx += 1;
1518 idx %= other_ids.len();
1519 all_ids.push(other_ids[idx]);
1520 }
1521
1522 all_ids
1523 }
1524
1525 fn first_pair(&self, v_ids: [&[VertexId]; 2]) -> (VertexId, VertexId) {
1526 let single_ids = &v_ids[self.single_range_idx];
1527 let other_ids = &v_ids[1 - self.single_range_idx];
1528
1529 let single_v_id = single_ids[self.single_idx_in_range % single_ids.len()];
1530 let other_v_id = other_ids[*self.other_range.start() % other_ids.len()];
1531
1532 (single_v_id, other_v_id)
1533 }
1534
1535 fn last_pair(&self, v_ids: [&[VertexId]; 2]) -> (VertexId, VertexId) {
1536 let single_ids = &v_ids[self.single_range_idx];
1537 let other_ids = &v_ids[1 - self.single_range_idx];
1538
1539 let single_v_id = single_ids[self.single_idx_in_range % single_ids.len()];
1540 let other_v_id = other_ids[*self.other_range.end() % other_ids.len()];
1541
1542 (single_v_id, other_v_id)
1543 }
1544
1545 fn fill_faces(&self, v_ids: [&[VertexId]; 2]) -> Vec<PlannedFace> {
1546 let single_ids = v_ids[self.single_range_idx];
1547 let other_ids = v_ids[1 - self.single_range_idx];
1548
1549 let single_v_id = single_ids[self.single_idx_in_range % single_ids.len()];
1550 let mut faces = Vec::<PlannedFace>::new();
1551
1552 let mut others = self.other_range.clone();
1553
1554 if others.start() == others.end() {
1555 return faces;
1556 }
1557
1558 let mut prev_other_v_id = other_ids[others.next().unwrap() % other_ids.len()];
1559 let mut face_order = PlannedFaceOrder::Middle;
1560
1561 if prev_other_v_id == single_v_id {
1562 face_order = PlannedFaceOrder::Start;
1563
1564 if let Some(second_idx) = others.next() {
1565 prev_other_v_id = other_ids[second_idx % other_ids.len()];
1566 } else {
1567 return faces;
1568 }
1569 }
1570
1571 for other_idx in others {
1572 let other_v_id = other_ids[other_idx % other_ids.len()];
1573
1574 if other_v_id == single_v_id {
1575 break;
1576 }
1577
1578 if self.single_range_idx == 0 {
1580 faces.push(PlannedFace::new(
1581 prev_other_v_id,
1582 single_v_id,
1583 other_v_id,
1584 face_order,
1585 ));
1586 } else {
1587 faces.push(PlannedFace::new(
1588 prev_other_v_id,
1589 other_v_id,
1590 single_v_id,
1591 face_order,
1592 ));
1593 }
1594
1595 face_order = PlannedFaceOrder::Middle;
1596 prev_other_v_id = other_v_id;
1597 }
1598
1599 faces
1600 }
1601
1602 #[cfg(feature = "rerun")]
1603 fn log_rerun(
1604 &self,
1605 label: impl AsRef<str>,
1606 v_ids: [&[VertexId]; 2],
1607 mesh_graph: &MeshGraph,
1608 ) -> Option<()> {
1609 use crate::utils::vec3_array;
1610
1611 let single_v_ids = v_ids[self.single_range_idx];
1612 let other_v_ids = v_ids[1 - self.single_range_idx];
1613
1614 let single_v_id = single_v_ids[self.single_idx_in_range];
1615 let single_pos = vec3_array(mesh_graph.positions.get(single_v_id)?);
1616
1617 let mut other_pos = vec![];
1618 let mut idx = self.other_range.start() % other_v_ids.len();
1619
1620 other_pos.push(vec3_array(mesh_graph.positions.get(other_v_ids[idx])?));
1621 while idx != *self.other_range.end() % other_v_ids.len() {
1622 idx += 1;
1623 idx %= other_v_ids.len();
1624 other_pos.push(vec3_array(mesh_graph.positions.get(other_v_ids[idx])?));
1625 }
1626
1627 crate::RR
1628 .log(
1629 format!("pairing/{}/single", label.as_ref()),
1630 &rerun::Points3D::new([single_pos.clone()]),
1631 )
1632 .unwrap();
1633 crate::RR
1634 .log(
1635 format!("pairing/{}/other", label.as_ref()),
1636 &rerun::LineStrips3D::new(
1637 other_pos
1638 .into_iter()
1639 .map(|other_pos| [single_pos.clone(), other_pos]),
1640 ),
1641 )
1642 .unwrap();
1643
1644 Some(())
1645 }
1646}
1647
1648#[derive(Debug)]
1662struct PlannedFace {
1663 order: PlannedFaceOrder,
1664 v1: VertexId,
1665 new_he_v1: VertexId,
1666 new_he_v2: VertexId,
1667}
1668
1669#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1670enum PlannedFaceOrder {
1671 Start,
1672 Middle,
1673 End,
1674 Single,
1675}
1676
1677impl PlannedFace {
1678 fn new(
1679 v_id1: VertexId,
1680 new_he_v_id1: VertexId,
1681 new_he_v_id2: VertexId,
1682 order: PlannedFaceOrder,
1683 ) -> Self {
1684 PlannedFace {
1685 order,
1686 v1: v_id1,
1687 new_he_v1: new_he_v_id1,
1688 new_he_v2: new_he_v_id2,
1689 }
1690 }
1691
1692 #[instrument(skip(mesh_graph))]
1693 fn add_to_mesh_graph(
1694 &self,
1695 mesh_graph: &mut MeshGraph,
1696 ) -> Option<(Option<HalfedgeId>, AddFace)> {
1697 #[cfg(feature = "rerun")]
1698 self.log_rerun("add_to_mesh_graph", mesh_graph);
1699
1700 let add_or_get_edge1 = mesh_graph.add_or_get_boundary_edge(self.v1, self.new_he_v1)?;
1701 let add_or_get_edge2 =
1702 mesh_graph.add_or_get_boundary_edge(self.new_he_v1, self.new_he_v2)?;
1703
1704 let mut add_face = mesh_graph.add_face_from_halfedges(
1705 add_or_get_edge1.start_to_end_he_id,
1706 add_or_get_edge2.start_to_end_he_id,
1707 )?;
1708
1709 if add_or_get_edge1.new_start_to_end {
1710 add_face
1711 .halfedge_ids
1712 .push(add_or_get_edge1.start_to_end_he_id);
1713 }
1714 if add_or_get_edge1.new_twin {
1715 add_face.halfedge_ids.push(add_or_get_edge1.twin_he_id);
1716 }
1717 if add_or_get_edge2.new_start_to_end {
1718 add_face
1719 .halfedge_ids
1720 .push(add_or_get_edge2.start_to_end_he_id);
1721 }
1722 if add_or_get_edge2.new_twin {
1723 add_face.halfedge_ids.push(add_or_get_edge2.twin_he_id);
1724 }
1725
1726 Some((
1727 if matches!(
1728 self.order,
1729 PlannedFaceOrder::Start | PlannedFaceOrder::Middle
1730 ) {
1731 Some(add_or_get_edge2.twin_he_id)
1732 } else {
1733 None
1734 },
1735 add_face,
1736 ))
1737 }
1738
1739 #[instrument(skip(mesh_graph))]
1740 fn add_to_mesh_graph_and_he(
1741 &self,
1742 mesh_graph: &mut MeshGraph,
1743 existing_he_id: HalfedgeId,
1744 ) -> Option<(Option<HalfedgeId>, AddFace)> {
1745 #[cfg(feature = "rerun")]
1746 self.log_rerun("add_to_mesh_graph_and_he", mesh_graph);
1747
1748 match self.order {
1749 PlannedFaceOrder::Middle => {
1750 let AddEdge {
1751 start_to_end_he_id,
1752 twin_he_id,
1753 } = mesh_graph.add_edge(self.new_he_v1, self.new_he_v2)?;
1754
1755 let mut add_face =
1756 mesh_graph.add_face_from_halfedges(existing_he_id, start_to_end_he_id)?;
1757 add_face.halfedge_ids.push(start_to_end_he_id);
1758 add_face.halfedge_ids.push(twin_he_id);
1759
1760 Some((Some(twin_he_id), add_face))
1761 }
1762 PlannedFaceOrder::End => {
1763 let add_or_get_edge =
1764 mesh_graph.add_or_get_boundary_edge(self.new_he_v1, self.new_he_v2)?;
1765
1766 let mut add_face = mesh_graph
1767 .add_face_from_halfedges(existing_he_id, add_or_get_edge.start_to_end_he_id)?;
1768 if add_or_get_edge.new_start_to_end {
1769 add_face
1770 .halfedge_ids
1771 .push(add_or_get_edge.start_to_end_he_id);
1772 }
1773 if add_or_get_edge.new_twin {
1774 add_face.halfedge_ids.push(add_or_get_edge.twin_he_id);
1775 }
1776
1777 Some((None, add_face))
1778 }
1779 _ => {
1780 error!("Invalid face order {:?}", self.order);
1781 None
1782 }
1783 }
1784 }
1785
1786 #[cfg(feature = "rerun")]
1787 fn log_rerun(&self, label: &str, mesh_graph: &MeshGraph) {
1788 use crate::{RR, utils::vec3_array};
1789
1790 let (positions, labels): (Vec<_>, Vec<_>) =
1791 [(self.v1, "1"), (self.new_he_v1, "2"), (self.new_he_v2, "3")]
1792 .into_iter()
1793 .filter_map(|(v_id, labl)| {
1794 mesh_graph.positions.get(v_id).and_then(|a| Some((a, labl)))
1795 })
1796 .unzip();
1797
1798 RR.log(
1799 format!("meshgraph/planned_face/{label}/positions"),
1800 &rerun::Points3D::new(positions.iter().map(vec3_array)).with_labels(labels),
1801 )
1802 .unwrap();
1803
1804 RR.log(
1805 format!("meshgraph/planned_face/{label}/edges"),
1806 &rerun::Arrows3D::from_vectors(
1807 positions
1808 .iter()
1809 .circular_array_windows()
1810 .map(|[a, b]| vec3_array(*b - *a)),
1811 )
1812 .with_origins(positions.iter().map(vec3_array)),
1813 )
1814 .unwrap();
1815 }
1816}