Skip to main content

brep_kernel/feature_pipeline/
context_offer.rs

1//! Selection-context applicability — kernel-owned show/no-show for the app's
2//! selection context bar.
3//!
4//! Each feature module exposes `context_applicable(&SelectionProbe) -> bool`
5//! next to its `schema()`: the ONE per-feature answer to "does the current
6//! selection make creating me meaningful?". This module only AGGREGATES those
7//! functions (the `schema.rs` pattern) into [`FEATURE_APPLICABILITY`], keyed by
8//! the catalogue `type` string, and the assembly-constraint equivalents live as
9//! `applicable` on each [`super::assembly::constraints::ConstraintTypeDef`].
10//!
11//! The probe is a KIND-LEVEL summary — per-kind counts plus a few app-derived
12//! roll-up booleans (`all_component`, `all_sheet_metal`) that answer ownership
13//! questions the raw names would; never the names themselves. Predicates decide
14//! eligibility; the app still derives WHICH reference fields to pre-fill from
15//! the schema `selectionFilter`s. A feature whose references a selection can
16//! never satisfy (primitives, datum-driven splines) simply returns `false`.
17
18/// Kind-level summary of the user's current selection, handed to each
19/// feature's / constraint's `context_applicable` test.
20///
21/// `solids` counts REAL solids only — committed sketches present in the scene
22/// as solids, but their reference kind is `SKETCH`, so the app partitions them
23/// into `sketches` before probing (the context bar's established split).
24#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
25pub struct SelectionProbe {
26    pub solids: usize,
27    pub sketches: usize,
28    pub faces: usize,
29    pub edges: usize,
30    /// Selected construction PLANES / DATUM planes (their scene FRAME names, e.g.
31    /// `Datum:XY` or a `P` plane feature's id). A datum/plane can seat a sketch's
32    /// `sketchPlane` (the kernel resolves a frame name there), so Sketch offers on
33    /// a plane-only selection just like it does on a planar face.
34    pub planes: usize,
35    /// Selected vertices (position-keyed, no names — no feature offer keys on
36    /// them, but they disqualify component-only shapes via `all_component`).
37    pub vertices: usize,
38    /// Distinct owning ACOMP components across the selected named entities.
39    pub components: usize,
40    /// Every selected named entity is component-owned (and ≥1 is selected).
41    /// Feature creation is fenced on component geometry, so feature predicates
42    /// never see it; constraint predicates REQUIRE it (`mapping::resolve_element`
43    /// rejects non-component references).
44    pub all_component: bool,
45    /// Every selected named entity sits on a SHEET-METAL body (and ≥1 is
46    /// selected) — the app resolves each pick's owning solid and checks its
47    /// `SheetTree` marker. The sheet-metal EDIT features (SM Flange / Fillet /
48    /// Chamfer) require it: they mutate an existing sheet-metal body, so they
49    /// are meaningless on plain geometry.
50    pub all_sheet_metal: bool,
51}
52
53impl SelectionProbe {
54    /// A profile source is selected (the extrude/revolve/sweep family's
55    /// `["SKETCH","FACE"]` fields).
56    pub fn has_profile(&self) -> bool {
57        self.faces > 0 || self.sketches > 0
58    }
59}
60
61/// Every catalogue feature's applicability predicate, catalogue order, keyed by
62/// the schema `type` string ([`super::schema::feature_schema_catalogue`] —
63/// `sheet_metal_corner` contributes two entries from one module). The paired
64/// test keeps this 1:1 with the catalogue.
65pub static FEATURE_APPLICABILITY: &[(&str, fn(&SelectionProbe) -> bool)] = &[
66    ("D", super::features::datum::context_applicable),
67    ("P", super::features::plane::context_applicable),
68    ("P.CU", super::features::cube::context_applicable),
69    ("P.CY", super::features::cylinder::context_applicable),
70    ("P.CO", super::features::cone::context_applicable),
71    ("P.S", super::features::sphere::context_applicable),
72    ("P.T", super::features::torus::context_applicable),
73    ("P.PY", super::features::pyramid::context_applicable),
74    ("IMPORT3D", super::features::import3d::context_applicable),
75    ("S", super::features::sketch::context_applicable),
76    ("SP", super::features::spline::context_applicable),
77    ("PORT", super::features::port::context_applicable),
78    ("HX", super::features::helix::context_applicable),
79    ("E", super::features::extrude::context_applicable),
80    ("B", super::features::boolean::context_applicable),
81    ("F", super::features::fillet::context_applicable),
82    ("CH", super::features::chamfer::context_applicable),
83    ("O.S", super::features::offset_shell::context_applicable),
84    ("O.F", super::features::offset_face::context_applicable),
85    ("PF", super::features::push_face::context_applicable),
86    ("DF", super::features::delete_face::context_applicable),
87    ("THK", super::features::thicken::context_applicable),
88    ("SM.TAB", super::features::sheet_metal_tab::context_applicable),
89    ("SM.CF", super::features::sheet_metal_contour_flange::context_applicable),
90    ("SM.F", super::features::sheet_metal_flange::context_applicable),
91    ("SM.HEM", super::features::sheet_metal_hem::context_applicable),
92    ("SM.FILLET", super::features::sheet_metal_corner::context_applicable),
93    ("SM.CHAMFER", super::features::sheet_metal_corner::context_applicable),
94    ("SM.CUTOUT", super::features::sheet_metal_cutout::context_applicable),
95    ("LOFT", super::features::loft::context_applicable),
96    ("M", super::features::mirror::context_applicable),
97    ("SPL", super::features::split::context_applicable),
98    ("R", super::features::revolve::context_applicable),
99    ("RIB", super::features::rib::context_applicable),
100    ("SW", super::features::sweep::context_applicable),
101    ("SWP", super::features::path_sweep::context_applicable),
102    ("H", super::features::hole::context_applicable),
103    ("TU", super::features::tube::context_applicable),
104    ("XFORM", super::features::transform::context_applicable),
105    ("PATTERN", super::features::pattern::context_applicable),
106    ("ACOMP", super::features::assembly_component::context_applicable),
107];
108
109/// Run feature `type_id`'s applicability predicate. Unknown type → `false`
110/// (nothing is offered for a feature the registry does not know).
111pub fn feature_context_applicable(type_id: &str, probe: &SelectionProbe) -> bool {
112    FEATURE_APPLICABILITY
113        .iter()
114        .find(|(id, _)| *id == type_id)
115        .is_some_and(|(_, applicable)| applicable(probe))
116}
117
118// BREP private tests: 7a82a24044e53956