From a1577d7fd2ffd6d090ba647de5bdc3626868dfcd Mon Sep 17 00:00:00 2001 From: Lewis Date: Thu, 28 May 2026 09:22:27 +0300 Subject: [PATCH] feat(kernel): brep arena builder, truck assembly, deps Lewis: May this revision serve well! --- Cargo.lock | 342 +++++++++++++++++- crates/bone-kernel/Cargo.toml | 2 + crates/bone-kernel/src/brep/build.rs | 297 +++++++++++++++ ..._unit_cube_iteration_is_label_ordered.snap | 35 ++ crates/bone-kernel/src/lib.rs | 4 + crates/bone-kernel/tests/surfaces3.rs | 4 +- 6 files changed, 671 insertions(+), 13 deletions(-) create mode 100644 crates/bone-kernel/src/brep/build.rs create mode 100644 crates/bone-kernel/src/brep/snapshots/bone_kernel__brep__tests__unit_cube_iteration_is_label_ordered.snap diff --git a/Cargo.lock b/Cargo.lock index 1a89a25..87b812b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "approx" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" +dependencies = [ + "num-traits", +] + [[package]] name = "approx" version = "0.5.1" @@ -110,6 +119,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "array-macro" +version = "2.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "220a2c618ab466efe41d0eace94dfeff1c35e3aa47891bdb95e1c0fefffd3c99" + [[package]] name = "arrayref" version = "0.3.9" @@ -418,7 +433,9 @@ version = "0.0.0" dependencies = [ "bone-types", "insta", + "slotmap", "thiserror 2.0.18", + "truck-modeling", "uom", ] @@ -499,6 +516,15 @@ dependencies = [ "uom", ] +[[package]] +name = "branches" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11502672c5570f77f6bdf573332483f8475bab6a7fda00f1fae8ddb5a6245c0" +dependencies = [ + "rustc_version", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -593,6 +619,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "cgmath" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" +dependencies = [ + "approx 0.4.0", + "num-traits", + "serde", +] + [[package]] name = "codespan-reporting" version = "0.13.1" @@ -640,6 +677,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation" version = "0.9.4" @@ -707,6 +750,25 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -731,6 +793,19 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "930c7171c8df9fb1782bdf9b918ed9ed2d33d1d22300abb754f9085bc48bf8e8" +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -804,6 +879,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d926b4d407d372f141f93bb444696142c29d32962ccbd3531117cf3aa0bfa9" +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + [[package]] name = "encode_unicode" version = "1.0.0" @@ -1263,6 +1344,19 @@ dependencies = [ "windows-link", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -1609,6 +1703,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -1695,6 +1798,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "katexit" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccfb0b7ce7938f84a5ecbdca5d0a991e46bc9d6d078934ad5e92c5270fe547db" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "khronos-egl" version = "6.0.0" @@ -1844,6 +1958,17 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matext4cgmath" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6541e181de37f70f0aceb25441823a3d0efa9cc1d23f475a0d3926678949178" +dependencies = [ + "cgmath", + "katexit", + "num-complex", +] + [[package]] name = "matrixmultiply" version = "0.3.10" @@ -1908,7 +2033,7 @@ dependencies = [ "log", "num-traits", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", "spirv", "thiserror 2.0.18", "unicode-ident", @@ -1920,7 +2045,7 @@ version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d43ddcacf343185dfd6de2ee786d9e8b1c2301622afab66b6c73baf9882abfd" dependencies = [ - "approx", + "approx 0.5.1", "matrixmultiply", "num-complex", "num-rational", @@ -2429,7 +2554,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" dependencies = [ - "approx", + "approx 0.5.1", "fast-srgb8", "palette_derive", ] @@ -2674,6 +2799,30 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -2697,8 +2846,8 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bitflags 2.11.1", "num-traits", - "rand", - "rand_chacha", + "rand 0.9.4", + "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", "unarray", @@ -2769,14 +2918,35 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + [[package]] name = "rand" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "rand_chacha", - "rand_core", + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", ] [[package]] @@ -2786,7 +2956,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", ] [[package]] @@ -2804,7 +2983,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -2846,6 +3025,35 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rclite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e09665c494a9de2bd230e1aedc0c2fb20481a51f28f7db84d0c9bcfe2fe537b2" +dependencies = [ + "branches", +] + [[package]] name = "read-fonts" version = "0.37.0" @@ -2943,6 +3151,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + [[package]] name = "rustc_version" version = "0.4.1" @@ -3124,7 +3338,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95" dependencies = [ - "approx", + "approx 0.5.1", "num-complex", "num-traits", "paste", @@ -3339,7 +3553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -3530,6 +3744,104 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "truck-base" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c279de9e92e5dc20a188deb0bb9a4bd421a6a185e57e03e19025e84a36c5a05" +dependencies = [ + "cgmath", + "matext4cgmath", + "rustc-hash 2.1.2", + "serde", +] + +[[package]] +name = "truck-derivers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421ff1c5a303aed64e295d9b30e8424ee7a2fa251617a59ae4acfd078496009f" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "truck-geometry" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d9d85f203fdbc2d206a38b8e7c4417a05a2eb589be8b81ecfe9f98faafc425d" +dependencies = [ + "serde", + "thiserror 1.0.69", + "truck-base", + "truck-geotrait", +] + +[[package]] +name = "truck-geotrait" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a31fe98d5a0589ad20576260e214af23dea21a8d125da81ad3bbcb666f411f" +dependencies = [ + "getrandom 0.2.17", + "rand 0.8.6", + "thiserror 1.0.69", + "truck-base", + "truck-derivers", +] + +[[package]] +name = "truck-modeling" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8082c2dfac8c2732a014d81807fb5d24dbac0d72ee4de2a705b7fbc05fa05b18" +dependencies = [ + "derive_more", + "rustc-hash 2.1.2", + "serde", + "thiserror 1.0.69", + "truck-base", + "truck-geometry", + "truck-geotrait", + "truck-polymesh", + "truck-topology", +] + +[[package]] +name = "truck-polymesh" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f22def310af4a89c37beb4839bbf702b302d2c112ea29553759f0e16622fbf" +dependencies = [ + "array-macro", + "bytemuck", + "itertools", + "rustc-hash 2.1.2", + "serde", + "thiserror 1.0.69", + "truck-base", + "truck-geotrait", +] + +[[package]] +name = "truck-topology" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf62a1610405c5e39ab5eb6948f326fc1641689734a6b8207a80792133fe0e78" +dependencies = [ + "parking_lot", + "rayon", + "rclite", + "rustc-hash 2.1.2", + "serde", + "thiserror 1.0.69", + "truck-base", + "truck-geotrait", +] + [[package]] name = "ttf-parser" version = "0.25.1" @@ -3638,6 +3950,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.3+wasi-0.2.9" @@ -3926,7 +4244,7 @@ dependencies = [ "portable-atomic", "profiling", "raw-window-handle", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror 2.0.18", "wgpu-core-deps-apple", diff --git a/crates/bone-kernel/Cargo.toml b/crates/bone-kernel/Cargo.toml index 4635d86..cb6d0a1 100644 --- a/crates/bone-kernel/Cargo.toml +++ b/crates/bone-kernel/Cargo.toml @@ -7,7 +7,9 @@ rust-version.workspace = true [dependencies] bone-types = { workspace = true } +slotmap = { workspace = true } thiserror = { workspace = true } +truck-modeling = "=0.6.0" uom = { workspace = true } [dev-dependencies] diff --git a/crates/bone-kernel/src/brep/build.rs b/crates/bone-kernel/src/brep/build.rs new file mode 100644 index 0000000..22d6057 --- /dev/null +++ b/crates/bone-kernel/src/brep/build.rs @@ -0,0 +1,297 @@ +#![allow( + dead_code, + reason = "facade construction seam exercised only by tests until the extrude evaluator wires the production caller" +)] + +use std::collections::HashMap; +use std::collections::hash_map::Entry; + +use bone_types::{ + BrepEdgeId, BrepFaceId, BrepLoopId, BrepShellId, BrepVertexId, EdgeLabel, FaceLabel, + VertexLabel, +}; +use slotmap::{Key, SlotMap}; +use truck_modeling::{ + BoundedCurve, Edge, EdgeID, FaceID, ParameterDivision1D, Shell, Solid, VertexID, +}; + +use super::{ + BrepEdge, BrepError, BrepFace, BrepLoop, BrepShell, BrepSolid, BrepVertex, LabelKind, TruckGap, +}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) struct BoundaryIndex(usize); + +#[derive(Clone, Copy, Debug)] +pub(crate) struct EdgeArenaHandle(usize); + +#[derive(Clone)] +pub(crate) struct Arena { + solid: Solid, + edges: Vec, +} + +impl Arena { + pub(crate) fn boundary(&self, index: BoundaryIndex) -> &Shell { + &self.solid.boundaries()[index.0] + } + + pub(crate) fn edge(&self, handle: EdgeArenaHandle) -> &Edge { + &self.edges[handle.0] + } +} + +const LENGTH_DIVISION_TOLERANCE: f64 = 1.0e-4; + +pub(crate) fn edge_length(edge: &Edge) -> f64 { + let curve = edge.curve(); + let (_, points) = curve.parameter_division(curve.range_tuple(), LENGTH_DIVISION_TOLERANCE); + points + .windows(2) + .map(|pair| { + let span = pair[1] - pair[0]; + span.x.hypot(span.y).hypot(span.z) + }) + .sum() +} + +pub(crate) struct SolidLabeling { + pub(crate) faces: HashMap, + pub(crate) edges: HashMap, + pub(crate) vertices: HashMap, +} + +struct VertexTable { + vertices: SlotMap, + by_truck: HashMap, +} + +struct EdgeTable { + edges: SlotMap, + by_truck: HashMap, + arena: Vec, +} + +struct FaceDescription { + label: FaceLabel, + loops: Vec>, +} + +struct ShellDescription { + boundary: BoundaryIndex, + faces: Vec, +} + +pub(crate) fn assemble(solid: Solid, labeling: &SolidLabeling) -> Result { + let VertexTable { + vertices, + by_truck: vertex_dedup, + } = build_vertices(&solid, labeling)?; + let EdgeTable { + edges, + by_truck: edge_dedup, + arena: arena_edges, + } = build_edges(&solid, labeling, &vertex_dedup)?; + let described = describe_shells(&solid, labeling, &edge_dedup)?; + let (shells, faces, loops) = mint_topology(described); + + let shell_order = ordered_by(&shells, |shell| shell.boundary_index); + let face_order = ordered_by_label(&faces, BrepFace::label, LabelKind::Face)?; + let edge_order = ordered_by_label(&edges, BrepEdge::label, LabelKind::Edge)?; + let vertex_order = ordered_by_label(&vertices, BrepVertex::label, LabelKind::Vertex)?; + + Ok(BrepSolid { + arena: Arena { + solid, + edges: arena_edges, + }, + shells, + faces, + loops, + edges, + vertices, + shell_order, + face_order, + edge_order, + vertex_order, + }) +} + +fn ordered_by(map: &SlotMap, key: impl Fn(&V) -> T) -> Vec { + let mut keys: Vec = map.keys().collect(); + keys.sort_by_cached_key(|k| key(&map[*k])); + keys +} + +fn ordered_by_label( + map: &SlotMap, + label: impl Fn(&V) -> T, + kind: LabelKind, +) -> Result, BrepError> { + let order = ordered_by(map, &label); + let unique = order + .windows(2) + .all(|pair| label(&map[pair[0]]) != label(&map[pair[1]])); + if unique { + Ok(order) + } else { + Err(BrepError::DuplicateLabel { kind }) + } +} + +fn build_vertices(solid: &Solid, labeling: &SolidLabeling) -> Result { + solid.vertex_iter().try_fold( + VertexTable { + vertices: SlotMap::with_key(), + by_truck: HashMap::new(), + }, + |mut table, vertex| { + if let Entry::Vacant(slot) = table.by_truck.entry(vertex.id()) { + let label = + *labeling + .vertices + .get(&vertex.id()) + .ok_or(BrepError::TruckUnsupported { + detail: TruckGap::Unlabeled(LabelKind::Vertex), + })?; + let id = table + .vertices + .insert_with_key(|id| BrepVertex { id, label }); + slot.insert(id); + } + Ok(table) + }, + ) +} + +fn build_edges( + solid: &Solid, + labeling: &SolidLabeling, + vertex_dedup: &HashMap, +) -> Result { + solid.edge_iter().try_fold( + EdgeTable { + edges: SlotMap::with_key(), + by_truck: HashMap::new(), + arena: Vec::new(), + }, + |mut table, edge| { + if let Entry::Vacant(slot) = table.by_truck.entry(edge.id()) { + let label = *labeling + .edges + .get(&edge.id()) + .ok_or(BrepError::TruckUnsupported { + detail: TruckGap::Unlabeled(LabelKind::Edge), + })?; + let front = + *vertex_dedup + .get(&edge.front().id()) + .ok_or(BrepError::TruckUnsupported { + detail: TruckGap::Unlabeled(LabelKind::Vertex), + })?; + let back = + *vertex_dedup + .get(&edge.back().id()) + .ok_or(BrepError::TruckUnsupported { + detail: TruckGap::Unlabeled(LabelKind::Vertex), + })?; + let handle = EdgeArenaHandle(table.arena.len()); + table.arena.push(edge.clone()); + let id = table.edges.insert_with_key(|id| BrepEdge { + id, + label, + handle, + vertices: [front, back], + }); + slot.insert(id); + } + Ok(table) + }, + ) +} + +fn describe_shells( + solid: &Solid, + labeling: &SolidLabeling, + edge_dedup: &HashMap, +) -> Result, BrepError> { + solid + .boundaries() + .iter() + .enumerate() + .map(|(boundary_index, shell)| { + let faces = shell + .face_iter() + .map(|face| { + let label = + *labeling + .faces + .get(&face.id()) + .ok_or(BrepError::TruckUnsupported { + detail: TruckGap::Unlabeled(LabelKind::Face), + })?; + let loops = face + .boundaries() + .iter() + .map(|wire| { + wire.edge_iter() + .map(|edge| { + edge_dedup.get(&edge.id()).copied().ok_or( + BrepError::TruckUnsupported { + detail: TruckGap::Unlabeled(LabelKind::Edge), + }, + ) + }) + .collect::, _>>() + }) + .collect::, _>>()?; + Ok(FaceDescription { label, loops }) + }) + .collect::, _>>()?; + Ok(ShellDescription { + boundary: BoundaryIndex(boundary_index), + faces, + }) + }) + .collect() +} + +fn mint_topology( + described: Vec, +) -> ( + SlotMap, + SlotMap, + SlotMap, +) { + described.into_iter().fold( + ( + SlotMap::with_key(), + SlotMap::with_key(), + SlotMap::with_key(), + ), + |(mut shells, mut faces, mut loops), shell_desc| { + let face_ids = shell_desc + .faces + .into_iter() + .map(|face_desc| { + let loop_ids = face_desc + .loops + .into_iter() + .map(|edges| loops.insert_with_key(|id| BrepLoop { id, edges })) + .collect(); + faces.insert_with_key(|id| BrepFace { + id, + label: face_desc.label, + loops: loop_ids, + }) + }) + .collect(); + shells.insert_with_key(|id| BrepShell { + id, + boundary_index: shell_desc.boundary, + faces: face_ids, + }); + (shells, faces, loops) + }, + ) +} diff --git a/crates/bone-kernel/src/brep/snapshots/bone_kernel__brep__tests__unit_cube_iteration_is_label_ordered.snap b/crates/bone-kernel/src/brep/snapshots/bone_kernel__brep__tests__unit_cube_iteration_is_label_ordered.snap new file mode 100644 index 0000000..034d419 --- /dev/null +++ b/crates/bone-kernel/src/brep/snapshots/bone_kernel__brep__tests__unit_cube_iteration_is_label_ordered.snap @@ -0,0 +1,35 @@ +--- +source: crates/bone-kernel/src/brep/mod.rs +expression: "format!(\"shells:\\n{shells}\\nfaces:\\n{faces}\\nedges:\\n{edges}\\nvertices:\\n{vertices}\")" +--- +shells: + shell faces=6 +faces: + face[FeatureId(1v1)]:start_cap + face[FeatureId(1v1)]:side(loop#0, from=SketchEntityId(1v1)) + face[FeatureId(1v1)]:side(loop#0, from=SketchEntityId(2v1)) + face[FeatureId(1v1)]:side(loop#0, from=SketchEntityId(3v1)) + face[FeatureId(1v1)]:side(loop#0, from=SketchEntityId(4v1)) + face[FeatureId(1v1)]:end_cap +edges: + edge[FeatureId(1v1)]:start_cap_edge(from=SketchEntityId(1v1)) + edge[FeatureId(1v1)]:start_cap_edge(from=SketchEntityId(2v1)) + edge[FeatureId(1v1)]:start_cap_edge(from=SketchEntityId(3v1)) + edge[FeatureId(1v1)]:start_cap_edge(from=SketchEntityId(4v1)) + edge[FeatureId(1v1)]:side_edge(corner, from=SketchEntityId(5v1)) + edge[FeatureId(1v1)]:side_edge(corner, from=SketchEntityId(6v1)) + edge[FeatureId(1v1)]:side_edge(corner, from=SketchEntityId(7v1)) + edge[FeatureId(1v1)]:side_edge(corner, from=SketchEntityId(8v1)) + edge[FeatureId(1v1)]:end_cap_edge(from=SketchEntityId(1v1)) + edge[FeatureId(1v1)]:end_cap_edge(from=SketchEntityId(2v1)) + edge[FeatureId(1v1)]:end_cap_edge(from=SketchEntityId(3v1)) + edge[FeatureId(1v1)]:end_cap_edge(from=SketchEntityId(4v1)) +vertices: + vertex[FeatureId(1v1)]:start_cap_vertex(corner, from=SketchEntityId(5v1)) + vertex[FeatureId(1v1)]:start_cap_vertex(corner, from=SketchEntityId(6v1)) + vertex[FeatureId(1v1)]:start_cap_vertex(corner, from=SketchEntityId(7v1)) + vertex[FeatureId(1v1)]:start_cap_vertex(corner, from=SketchEntityId(8v1)) + vertex[FeatureId(1v1)]:end_cap_vertex(corner, from=SketchEntityId(5v1)) + vertex[FeatureId(1v1)]:end_cap_vertex(corner, from=SketchEntityId(6v1)) + vertex[FeatureId(1v1)]:end_cap_vertex(corner, from=SketchEntityId(7v1)) + vertex[FeatureId(1v1)]:end_cap_vertex(corner, from=SketchEntityId(8v1)) diff --git a/crates/bone-kernel/src/lib.rs b/crates/bone-kernel/src/lib.rs index 1238904..e5c840b 100644 --- a/crates/bone-kernel/src/lib.rs +++ b/crates/bone-kernel/src/lib.rs @@ -2,6 +2,7 @@ pub mod aabb; mod angles; pub mod arc2; pub mod arc3; +pub mod brep; pub mod circle2; pub mod circle3; mod circular3; @@ -22,6 +23,9 @@ pub mod surface3; pub use aabb::Aabb2; pub use arc2::{Arc2, arc_bounding_box}; pub use arc3::Arc3; +pub use brep::{ + BrepEdge, BrepError, BrepFace, BrepLoop, BrepShell, BrepSolid, BrepVertex, LabelKind, TruckGap, +}; pub use circle2::Circle2; pub use circle3::Circle3; pub use closest::{ClosestPoint, ClosestPoint2, ClosestPoint3}; diff --git a/crates/bone-kernel/tests/surfaces3.rs b/crates/bone-kernel/tests/surfaces3.rs index a431816..f8647a5 100644 --- a/crates/bone-kernel/tests/surfaces3.rs +++ b/crates/bone-kernel/tests/surfaces3.rs @@ -210,7 +210,9 @@ fn cylinder_surface_rejects_non_finite() { assert!(CylinderSurface::new(plane, mm(f64::NAN), rad(0.0), rad(PI), mm(1.0), TOL).is_err()); assert!(CylinderSurface::new(plane, mm(1.0), rad(0.0), rad(PI), mm(f64::NAN), TOL).is_err()); assert!(CylinderSurface::new(plane, mm(1.0), rad(0.0), rad(f64::NAN), mm(1.0), TOL).is_err()); - assert!(CylinderSurface::new(plane, mm(1.0), rad(0.0), rad(f64::INFINITY), mm(1.0), TOL).is_err()); + assert!( + CylinderSurface::new(plane, mm(1.0), rad(0.0), rad(f64::INFINITY), mm(1.0), TOL).is_err() + ); } #[test] -- 2.51.2