honeycomb_kernels/remeshing/
capture.rs1use std::collections::VecDeque;
2
3use honeycomb_core::{
4 cmap::{CMap2, DartIdType, NULL_DART_ID, OrbitPolicy},
5 geometry::CoordsFloat,
6};
7use rustc_hash::FxHashSet as HashSet;
8use vtkio::Vtk;
9
10use crate::utils::{CurveIdType, EdgeAnchor, FaceAnchor, VertexAnchor};
11use crate::{
12 grid_generation::GridBuilder,
13 grisubal::{
14 Clip, GrisubalError,
15 model::{Boundary, Geometry2},
16 routines::{
17 clip_left, clip_right, compute_intersection_ids, compute_overlapping_grid,
18 detect_orientation_issue, generate_edge_data, generate_intersection_data,
19 group_intersections_per_edge, insert_edges_in_map, insert_intersections,
20 },
21 },
22};
23
24#[allow(clippy::missing_errors_doc)]
25#[allow(clippy::needless_pass_by_value)]
46pub fn capture_geometry<T: CoordsFloat>(
47 file_path: impl AsRef<std::path::Path>,
48 grid_cell_sizes: [T; 2],
49 clip: Clip,
50) -> Result<CMap2<T>, GrisubalError> {
51 let geometry_vtk = match Vtk::import(file_path) {
53 Ok(vtk) => vtk,
54 Err(e) => panic!("E: could not open specified vtk file - {e}"),
55 };
56
57 let geometry = Geometry2::try_from(geometry_vtk)?;
59
60 detect_orientation_issue(&geometry)?;
62
63 let ([nx, ny], origin) = compute_overlapping_grid(&geometry, grid_cell_sizes, true)?;
65 let [cx, cy] = grid_cell_sizes;
66
67 let mut cmap = GridBuilder::<2, T>::default()
69 .n_cells([nx, ny])
70 .len_per_cell([cx, cy])
71 .origin([origin.0, origin.1])
72 .add_attribute::<Boundary>() .add_attribute::<VertexAnchor>()
74 .add_attribute::<EdgeAnchor>()
75 .add_attribute::<FaceAnchor>()
76 .build()
77 .expect("E: unreachable"); let (new_segments, intersection_metadata) =
84 generate_intersection_data(&cmap, &geometry, [nx, ny], [cx, cy], origin);
85 let n_intersec = intersection_metadata.len();
87 let (edge_intersec, dart_slices) =
88 group_intersections_per_edge(&mut cmap, intersection_metadata);
89 let intersection_darts = compute_intersection_ids(n_intersec, &edge_intersec, &dart_slices);
90
91 insert_intersections(&cmap, &edge_intersec, &dart_slices);
93
94 let edges = generate_edge_data(&cmap, &geometry, &new_segments, &intersection_darts);
96
97 insert_edges_in_map(&mut cmap, &edges);
99
100 match clip {
102 Clip::Left => clip_left(&mut cmap)?,
103 Clip::Right => clip_right(&mut cmap)?,
104 Clip::None => {}
105 }
106
107 cmap.remove_attribute_storage::<Boundary>();
109
110 Ok(cmap)
111}
112
113#[derive(thiserror::Error, Debug, PartialEq, Eq)]
115pub enum ClassificationError {
116 #[error("missing attribute for classification: {0}")]
118 MissingAttribute(&'static str),
119 #[error("geometry contains an ambiguous or unsupported configuration: {0}")]
121 UnsupportedGeometry(&'static str),
122}
123
124#[allow(clippy::cast_possible_truncation, clippy::too_many_lines)]
148pub fn classify_capture<T: CoordsFloat>(cmap: &CMap2<T>) -> Result<(), ClassificationError> {
149 if !cmap.contains_attribute::<VertexAnchor>() {
150 Err(ClassificationError::MissingAttribute(
151 std::any::type_name::<VertexAnchor>(),
152 ))?;
153 }
154 if !cmap.contains_attribute::<EdgeAnchor>() {
155 Err(ClassificationError::MissingAttribute(
156 std::any::type_name::<EdgeAnchor>(),
157 ))?;
158 }
159 if !cmap.contains_attribute::<FaceAnchor>() {
160 Err(ClassificationError::MissingAttribute(
161 std::any::type_name::<FaceAnchor>(),
162 ))?;
163 }
164
165 let mut curve_id = 0;
166
167 for (i, dart) in cmap
169 .iter_vertices()
170 .filter_map(|v| {
171 cmap.force_read_attribute::<VertexAnchor>(v).and_then(|_| {
172 cmap.orbit(OrbitPolicy::Vertex, v)
173 .find(|d| cmap.beta::<2>(*d) == NULL_DART_ID)
174 })
175 })
176 .enumerate()
177 {
178 mark_curve(cmap, dart, i as CurveIdType)?;
179 curve_id = curve_id.max(i);
180 }
181 while let Some(dart) = (1..cmap.n_darts() as DartIdType)
183 .filter_map(|d| {
184 let used = !cmap.is_unused(d);
186 if used {
187 cmap.orbit(OrbitPolicy::Vertex, d)
188 .find(|dd| cmap.beta::<2>(*dd) == NULL_DART_ID)
189 } else {
190 None
191 }
192 })
193 .find(|d| {
194 cmap.force_read_attribute::<EdgeAnchor>(cmap.edge_id(*d))
195 .is_none()
196 })
197 {
198 curve_id += 1; cmap.force_write_attribute(
200 cmap.vertex_id(dart),
201 VertexAnchor::Curve(curve_id as CurveIdType),
202 );
203 mark_curve(cmap, dart, curve_id as CurveIdType)?;
204 }
205
206 let mut surface_id = 0;
208 let mut queue = VecDeque::new();
209 let mut marked = HashSet::default();
210 marked.insert(0);
211 cmap.iter_faces()
212 .for_each(|f| {
215 if cmap.force_read_attribute::<FaceAnchor>(f).is_none() {
216 queue.clear();
217 queue.push_front(f);
218 while let Some(crt) = queue.pop_front() {
219 cmap.force_write_attribute(crt, FaceAnchor::Surface(surface_id));
221 cmap.orbit(OrbitPolicy::Face, crt as DartIdType)
222 .filter(|d| {
223 cmap.force_read_attribute::<EdgeAnchor>(cmap.edge_id(*d))
224 .is_none()
225 })
226 .for_each(|d| {
227 cmap.force_write_attribute(
228 cmap.edge_id(d),
229 EdgeAnchor::Surface(surface_id),
230 );
231 if cmap
232 .force_read_attribute::<VertexAnchor>(cmap.vertex_id(d))
233 .is_none()
234 {
235 cmap.force_write_attribute(
236 cmap.vertex_id(d),
237 VertexAnchor::Surface(surface_id),
238 );
239 }
240 let neighbor_face = cmap.face_id(cmap.beta::<2>(d));
241 if marked.insert(neighbor_face) {
242 queue.push_back(neighbor_face);
243 }
244 });
245 }
246 surface_id += 1;
247 }
248 });
249
250 debug_assert!(
252 cmap.iter_vertices()
253 .all(|v| cmap.force_read_attribute::<VertexAnchor>(v).is_some()),
254 "E: Not all vertices are classified",
255 );
256 debug_assert!(
257 cmap.iter_edges()
258 .all(|e| cmap.force_read_attribute::<EdgeAnchor>(e).is_some()),
259 "E: Not all edges are classified",
260 );
261 debug_assert!(
262 cmap.iter_faces()
263 .all(|f| cmap.force_read_attribute::<FaceAnchor>(f).is_some()),
264 "E: Not all faces are classified",
265 );
266
267 Ok(())
268}
269
270#[allow(clippy::cast_possible_truncation)]
275fn mark_curve<T: CoordsFloat>(
276 cmap: &CMap2<T>,
277 start: DartIdType,
278 curve_id: CurveIdType,
279) -> Result<(), ClassificationError> {
280 cmap.force_write_attribute::<EdgeAnchor>(cmap.edge_id(start), EdgeAnchor::Curve(curve_id));
283 let mut next = cmap.beta::<1>(start);
284 while cmap
285 .force_read_attribute::<VertexAnchor>(cmap.vertex_id(next))
286 .is_none()
287 {
288 if let Some(crt) = cmap
289 .orbit(OrbitPolicy::Vertex, next)
290 .find(|d| cmap.beta::<2>(*d) == NULL_DART_ID)
291 {
292 cmap.force_write_attribute(cmap.vertex_id(crt), VertexAnchor::Curve(curve_id));
293 cmap.force_write_attribute(cmap.edge_id(crt), EdgeAnchor::Curve(curve_id));
294 next = cmap.beta::<1>(crt);
295 } else {
296 Err(ClassificationError::UnsupportedGeometry(
298 "open geometry or node outside of boundary",
299 ))?;
300 }
301 }
302 Ok(())
303}