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