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