axiolid_hidden_lines/faces/
provider.rs1use std::cmp::Ordering;
4
5use axiolid_contracts::{
6 Backend, BackendDescriptor, BackendId, CancellationGranularity, ExecutionOptions,
7 ExecutionTarget, GeomError, Operation, ScratchRequirement,
8};
9use axiolid_exact_arrangement::{arrange, PairBudget, Segment};
10use axiolid_mesh_hidden_lines_contract::{
11 FaceGroups, HiddenLineError, MeshVisibleFaces, VisibleFaceEvidence, VisibleFaceOutcome,
12 VisibleFaceRequest,
13};
14
15use super::intersect::intersection_segments;
16use super::label::label_piece;
17use super::points::Points;
18use super::regions::{build, OutputLimits};
19use super::segments::{clip_segments, crop_segments, edge_segments, PAIRS};
20use super::{Faces, Work};
21use crate::broad::{Broad, BroadPhase};
22use crate::prepare::{prepare, Prepared, ResourceNames, SharedLimits};
23use crate::view::require_orthographic;
24
25const SCRATCH_PER_ELEMENT: usize = 3072;
27
28#[derive(Debug, Default, Clone, Copy)]
49pub struct ScalarVisibleFaces;
50
51impl ScalarVisibleFaces {
52 pub const ID: BackendId = BackendId::new("scalar-visible-faces");
54
55 #[must_use]
57 pub const fn new() -> Self {
58 Self
59 }
60}
61
62impl Backend for ScalarVisibleFaces {
63 fn descriptor(&self) -> BackendDescriptor {
64 BackendDescriptor::new(Self::ID, ExecutionTarget::PortableCpu)
65 }
66}
67
68impl MeshVisibleFaces for ScalarVisibleFaces {
69 fn scratch_requirement(&self) -> ScratchRequirement {
70 ScratchRequirement::PerElement {
71 bytes_per_element: SCRATCH_PER_ELEMENT,
72 }
73 }
74
75 fn cancellation_granularity(&self) -> CancellationGranularity {
76 CancellationGranularity::Incremental
77 }
78
79 fn visible_faces(
80 &self,
81 request: &VisibleFaceRequest<'_>,
82 options: &ExecutionOptions,
83 ) -> Result<VisibleFaceOutcome, HiddenLineError> {
84 require_orthographic(&request.view, Self::ID, Operation::MeshVisibleFaces)?;
85 run(BroadPhase::Tree, true, request, options)
86 }
87}
88
89#[doc(hidden)]
92#[derive(Debug, Default, Clone, Copy)]
93pub struct ScanVisibleFaces;
94
95impl Backend for ScanVisibleFaces {
96 fn descriptor(&self) -> BackendDescriptor {
97 BackendDescriptor::new(
98 BackendId::new("scalar-visible-faces-scan"),
99 ExecutionTarget::PortableCpu,
100 )
101 }
102}
103
104impl MeshVisibleFaces for ScanVisibleFaces {
105 fn scratch_requirement(&self) -> ScratchRequirement {
106 ScalarVisibleFaces.scratch_requirement()
107 }
108
109 fn cancellation_granularity(&self) -> CancellationGranularity {
110 CancellationGranularity::Incremental
111 }
112
113 fn visible_faces(
114 &self,
115 request: &VisibleFaceRequest<'_>,
116 options: &ExecutionOptions,
117 ) -> Result<VisibleFaceOutcome, HiddenLineError> {
118 require_orthographic(
119 &request.view,
120 self.descriptor().id,
121 Operation::MeshVisibleFaces,
122 )?;
123 run(BroadPhase::Scan, true, request, options)
124 }
125}
126
127#[doc(hidden)]
132#[derive(Debug, Default, Clone, Copy)]
133pub struct UnprunedVisibleFaces;
134
135impl Backend for UnprunedVisibleFaces {
136 fn descriptor(&self) -> BackendDescriptor {
137 BackendDescriptor::new(
138 BackendId::new("scalar-visible-faces-unpruned"),
139 ExecutionTarget::PortableCpu,
140 )
141 }
142}
143
144impl MeshVisibleFaces for UnprunedVisibleFaces {
145 fn scratch_requirement(&self) -> ScratchRequirement {
146 ScalarVisibleFaces.scratch_requirement()
147 }
148
149 fn cancellation_granularity(&self) -> CancellationGranularity {
150 CancellationGranularity::Incremental
151 }
152
153 fn visible_faces(
154 &self,
155 request: &VisibleFaceRequest<'_>,
156 options: &ExecutionOptions,
157 ) -> Result<VisibleFaceOutcome, HiddenLineError> {
158 require_orthographic(
159 &request.view,
160 self.descriptor().id,
161 Operation::MeshVisibleFaces,
162 )?;
163 run(BroadPhase::Tree, false, request, options)
164 }
165}
166
167fn run(
168 phase: BroadPhase,
169 prune: bool,
170 request: &VisibleFaceRequest<'_>,
171 options: &ExecutionOptions,
172) -> Result<VisibleFaceOutcome, HiddenLineError> {
173 options.check_cancelled()?;
174 for entry in request.meshes {
175 if let FaceGroups::PerTriangle(groups) = entry.groups {
176 if groups.len() != entry.mesh.triangle_count() {
177 return Err(HiddenLineError::mesh(
178 entry.id,
179 GeomError::InvalidInput(format!(
180 "{} face groups for {} triangles",
181 groups.len(),
182 entry.mesh.triangle_count()
183 )),
184 ));
185 }
186 }
187 }
188 let limits = request.limits;
189 let prepared = prepare(
190 request.meshes,
191 &request.view,
192 request.region,
193 SharedLimits {
194 max_meshes: limits.max_meshes,
195 max_source_vertices: limits.max_source_vertices,
196 max_source_triangles: limits.max_source_triangles,
197 },
198 &ResourceNames::VISIBLE_FACES,
199 options,
200 )?;
201 let Prepared {
202 verts,
203 tris,
204 total_triangles,
205 escalations,
206 ..
207 } = &prepared;
208 let broad = Broad::build(tris, phase);
209 let mut work = Work {
210 budget: PairBudget::new(limits.max_candidate_pairs, PAIRS),
211 escalations: *escalations,
212 };
213 let faces = Faces::new(
214 &prepared,
215 &broad,
216 request.meshes,
217 (request.view.near, request.view.far, request.region),
218 &mut work,
219 options,
220 )?;
221
222 let mut points = Points::new(verts.len());
223 let mut segments: Vec<Segment> = Vec::new();
224 edge_segments(
225 &faces,
226 &mut points,
227 prune,
228 &mut work,
229 options,
230 &mut segments,
231 )?;
232 clip_segments(&faces, &mut points, &mut work, options, &mut segments)?;
233 crop_segments(&faces, &mut points, &mut segments)?;
234 let outline_segments = segments.len();
235 let per_element = SCRATCH_PER_ELEMENT.saturating_mul(*total_triangles);
236 intersection_segments(
237 &faces,
238 &mut points,
239 &mut work,
240 per_element,
241 options,
242 &mut segments,
243 )?;
244 let arrangement = arrange(
245 &mut points.store,
246 &segments,
247 &mut work.budget,
248 per_element,
249 options,
250 )?;
251
252 let mut labels = Vec::with_capacity(arrangement.pieces.len());
253 let mut evidence = VisibleFaceEvidence::input_mesh(request.meshes.len(), *total_triangles);
254 for (index, piece) in arrangement.pieces.iter().enumerate() {
255 if index % 256 == 0 {
256 options.check_cancelled()?;
257 }
258 let ends = piece.ends.map(|v| arrangement.vertices[v.0].point);
259 let label = label_piece(&faces, &points.store, ends, &mut work)?;
260 evidence.tied_sides += label.iter().filter(|l| l.keys.len() > 1).count();
261 if label[0].keys != label[1].keys && piece.segments[0] < outline_segments {
262 evidence.boundary_segments += 1;
263 }
264 labels.push(label);
265 }
266
267 let built = build(
268 &faces,
269 &points.store,
270 &arrangement,
271 &labels,
272 OutputLimits {
273 max_regions: limits.max_regions,
274 max_region_vertices: limits.max_region_vertices,
275 },
276 per_element,
277 &mut work,
278 options,
279 )?;
280 let regions = built.regions;
281
282 evidence.edge_on_triangles = tris.iter().filter(|t| t.sigma == Ordering::Equal).count();
283 evidence.removed_triangles = faces.removed.iter().filter(|&&r| r).count();
284 evidence.intersection_segments = segments.len() - outline_segments;
285 evidence.crossings = arrangement.crossings;
286 evidence.pieces = arrangement.pieces.len();
287 evidence.candidate_pairs = work.budget.used;
288 evidence.exact_escalations = work.escalations + points.store.escalations();
289 evidence.regions = regions.len();
290 evidence.cap_regions = regions
291 .iter()
292 .filter(|r| r.source == axiolid_mesh_hidden_lines_contract::FaceSource::Cap)
293 .count();
294 evidence.snap_displacement = built.snap_displacement;
295 evidence.rerouted_edges = built.rerouted_edges;
296 evidence.collapsed_regions = built.collapsed_regions;
297 evidence.collapsed_holes = built.collapsed_holes;
298 Ok(VisibleFaceOutcome::new(request.view, regions, evidence))
299}