1#![forbid(unsafe_code)]
2
3use std::collections::HashMap;
11use std::fmt;
12
13use axiolid_core::Interval;
14use axiolid_curve::{Curve2, Curve3};
15use axiolid_surface::Surface;
16use axiolid_topology::{audit_brep, BRep, BRepHealth, EdgeId, FaceId, LoopId, ShellId};
17
18pub mod name;
20
21pub use name::{EdgeName, FaceName, Operand, SweptFace};
22
23macro_rules! geometry_id {
24 ($name:ident, $label:literal) => {
25 #[doc = concat!("Typed handle into the exact B-rep ", $label, " catalog.")]
26 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27 pub struct $name(u32);
28
29 impl $name {
30 fn from_index(index: usize) -> Self {
31 Self(u32::try_from(index).expect("exact B-rep catalog exceeds u32 capacity"))
32 }
33
34 pub const fn index(self) -> usize {
36 self.0 as usize
37 }
38 }
39 };
40}
41
42geometry_id!(Curve3Id, "3D curve");
43geometry_id!(Curve2Id, "2D pcurve");
44geometry_id!(SurfaceId, "surface");
45
46pub type ExactTopology = BRep<Curve3Id, Curve2Id, SurfaceId>;
48
49#[derive(Debug, Clone, PartialEq)]
57pub struct ExactBRep {
58 topology: ExactTopology,
59 curves3: Vec<Curve3>,
60 curves2: Vec<Curve2>,
61 surfaces: Vec<Surface>,
62 edge_intervals: HashMap<EdgeId, Interval>,
63 pcurve_intervals: HashMap<(LoopId, usize), Interval>,
64 face_names: HashMap<FaceId, FaceName>,
65 edge_names: HashMap<EdgeId, EdgeName>,
66}
67
68impl ExactBRep {
69 pub fn face_name(&self, face: FaceId) -> Option<&FaceName> {
75 self.face_names.get(&face)
76 }
77
78 pub fn edge_name(&self, edge: EdgeId) -> Option<&EdgeName> {
80 self.edge_names.get(&edge)
81 }
82
83 pub fn face_by_name(&self, name: &FaceName) -> Option<FaceId> {
89 self.face_names
90 .iter()
91 .find(|(_, candidate)| *candidate == name)
92 .map(|(id, _)| *id)
93 }
94
95 pub fn edge_by_name(&self, name: &EdgeName) -> Option<EdgeId> {
97 self.edge_names
98 .iter()
99 .find(|(_, candidate)| *candidate == name)
100 .map(|(id, _)| *id)
101 }
102 pub fn name_caps(&mut self, start: Option<FaceName>, end: Option<FaceName>) {
109 let start_id = self.topology.face_id_at(0);
110 let end_id = self.topology.face_id_at(1);
111 if let (Some(name), Some(id)) = (start, start_id) {
112 self.face_names.insert(id, name);
113 }
114 if let (Some(name), Some(id)) = (end, end_id) {
115 self.face_names.insert(id, name);
116 }
117 }
118
119 pub fn topology(&self) -> &ExactTopology {
121 &self.topology
122 }
123
124 pub fn curves3(&self) -> &[Curve3] {
126 &self.curves3
127 }
128
129 pub fn curves2(&self) -> &[Curve2] {
131 &self.curves2
132 }
133
134 pub fn surfaces(&self) -> &[Surface] {
136 &self.surfaces
137 }
138
139 pub fn edge_interval(&self, edge: EdgeId) -> Option<Interval> {
141 self.edge_intervals.get(&edge).copied()
142 }
143
144 pub fn pcurve_interval(&self, loop_id: LoopId, use_index: usize) -> Option<Interval> {
146 self.pcurve_intervals.get(&(loop_id, use_index)).copied()
147 }
148}
149
150#[derive(Debug, Clone, Default, PartialEq)]
152pub struct ExactBRepBuilder {
153 topology: ExactTopology,
154 curves3: Vec<Curve3>,
155 curves2: Vec<Curve2>,
156 surfaces: Vec<Surface>,
157 edge_intervals: HashMap<EdgeId, Interval>,
158 pcurve_intervals: HashMap<(LoopId, usize), Interval>,
159 face_names: HashMap<FaceId, FaceName>,
160 edge_names: HashMap<EdgeId, EdgeName>,
161}
162
163impl ExactBRepBuilder {
164 pub fn set_face_name(&mut self, face: FaceId, name: FaceName) {
170 self.face_names.insert(face, name);
171 }
172
173 pub fn set_edge_name(&mut self, edge: EdgeId, name: EdgeName) {
175 self.edge_names.insert(edge, name);
176 }
177 pub fn try_reserve(
179 &mut self,
180 curves3: usize,
181 curves2: usize,
182 surfaces: usize,
183 edge_intervals: usize,
184 pcurve_intervals: usize,
185 ) -> Result<(), std::collections::TryReserveError> {
186 self.curves3.try_reserve_exact(curves3)?;
187 self.curves2.try_reserve_exact(curves2)?;
188 self.surfaces.try_reserve_exact(surfaces)?;
189 self.edge_intervals.try_reserve(edge_intervals)?;
190 self.pcurve_intervals.try_reserve(pcurve_intervals)?;
191 Ok(())
192 }
193
194 pub fn add_curve3(&mut self, curve: Curve3) -> Curve3Id {
196 let id = Curve3Id::from_index(self.curves3.len());
197 self.curves3.push(curve);
198 id
199 }
200
201 pub fn add_curve2(&mut self, curve: Curve2) -> Curve2Id {
203 let id = Curve2Id::from_index(self.curves2.len());
204 self.curves2.push(curve);
205 id
206 }
207
208 pub fn add_surface(&mut self, surface: Surface) -> SurfaceId {
210 let id = SurfaceId::from_index(self.surfaces.len());
211 self.surfaces.push(surface);
212 id
213 }
214
215 pub fn topology_mut(&mut self) -> &mut ExactTopology {
217 &mut self.topology
218 }
219
220 pub fn set_edge_interval(&mut self, edge: EdgeId, interval: Interval) {
222 self.edge_intervals.insert(edge, interval);
223 }
224
225 pub fn set_pcurve_interval(&mut self, loop_id: LoopId, use_index: usize, interval: Interval) {
227 self.pcurve_intervals.insert((loop_id, use_index), interval);
228 }
229
230 pub fn append(&mut self, other: &ExactBRep, reverse: bool) -> Vec<ShellId> {
241 use axiolid_topology::{Edge, EdgeUse, Face, Loop, Orientation, Shell, Vertex};
242
243 let curves3: Vec<Curve3Id> = other
244 .curves3
245 .iter()
246 .map(|curve| self.add_curve3(curve.clone()))
247 .collect();
248 let curves2: Vec<Curve2Id> = other
249 .curves2
250 .iter()
251 .map(|curve| self.add_curve2(curve.clone()))
252 .collect();
253 let surfaces: Vec<SurfaceId> = other
254 .surfaces
255 .iter()
256 .map(|surface| self.add_surface(surface.clone()))
257 .collect();
258 let source = &other.topology;
259 let vertices: Vec<_> = source
260 .vertices()
261 .iter()
262 .map(|vertex| {
263 self.topology.add_vertex(Vertex {
264 position: vertex.position,
265 })
266 })
267 .collect();
268 let mut edges = Vec::with_capacity(source.edges().len());
269 for (index, edge) in source.edges().iter().enumerate() {
270 let id = self.topology.add_edge(Edge {
271 start: vertices[edge.start.index()],
272 end: vertices[edge.end.index()],
273 curve: edge.curve.map(|curve| curves3[curve.index()]),
274 });
275 if let Some(old) = source.edge_id_at(index) {
276 if let Some(interval) = other.edge_intervals.get(&old) {
277 self.edge_intervals.insert(id, *interval);
278 }
279 if let Some(name) = other.edge_names.get(&old) {
280 self.edge_names.insert(id, name.clone());
281 }
282 }
283 edges.push(id);
284 }
285 let mut loops = Vec::with_capacity(source.loops().len());
286 for (index, wire) in source.loops().iter().enumerate() {
287 let id = self.topology.add_loop(Loop {
288 edges: wire
289 .edges
290 .iter()
291 .map(|use_| EdgeUse {
292 edge: edges[use_.edge.index()],
293 orientation: use_.orientation,
294 pcurve: use_.pcurve.map(|curve| curves2[curve.index()]),
295 })
296 .collect(),
297 });
298 if let Some(old) = source.loop_id_at(index) {
299 for use_index in 0..wire.edges.len() {
300 if let Some(interval) = other.pcurve_intervals.get(&(old, use_index)) {
301 self.pcurve_intervals.insert((id, use_index), *interval);
302 }
303 }
304 }
305 loops.push(id);
306 }
307 let mut faces = Vec::with_capacity(source.faces().len());
308 for (index, face) in source.faces().iter().enumerate() {
309 let id = self.topology.add_face(Face {
310 surface: face.surface.map(|surface| surfaces[surface.index()]),
311 bounds: face
312 .bounds
313 .iter()
314 .map(|bound| axiolid_topology::FaceBound {
315 loop_id: loops[bound.loop_id.index()],
316 ..*bound
317 })
318 .collect(),
319 orientation: face.orientation,
320 });
321 if let Some(name) = source
322 .face_id_at(index)
323 .and_then(|old| other.face_names.get(&old))
324 {
325 self.face_names.insert(id, name.clone());
326 }
327 faces.push(id);
328 }
329 source
330 .shells()
331 .iter()
332 .map(|shell| {
333 self.topology.add_shell(Shell {
334 faces: shell
335 .faces
336 .iter()
337 .map(|&(face, sense)| {
338 let sense = match (sense, reverse) {
339 (sense, false) => sense,
340 (Orientation::Forward, true) => Orientation::Reversed,
341 (Orientation::Reversed, true) => Orientation::Forward,
342 };
343 (faces[face.index()], sense)
344 })
345 .collect(),
346 closed: shell.closed,
347 })
348 })
349 .collect()
350 }
351
352 pub fn finish(self) -> Result<ExactBRep, ExactBRepError> {
353 validate(&self)?;
354 Ok(ExactBRep {
355 topology: self.topology,
356 curves3: self.curves3,
357 curves2: self.curves2,
358 surfaces: self.surfaces,
359 edge_intervals: self.edge_intervals,
360 pcurve_intervals: self.pcurve_intervals,
361 face_names: self.face_names,
362 edge_names: self.edge_names,
363 })
364 }
365}
366
367#[non_exhaustive]
369#[derive(Debug, Clone, PartialEq, Eq)]
370pub enum ExactBRepError {
371 Empty,
373 Topology(BRepHealth),
375 MissingEdgeCurve { edge_index: usize },
377 UnknownCurve3 { edge_index: usize },
379 MissingEdgeInterval { edge_index: usize },
381 InvalidEdgeInterval { edge_index: usize },
383 MissingPcurve { loop_index: usize, use_index: usize },
385 UnknownCurve2 { loop_index: usize, use_index: usize },
387 MissingPcurveInterval { loop_index: usize, use_index: usize },
389 InvalidPcurveInterval { loop_index: usize, use_index: usize },
391 MissingFaceSurface { face_index: usize },
393 UnknownSurface { face_index: usize },
395}
396
397impl fmt::Display for ExactBRepError {
398 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
399 match self {
400 Self::Empty => f.write_str("exact B-rep requires at least one face"),
401 Self::Topology(_) => f.write_str("exact B-rep topology is structurally invalid"),
402 Self::MissingEdgeCurve { edge_index } => {
403 write!(f, "edge {edge_index} lacks a 3D curve")
404 }
405 Self::UnknownCurve3 { edge_index } => {
406 write!(f, "edge {edge_index} has an unknown 3D curve")
407 }
408 Self::MissingEdgeInterval { edge_index } => {
409 write!(f, "edge {edge_index} lacks a curve interval")
410 }
411 Self::InvalidEdgeInterval { edge_index } => {
412 write!(f, "edge {edge_index} has an invalid curve interval")
413 }
414 Self::MissingPcurve {
415 loop_index,
416 use_index,
417 } => write!(f, "loop {loop_index} use {use_index} lacks a pcurve"),
418 Self::UnknownCurve2 {
419 loop_index,
420 use_index,
421 } => write!(f, "loop {loop_index} use {use_index} has an unknown pcurve"),
422 Self::MissingPcurveInterval {
423 loop_index,
424 use_index,
425 } => write!(
426 f,
427 "loop {loop_index} use {use_index} lacks a pcurve interval"
428 ),
429 Self::InvalidPcurveInterval {
430 loop_index,
431 use_index,
432 } => write!(
433 f,
434 "loop {loop_index} use {use_index} has an invalid pcurve interval"
435 ),
436 Self::MissingFaceSurface { face_index } => {
437 write!(f, "face {face_index} lacks a support surface")
438 }
439 Self::UnknownSurface { face_index } => {
440 write!(f, "face {face_index} has an unknown support surface")
441 }
442 }
443 }
444}
445
446impl std::error::Error for ExactBRepError {}
447
448fn validate(value: &ExactBRepBuilder) -> Result<(), ExactBRepError> {
449 if value.topology.faces().is_empty() {
450 return Err(ExactBRepError::Empty);
451 }
452 let health = audit_brep(&value.topology);
453 if !health.is_tessellable() {
454 return Err(ExactBRepError::Topology(health));
455 }
456 for (edge_index, edge) in value.topology.edges().iter().enumerate() {
457 let Some(curve) = edge.curve else {
458 return Err(ExactBRepError::MissingEdgeCurve { edge_index });
459 };
460 if curve.index() >= value.curves3.len() {
461 return Err(ExactBRepError::UnknownCurve3 { edge_index });
462 }
463 let Some(edge_id) = value.topology.edge_id_at(edge_index) else {
464 return Err(ExactBRepError::MissingEdgeInterval { edge_index });
465 };
466 let Some(interval) = value.edge_intervals.get(&edge_id) else {
467 return Err(ExactBRepError::MissingEdgeInterval { edge_index });
468 };
469 if !valid_interval(*interval) {
470 return Err(ExactBRepError::InvalidEdgeInterval { edge_index });
471 }
472 }
473 for (loop_index, loop_) in value.topology.loops().iter().enumerate() {
474 let Some(loop_id) = value.topology.loop_id_at(loop_index) else {
475 return Err(ExactBRepError::Topology(health));
476 };
477 for (use_index, use_) in loop_.edges.iter().enumerate() {
478 let Some(curve) = use_.pcurve else {
479 return Err(ExactBRepError::MissingPcurve {
480 loop_index,
481 use_index,
482 });
483 };
484 if curve.index() >= value.curves2.len() {
485 return Err(ExactBRepError::UnknownCurve2 {
486 loop_index,
487 use_index,
488 });
489 }
490 let Some(interval) = value.pcurve_intervals.get(&(loop_id, use_index)) else {
491 return Err(ExactBRepError::MissingPcurveInterval {
492 loop_index,
493 use_index,
494 });
495 };
496 if !valid_interval(*interval) {
497 return Err(ExactBRepError::InvalidPcurveInterval {
498 loop_index,
499 use_index,
500 });
501 }
502 }
503 }
504 for (face_index, face) in value.topology.faces().iter().enumerate() {
505 let Some(surface) = face.surface else {
506 return Err(ExactBRepError::MissingFaceSurface { face_index });
507 };
508 if surface.index() >= value.surfaces.len() {
509 return Err(ExactBRepError::UnknownSurface { face_index });
510 }
511 }
512 Ok(())
513}
514
515fn valid_interval(interval: Interval) -> bool {
516 interval.start.is_finite() && interval.end.is_finite() && interval.length() > 0.0
517}