1use axiolid::contracts::ExecutionOptions;
13use axiolid::mesh_hidden_lines::{
14 FaceSource, VisibleFaceLimits, VisibleFaceOutcome, VisibleFaceRequest,
15};
16
17use super::*;
18
19#[repr(C)]
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct AxiolidVisibleFaceLimits {
23 pub max_meshes: usize,
24 pub max_source_vertices: usize,
25 pub max_source_triangles: usize,
26 pub max_candidate_pairs: usize,
27 pub max_regions: usize,
28 pub max_region_vertices: usize,
29}
30
31#[non_exhaustive]
33#[repr(i32)]
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum AxiolidFaceSource {
36 Group = 1,
38 Cap = 2,
40 Unknown = 255,
41}
42
43#[repr(C)]
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct AxiolidVisibleRegion {
47 pub mesh_id: u64,
49 pub first_ring: usize,
51 pub ring_count: usize,
53 pub first_triangle: usize,
55 pub triangle_count: usize,
57 pub group: u32,
59 pub source: AxiolidFaceSource,
60}
61
62#[repr(C)]
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub struct AxiolidRegionRing {
66 pub first_point: usize,
67 pub point_count: usize,
68}
69
70#[repr(C)]
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub struct AxiolidVisibleFaceCounts {
74 pub regions: usize,
75 pub rings: usize,
76 pub points: usize,
77 pub triangles: usize,
78}
79
80#[repr(C)]
82#[derive(Debug, Clone, Copy, PartialEq)]
83pub struct AxiolidVisibleFaceEvidence {
84 pub source_meshes: usize,
85 pub source_triangles: usize,
86 pub edge_on_triangles: usize,
87 pub removed_triangles: usize,
88 pub boundary_segments: usize,
89 pub intersection_segments: usize,
90 pub crossings: usize,
91 pub pieces: usize,
92 pub tied_sides: usize,
93 pub candidate_pairs: usize,
94 pub exact_escalations: usize,
95 pub regions: usize,
96 pub cap_regions: usize,
97 pub rerouted_edges: usize,
98 pub collapsed_regions: usize,
99 pub collapsed_holes: usize,
100 pub snap_displacement: f64,
103}
104
105fn face_source(value: FaceSource) -> (AxiolidFaceSource, u32) {
106 match value {
107 FaceSource::Group(group) => (AxiolidFaceSource::Group, group.0),
108 FaceSource::Cap => (AxiolidFaceSource::Cap, 0),
109 _ => (AxiolidFaceSource::Unknown, 0),
110 }
111}
112
113fn counts(outcome: &VisibleFaceOutcome) -> AxiolidVisibleFaceCounts {
114 let mut counts = AxiolidVisibleFaceCounts {
115 regions: outcome.regions.len(),
116 rings: 0,
117 points: 0,
118 triangles: 0,
119 };
120 for region in &outcome.regions {
121 counts.rings += 1 + region.holes.len();
122 counts.points += region.outer.len() + region.holes.iter().map(Vec::len).sum::<usize>();
123 counts.triangles += region.triangles.len();
124 }
125 counts
126}
127
128#[no_mangle]
136pub unsafe extern "C" fn axiolid_v0_4_visible_faces(
137 context: AxiolidContextHandle,
138 meshes: *const AxiolidViewMesh,
139 mesh_count: usize,
140 view: *const AxiolidView,
141 region: *const AxiolidImageBox,
142 limits: *const AxiolidVisibleFaceLimits,
143 tolerance_value: AxiolidTolerance,
144 out_result: *mut AxiolidResultHandle,
145) -> AxiolidStatus {
146 const OPERATION: AxiolidOperation = AxiolidOperation::MeshVisibleFaces;
147 boundary(|| {
148 if out_result.is_null()
149 || view.is_null()
150 || limits.is_null()
151 || (mesh_count != 0 && meshes.is_null())
152 {
153 return AxiolidStatus::NullPointer;
154 }
155 let Some(context) = context_entry(context.0) else {
156 return AxiolidStatus::InvalidHandle;
157 };
158 let mut context = lock_unpoisoned(&context);
159 if mesh_count > context.config.max_meshes as usize {
160 return AxiolidStatus::LimitExceeded;
161 }
162 let (view, limits) = unsafe { (view.read(), limits.read()) };
164 let region = unsafe { region.as_ref() };
166 let entries = match unsafe { mesh_entries(meshes, mesh_count) } {
169 Ok(entries) => entries,
170 Err(status) => return status,
171 };
172 let Ok(tolerance) = tolerance(tolerance_value) else {
173 return record_refusal(
174 context,
175 OPERATION,
176 Refusal::invalid("tolerance must be finite and non-negative"),
177 tolerance_value,
178 );
179 };
180 let decoded = (|| {
181 let view = read_view(&view)?;
182 let region = region.map(read_region).transpose()?;
183 let meshes = entries
184 .iter()
185 .map(|entry| read_mesh(entry, &context))
186 .collect::<Result<Vec<_>, _>>()?;
187 Ok((view, region, meshes))
188 })();
189 let (view, region, decoded) = match decoded {
190 Ok(decoded) => decoded,
191 Err(refusal) => return record_refusal(context, OPERATION, refusal, tolerance_value),
192 };
193 let request_limits = VisibleFaceLimits::new(
194 limits.max_meshes,
195 limits.max_source_vertices,
196 limits.max_source_triangles,
197 limits.max_candidate_pairs,
198 limits.max_regions,
199 limits.max_region_vertices,
200 );
201 let options = ExecutionOptions::new(tolerance);
202 let outcome = {
205 let state: &Context = &context;
206 let inputs = match mesh_inputs(state, &decoded) {
207 Ok(inputs) => inputs,
208 Err(refusal) => {
209 return record_refusal(context, OPERATION, refusal, tolerance_value)
210 }
211 };
212 let mut request = VisibleFaceRequest::new(&inputs, view, request_limits);
213 if let Some(region) = region {
214 request = request.with_region(region);
215 }
216 state.application.visible_faces(&request, &options)
217 };
218 let outcome = match outcome {
219 Ok(outcome) => outcome,
220 Err(error) => return record_drawing_error(context, OPERATION, error),
221 };
222 let Ok(handle) = insert_result(&mut context, StoredResult::VisibleFaces(outcome)) else {
223 return AxiolidStatus::LimitExceeded;
224 };
225 unsafe { out_result.write(AxiolidResultHandle(handle)) };
227 AxiolidStatus::Ok
228 })
229}
230
231fn with_outcome(
233 context: AxiolidContextHandle,
234 result: AxiolidResultHandle,
235 read: impl FnOnce(&VisibleFaceOutcome) -> AxiolidStatus,
236) -> AxiolidStatus {
237 let Some(context) = context_entry(context.0) else {
238 return AxiolidStatus::InvalidHandle;
239 };
240 let context = lock_unpoisoned(&context);
241 match context.results.get(result.0) {
242 Some(StoredResult::VisibleFaces(outcome)) => read(outcome),
243 Some(_) => AxiolidStatus::WrongResultKind,
244 None => AxiolidStatus::InvalidHandle,
245 }
246}
247
248#[no_mangle]
252pub unsafe extern "C" fn axiolid_v0_4_visible_faces_counts(
253 context: AxiolidContextHandle,
254 result: AxiolidResultHandle,
255 out_counts: *mut AxiolidVisibleFaceCounts,
256) -> AxiolidStatus {
257 boundary(|| {
258 if out_counts.is_null() {
259 return AxiolidStatus::NullPointer;
260 }
261 with_outcome(context, result, |outcome| {
262 unsafe { out_counts.write(counts(outcome)) };
264 AxiolidStatus::Ok
265 })
266 })
267}
268
269#[no_mangle]
280pub unsafe extern "C" fn axiolid_v0_4_visible_faces_copy(
281 context: AxiolidContextHandle,
282 result: AxiolidResultHandle,
283 regions: *mut AxiolidVisibleRegion,
284 region_cap: usize,
285 rings: *mut AxiolidRegionRing,
286 ring_cap: usize,
287 points_xy: *mut f64,
288 point_cap: usize,
289 triangles: *mut u32,
290 triangle_cap: usize,
291) -> AxiolidStatus {
292 boundary(|| {
293 if (region_cap != 0 && regions.is_null())
294 || (ring_cap != 0 && rings.is_null())
295 || (point_cap != 0 && points_xy.is_null())
296 || (triangle_cap != 0 && triangles.is_null())
297 {
298 return AxiolidStatus::NullPointer;
299 }
300 with_outcome(context, result, |outcome| {
301 let need = counts(outcome);
302 if region_cap < need.regions
303 || ring_cap < need.rings
304 || point_cap < need.points
305 || triangle_cap < need.triangles
306 {
307 return AxiolidStatus::BufferTooSmall;
308 }
309 let (mut ring_at, mut point_at, mut triangle_at) = (0_usize, 0_usize, 0_usize);
310 for (index, region) in outcome.regions.iter().enumerate() {
311 let (source, group) = face_source(region.source);
312 let value = AxiolidVisibleRegion {
313 mesh_id: region.mesh.0,
314 first_ring: ring_at,
315 ring_count: 1 + region.holes.len(),
316 first_triangle: triangle_at,
317 triangle_count: region.triangles.len(),
318 group,
319 source,
320 };
321 unsafe { regions.add(index).write(value) };
323 for ring in std::iter::once(®ion.outer).chain(®ion.holes) {
324 let value = AxiolidRegionRing {
325 first_point: point_at,
326 point_count: ring.len(),
327 };
328 unsafe { rings.add(ring_at).write(value) };
330 ring_at += 1;
331 for point in ring {
332 unsafe {
334 points_xy.add(2 * point_at).write(point.x);
335 points_xy.add(2 * point_at + 1).write(point.y);
336 }
337 point_at += 1;
338 }
339 }
340 for &triangle in ®ion.triangles {
341 unsafe { triangles.add(triangle_at).write(triangle) };
343 triangle_at += 1;
344 }
345 }
346 AxiolidStatus::Ok
347 })
348 })
349}
350
351#[no_mangle]
355pub unsafe extern "C" fn axiolid_v0_4_visible_faces_evidence(
356 context: AxiolidContextHandle,
357 result: AxiolidResultHandle,
358 out: *mut AxiolidVisibleFaceEvidence,
359) -> AxiolidStatus {
360 boundary(|| {
361 if out.is_null() {
362 return AxiolidStatus::NullPointer;
363 }
364 with_outcome(context, result, |outcome| {
365 let evidence = &outcome.evidence;
366 let value = AxiolidVisibleFaceEvidence {
367 source_meshes: evidence.source_meshes,
368 source_triangles: evidence.source_triangles,
369 edge_on_triangles: evidence.edge_on_triangles,
370 removed_triangles: evidence.removed_triangles,
371 boundary_segments: evidence.boundary_segments,
372 intersection_segments: evidence.intersection_segments,
373 crossings: evidence.crossings,
374 pieces: evidence.pieces,
375 tied_sides: evidence.tied_sides,
376 candidate_pairs: evidence.candidate_pairs,
377 exact_escalations: evidence.exact_escalations,
378 regions: evidence.regions,
379 cap_regions: evidence.cap_regions,
380 rerouted_edges: evidence.rerouted_edges,
381 collapsed_regions: evidence.collapsed_regions,
382 collapsed_holes: evidence.collapsed_holes,
383 snap_displacement: evidence.snap_displacement.get(),
384 };
385 unsafe { out.write(value) };
387 AxiolidStatus::Ok
388 })
389 })
390}