Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(kernel): brep facade error... stuff

Lewis: May this revision serve well! <lu5a@proton.me>

author
Lewis
date (May 28, 2026, 9:22 AM +0300) commit 378abee7 parent a1577d7f change-id pqnttnqz
+91 -46
+29 -27
crates/bone-kernel/src/brep/build.rs
··· 7 7 use std::collections::hash_map::Entry; 8 8 9 9 use bone_types::{ 10 - BrepEdgeId, BrepFaceId, BrepLoopId, BrepShellId, BrepVertexId, EdgeLabel, FaceLabel, 10 + BrepEdgeId, BrepFaceId, BrepLoopId, BrepShellId, BrepVertexId, EdgeLabel, FaceLabel, Tolerance, 11 11 VertexLabel, 12 12 }; 13 13 use slotmap::{Key, SlotMap}; ··· 15 15 BoundedCurve, Edge, EdgeID, FaceID, ParameterDivision1D, Shell, Solid, VertexID, 16 16 }; 17 17 18 - use super::{ 19 - BrepEdge, BrepError, BrepFace, BrepLoop, BrepShell, BrepSolid, BrepVertex, LabelKind, TruckGap, 20 - }; 18 + use super::{BrepEdge, BrepError, BrepFace, BrepLoop, BrepShell, BrepSolid, BrepVertex, LabelKind}; 21 19 22 20 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 23 21 pub(crate) struct BoundaryIndex(usize); ··· 41 39 } 42 40 } 43 41 44 - const LENGTH_DIVISION_TOLERANCE: f64 = 1.0e-4; 42 + const LENGTH_DIVISION_TOLERANCE: Tolerance = Tolerance::new(1.0e-4); 45 43 46 44 pub(crate) fn edge_length(edge: &Edge) -> f64 { 47 45 let curve = edge.curve(); 48 - let (_, points) = curve.parameter_division(curve.range_tuple(), LENGTH_DIVISION_TOLERANCE); 46 + let (_, points) = 47 + curve.parameter_division(curve.range_tuple(), LENGTH_DIVISION_TOLERANCE.value()); 49 48 points 50 49 .windows(2) 51 50 .map(|pair| { ··· 99 98 let face_order = ordered_by_label(&faces, BrepFace::label, LabelKind::Face)?; 100 99 let edge_order = ordered_by_label(&edges, BrepEdge::label, LabelKind::Edge)?; 101 100 let vertex_order = ordered_by_label(&vertices, BrepVertex::label, LabelKind::Vertex)?; 101 + let loop_order: Vec<BrepLoopId> = face_order 102 + .iter() 103 + .flat_map(|face_id| faces[*face_id].loops.iter().copied()) 104 + .collect(); 102 105 103 106 Ok(BrepSolid { 104 107 arena: Arena { ··· 112 115 vertices, 113 116 shell_order, 114 117 face_order, 118 + loop_order, 115 119 edge_order, 116 120 vertex_order, 117 121 }) ··· 151 155 *labeling 152 156 .vertices 153 157 .get(&vertex.id()) 154 - .ok_or(BrepError::TruckUnsupported { 155 - detail: TruckGap::Unlabeled(LabelKind::Vertex), 158 + .ok_or(BrepError::MissingLabel { 159 + kind: LabelKind::Vertex, 156 160 })?; 157 161 let id = table 158 162 .vertices ··· 180 184 let label = *labeling 181 185 .edges 182 186 .get(&edge.id()) 183 - .ok_or(BrepError::TruckUnsupported { 184 - detail: TruckGap::Unlabeled(LabelKind::Edge), 187 + .ok_or(BrepError::MissingLabel { 188 + kind: LabelKind::Edge, 185 189 })?; 186 190 let front = 187 191 *vertex_dedup 188 192 .get(&edge.front().id()) 189 - .ok_or(BrepError::TruckUnsupported { 190 - detail: TruckGap::Unlabeled(LabelKind::Vertex), 191 - })?; 192 - let back = 193 - *vertex_dedup 194 - .get(&edge.back().id()) 195 - .ok_or(BrepError::TruckUnsupported { 196 - detail: TruckGap::Unlabeled(LabelKind::Vertex), 193 + .ok_or(BrepError::MissingLabel { 194 + kind: LabelKind::Vertex, 197 195 })?; 196 + let back = *vertex_dedup 197 + .get(&edge.back().id()) 198 + .ok_or(BrepError::MissingLabel { 199 + kind: LabelKind::Vertex, 200 + })?; 198 201 let handle = EdgeArenaHandle(table.arena.len()); 199 202 table.arena.push(edge.clone()); 200 203 let id = table.edges.insert_with_key(|id| BrepEdge { ··· 223 226 let faces = shell 224 227 .face_iter() 225 228 .map(|face| { 226 - let label = 227 - *labeling 228 - .faces 229 - .get(&face.id()) 230 - .ok_or(BrepError::TruckUnsupported { 231 - detail: TruckGap::Unlabeled(LabelKind::Face), 232 - })?; 229 + let label = *labeling 230 + .faces 231 + .get(&face.id()) 232 + .ok_or(BrepError::MissingLabel { 233 + kind: LabelKind::Face, 234 + })?; 233 235 let loops = face 234 236 .boundaries() 235 237 .iter() ··· 237 239 wire.edge_iter() 238 240 .map(|edge| { 239 241 edge_dedup.get(&edge.id()).copied().ok_or( 240 - BrepError::TruckUnsupported { 241 - detail: TruckGap::Unlabeled(LabelKind::Edge), 242 + BrepError::MissingLabel { 243 + kind: LabelKind::Edge, 242 244 }, 243 245 ) 244 246 })
+60 -18
crates/bone-kernel/src/brep/mod.rs
··· 9 9 use build::{Arena, BoundaryIndex, EdgeArenaHandle, edge_length}; 10 10 11 11 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 12 - pub enum TruckGap { 13 - Unlabeled(LabelKind), 14 - } 12 + pub enum TruckGap {} 15 13 16 14 impl core::fmt::Display for TruckGap { 17 - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 18 - match self { 19 - Self::Unlabeled(kind) => write!(f, "{kind} carries no extrude label"), 20 - } 15 + fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 16 + match *self {} 21 17 } 22 18 } 23 19 ··· 38 34 } 39 35 } 40 36 37 + #[derive(Debug, Clone, Copy, PartialEq, Eq)] 38 + pub enum ProfileDefect { 39 + OpenLoop, 40 + SelfIntersectingLoop, 41 + ZeroArea, 42 + } 43 + 44 + impl core::fmt::Display for ProfileDefect { 45 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 46 + f.write_str(match self { 47 + Self::OpenLoop => "an open loop", 48 + Self::SelfIntersectingLoop => "a self-intersecting loop", 49 + Self::ZeroArea => "zero area", 50 + }) 51 + } 52 + } 53 + 41 54 #[derive(Debug, thiserror::Error)] 42 55 pub enum BrepError { 56 + #[error("extrude profile has {reason}")] 57 + InvalidProfile { reason: ProfileDefect }, 58 + #[error("extrude depth is zero")] 59 + EmptyExtrudeDepth, 60 + #[error("solid self-intersects")] 61 + SelfIntersecting { faces: Vec<BrepFaceId> }, 43 62 #[error("boundary shell is not a closed manifold")] 44 63 ShellNotClosed, 45 64 #[error("edge {edge:?} collapses below the validation tolerance")] 46 65 DegenerateEdge { edge: BrepEdgeId }, 47 66 #[error("kernel facade does not yet wrap this path: {detail}")] 48 67 TruckUnsupported { detail: TruckGap }, 68 + #[error("{kind} carries no extrude label")] 69 + MissingLabel { kind: LabelKind }, 49 70 #[error("duplicate {kind} label breaks per-element naming")] 50 71 DuplicateLabel { kind: LabelKind }, 51 72 } ··· 164 185 vertices: SlotMap<BrepVertexId, BrepVertex>, 165 186 shell_order: Vec<BrepShellId>, 166 187 face_order: Vec<BrepFaceId>, 188 + loop_order: Vec<BrepLoopId>, 167 189 edge_order: Vec<BrepEdgeId>, 168 190 vertex_order: Vec<BrepVertexId>, 169 191 } ··· 178 200 } 179 201 180 202 pub fn iter_loops(&self) -> impl Iterator<Item = &BrepLoop> { 181 - self.loops.values() 203 + self.loop_order.iter().map(|id| &self.loops[*id]) 182 204 } 183 205 184 206 pub fn iter_edges(&self) -> impl Iterator<Item = &BrepEdge> { ··· 215 237 #[cfg(test)] 216 238 mod tests { 217 239 use super::build::{SolidLabeling, assemble, edge_length}; 218 - use super::{BrepError, LabelKind, TruckGap}; 240 + use super::{BrepError, BrepLoop, LabelKind}; 219 241 use bone_types::{ 220 - EdgeLabel, EdgeRole, FaceLabel, FaceRole, FeatureId, LoopIndex, SideKind, SketchEntityId, 221 - Tolerance, VertexLabel, VertexRole, 242 + BrepLoopId, EdgeLabel, EdgeRole, FaceLabel, FaceRole, FeatureId, LoopIndex, SideKind, 243 + SketchEntityId, Tolerance, VertexLabel, VertexRole, 222 244 }; 223 245 use slotmap::SlotMap; 224 246 use std::collections::HashMap; ··· 441 463 } 442 464 443 465 #[test] 466 + fn loop_iteration_follows_face_order() { 467 + let profile = profile(); 468 + let solid = unit_cube(); 469 + let labeling = label_solid(&solid, &profile); 470 + let Ok(brep) = assemble(solid, &labeling) else { 471 + panic!("unit cube labels are complete"); 472 + }; 473 + let grouped_by_face: Vec<BrepLoopId> = brep 474 + .iter_faces() 475 + .flat_map(|face| face.loops().iter().copied()) 476 + .collect(); 477 + let iterated: Vec<BrepLoopId> = brep.iter_loops().map(BrepLoop::id).collect(); 478 + assert_eq!(iterated.len(), 6); 479 + assert_eq!(grouped_by_face, iterated); 480 + } 481 + 482 + #[test] 444 483 fn unit_cube_validates() { 445 484 let profile = profile(); 446 485 let solid = unit_cube(); ··· 473 512 labeling.faces.clear(); 474 513 assert!(matches!( 475 514 assemble(solid, &labeling), 476 - Err(BrepError::TruckUnsupported { 477 - detail: TruckGap::Unlabeled(LabelKind::Face) 515 + Err(BrepError::MissingLabel { 516 + kind: LabelKind::Face 478 517 }) 479 518 )); 480 519 } ··· 549 588 labeling.edges.clear(); 550 589 assert!(matches!( 551 590 assemble(solid, &labeling), 552 - Err(BrepError::TruckUnsupported { 553 - detail: TruckGap::Unlabeled(LabelKind::Edge) 591 + Err(BrepError::MissingLabel { 592 + kind: LabelKind::Edge 554 593 }) 555 594 )); 556 595 } ··· 563 602 labeling.vertices.clear(); 564 603 assert!(matches!( 565 604 assemble(solid, &labeling), 566 - Err(BrepError::TruckUnsupported { 567 - detail: TruckGap::Unlabeled(LabelKind::Vertex) 605 + Err(BrepError::MissingLabel { 606 + kind: LabelKind::Vertex 568 607 }) 569 608 )); 570 609 } ··· 577 616 let Some(shared) = labeling.edges.values().copied().next() else { 578 617 panic!("cube has edges"); 579 618 }; 580 - labeling.edges.values_mut().for_each(|label| *label = shared); 619 + labeling 620 + .edges 621 + .values_mut() 622 + .for_each(|label| *label = shared); 581 623 assert!(matches!( 582 624 assemble(solid, &labeling), 583 625 Err(BrepError::DuplicateLabel {
+2 -1
crates/bone-kernel/src/lib.rs
··· 24 24 pub use arc2::{Arc2, arc_bounding_box}; 25 25 pub use arc3::Arc3; 26 26 pub use brep::{ 27 - BrepEdge, BrepError, BrepFace, BrepLoop, BrepShell, BrepSolid, BrepVertex, LabelKind, TruckGap, 27 + BrepEdge, BrepError, BrepFace, BrepLoop, BrepShell, BrepSolid, BrepVertex, LabelKind, 28 + ProfileDefect, TruckGap, 28 29 }; 29 30 pub use circle2::Circle2; 30 31 pub use circle3::Circle3;