honeycomb_kernels/grisubal/routines/
clip.rs1use std::collections::{HashSet, VecDeque};
4
5use honeycomb_core::cmap::{CMap2, DartIdType, FaceIdType, NULL_DART_ID, OrbitPolicy};
6use honeycomb_core::geometry::{CoordsFloat, Vertex2};
7
8use crate::grisubal::GrisubalError;
9use crate::grisubal::model::Boundary;
10
11pub fn clip_left<T: CoordsFloat>(cmap: &mut CMap2<T>) -> Result<(), GrisubalError> {
13 let marked = mark_faces(cmap, Boundary::Left, Boundary::Right)?;
15
16 delete_darts(cmap, marked, Boundary::Right);
18
19 Ok(())
20}
21
22pub fn clip_right<T: CoordsFloat>(cmap: &mut CMap2<T>) -> Result<(), GrisubalError> {
24 let marked = mark_faces(cmap, Boundary::Right, Boundary::Left)?;
26
27 delete_darts(cmap, marked, Boundary::Left);
29
30 Ok(())
31}
32
33#[allow(clippy::cast_possible_truncation)]
36fn mark_faces<T: CoordsFloat>(
37 cmap: &CMap2<T>,
38 mark: Boundary,
39 other: Boundary,
40) -> Result<HashSet<FaceIdType>, GrisubalError> {
41 let mut marked: HashSet<FaceIdType> = HashSet::from([0]);
42 let mut queue: VecDeque<FaceIdType> = (1..cmap.n_darts() as DartIdType)
43 .filter_map(|dart_id| {
44 if cmap.force_read_attribute::<Boundary>(dart_id) == Some(mark)
46 && !cmap.is_free(dart_id)
47 {
48 return Some(cmap.face_id(dart_id));
49 }
50 None
51 })
52 .collect();
53 while let Some(face_id) = queue.pop_front() {
54 if marked.insert(face_id) {
56 let mut darts = cmap.orbit(OrbitPolicy::Face, face_id as DartIdType);
58 if darts.any(|did| cmap.force_read_attribute::<Boundary>(did) == Some(other)) {
59 return Err(GrisubalError::InconsistentOrientation(
60 "between-boundary inconsistency",
61 ));
62 }
63 let darts = cmap.orbit(OrbitPolicy::Face, face_id as DartIdType);
65 queue.extend(darts.filter_map(|dart_id| {
66 if matches!(
67 cmap.force_read_attribute::<Boundary>(cmap.beta::<2>(dart_id)),
68 Some(Boundary::None) | None
69 ) {
70 return Some(cmap.face_id(cmap.beta::<2>(dart_id)));
71 }
72 None
73 }));
74 }
75 }
76 marked.remove(&0);
77 Ok(marked)
78}
79
80#[allow(clippy::cast_possible_truncation)]
81fn delete_darts<T: CoordsFloat>(
82 cmap: &mut CMap2<T>,
83 marked: HashSet<FaceIdType>,
84 kept_boundary: Boundary,
85) {
86 let kept_boundary_components: Vec<(DartIdType, Vertex2<T>)> = (1..cmap.n_darts() as DartIdType)
87 .filter_map(|dart_id| {
88 if cmap.force_read_attribute::<Boundary>(dart_id) == Some(kept_boundary) {
89 return Some((
90 dart_id,
91 cmap.force_read_vertex(cmap.vertex_id(dart_id))
92 .expect("E: found a topological vertex with no associated coordinates"),
93 ));
94 }
95 None
96 })
97 .collect();
98
99 for face_id in marked {
100 let darts: Vec<DartIdType> = cmap
101 .orbit(OrbitPolicy::Face, face_id as DartIdType)
102 .collect();
103 for &dart in &darts {
104 let _ = cmap.force_remove_vertex(cmap.vertex_id(dart));
105 cmap.set_betas(dart, [NULL_DART_ID; 3]);
106 cmap.remove_free_dart(dart);
107 }
108 }
109
110 for (dart, vertex) in kept_boundary_components {
111 cmap.set_beta::<2>(dart, NULL_DART_ID); cmap.force_write_vertex(cmap.vertex_id(dart), vertex);
113 }
114}