Skip to main content

brep_kernel/feature_pipeline/sheet_metal/
mod.rs

1//! Exact-BREP sheet metal.
2//!
3//! Sheet metal is modeled as a parametric [`tree::SheetTree`] of flats + bends and
4//! turned into exact-BREP geometry by a single fold-parametric [`evaluate`]: the
5//! folded 3D part at `fold = 1.0`, and the flat pattern (the "unfold") at
6//! `fold = 0.0`. Everything is built from existing kernel ops — no triangle mesh.
7//!
8//! The five sheet-metal features (SM.TAB / SM.F / SM.CF / SM.HEM / SM.CUTOUT)
9//! author or mutate the tree; UNFOLD re-evaluates it flat. Those feature entry
10//! points land on top of this evaluator in later slices.
11
12pub mod evaluate;
13pub mod flat_pattern;
14pub mod tree;
15
16pub use evaluate::evaluate;
17pub use tree::{Bend, Edge, Flat, Hole, HoleBend, HoleBendKind, SheetTree};
18
19use std::cell::RefCell;
20use std::collections::HashMap;
21
22thread_local! {
23    /// The sheet-metal tree carried by each resident sheet-metal solid, keyed by
24    /// its registry handle — the Rust analogue of the `userData.sheetMetalModel
25    /// .tree`. A chaining feature (SM.F / SM.HEM / SM.CUTOUT) resolves the prior
26    /// body's handle via the scene-map, reads its tree here, mutates it, and stores
27    /// the updated tree under the new solid's handle. Tree lifetime follows handle
28    /// lifetime: the history cache removes a tree exactly when it frees its handle
29    /// (`remove_tree`), so a clean cached body keeps its tree across runs.
30    static SHEET_TREES: RefCell<HashMap<u32, SheetTree>> = RefCell::new(HashMap::new());
31}
32
33/// Attach `tree` to the resident solid `handle`.
34pub fn put_tree(handle: u32, tree: SheetTree) {
35    SHEET_TREES.with(|trees| {
36        trees.borrow_mut().insert(handle, tree);
37    });
38}
39
40/// The sheet-metal tree of the resident solid `handle`, if it carries one.
41pub fn get_tree(handle: u32) -> Option<SheetTree> {
42    SHEET_TREES.with(|trees| trees.borrow().get(&handle).cloned())
43}
44
45/// Drop the tree carried by `handle`. Tree lifetime is tied to handle lifetime:
46/// the history cache calls this exactly where it frees the handle, so a clean
47/// (cached) sheet-metal body keeps its tree across runs and an invalidated one
48/// loses it with its solid.
49pub fn remove_tree(handle: u32) {
50    SHEET_TREES.with(|trees| {
51        trees.borrow_mut().remove(&handle);
52    });
53}
54
55/// Drop every stored tree — the full-reset path (`clear_history_cache`, e.g. on a
56/// part/document switch).
57pub fn clear_trees() {
58    SHEET_TREES.with(|trees| trees.borrow_mut().clear());
59}
60
61/// Whether the resident solid `handle` carries a sheet-metal tree — the
62/// SheetTree-free predicate the export lane filters resident solids with (so the
63/// native engine picks the flat-pattern target without touching `SheetTree`).
64pub fn is_sheet_metal_handle(handle: u32) -> bool {
65    SHEET_TREES.with(|trees| trees.borrow().contains_key(&handle))
66}
67
68/// Export the flat pattern of the sheet-metal body `handle` as DXF (R12 ASCII).
69/// Runs the unfold TRANSIENTLY off the resident tree — no feature, no history
70/// mutation. Errors if the handle carries no sheet-metal tree.
71pub fn flat_pattern_dxf(handle: u32) -> Result<String, String> {
72    let tree = get_tree(handle)
73        .ok_or_else(|| "flat pattern: handle is not a sheet-metal body".to_string())?;
74    Ok(flat_pattern::to_dxf(&flat_pattern::build(&tree)?))
75}
76
77/// Export the flat pattern of the sheet-metal body `handle` as SVG — the DXF
78/// sibling of [`flat_pattern_dxf`].
79pub fn flat_pattern_svg(handle: u32) -> Result<String, String> {
80    let tree = get_tree(handle)
81        .ok_or_else(|| "flat pattern: handle is not a sheet-metal body".to_string())?;
82    Ok(flat_pattern::to_svg(&flat_pattern::build(&tree)?))
83}