brep_render/engine_state.rs
1//! [`EngineState`] — the windowing-agnostic viewer state machine the host UI
2//! programs against (R3): scene + camera + controls + settings + emphasis, plus
3//! the whole event/command/query surface (run-history feed, pointer/wheel
4//! ingestion, camera commands, picking, world→screen, visibility). No GPU, no
5//! canvas — the wasm `Engine` and the winit desktop shell both wrap this; it is
6//! fully unit-testable on native.
7//!
8//! Everything crosses the R3 boundary as plain JSON/scalars: the host never holds a
9//! renderer object, only names, ids, and JSON.
10
11use crate::controls::ArcballControls;
12use crate::history::History;
13use crate::pick::{self, PickOptions};
14use crate::scene::RenderScene;
15use crate::style::{Emphasis, RenderSettings};
16use crate::view::ViewCamera;
17use crate::widgets::{gizmo_camera, WidgetOverlay, WidgetRegistry};
18use brep_kernel::HistoryRequest;
19use std::collections::HashMap;
20
21/// One DIALOG row's hover state — which panel lit it, the row's own TEXT, and
22/// what that text resolved to in the scene.
23///
24/// The row text is the memo key rather than the candidate's name because the two
25/// differ whenever the derived-face fallback fires (a fillet's `Edges` row reads
26/// `A|B[0]` while the face it lights is `F1:BLEND:A|B[0]` — see
27/// [`EngineState::hover_entity_by_name`]). An unresolvable row memoizes as
28/// `candidate: None`, so a held hover over a name the scene does not carry scans
29/// the scene ONCE, not once per frame.
30#[derive(Clone)]
31pub(crate) struct DialogHover {
32 /// Which panel set it (`"history"`, `"constraints"`, `"pmi"`, `"refsel"`) —
33 /// [`EngineState::dialog_hover_end`] acts only for the owner that set it, so a
34 /// pane that never hovered a row cannot end another pane's hover.
35 pub(crate) owner: &'static str,
36 /// The row's TEXT, exactly as the dialog listed it.
37 pub(crate) row: String,
38 /// What it resolved to, or `None` for a row the scene does not carry.
39 pub(crate) candidate: Option<pick::PickCandidate>,
40}
41
42pub struct EngineState {
43 pub scene: RenderScene,
44 pub camera: ViewCamera,
45 pub controls: ArcballControls,
46 pub settings: RenderSettings,
47 pub emphasis: Emphasis,
48 /// In-scene overlay widgets: datums/dimensions/curves, transform
49 /// gizmo, ViewCube — fed as JSON, drawn by the render core's overlay pass.
50 pub widgets: WidgetRegistry,
51 /// The engine-owned editable model recipe (ordered features + rollback
52 /// index) — the SINGLE source of truth for the model. The UI never keeps its
53 /// own copy; it mutates/reads this through the `history_*` / feature methods.
54 pub history: History,
55 interference_sessions: std::collections::BTreeMap<String, interference::InterferenceCursor>,
56 interference_sequence: u64,
57 illustration_snapshot: Option<illustration::IllustrationSnapshot>,
58 geometry_diagnostics: std::collections::BTreeMap<String, serde_json::Value>,
59 /// The build report (`{featureErrors, unresolved, displayErrors}`) of the
60 /// last history run, so the UI can show it without re-running.
61 history_report: String,
62 /// The Properties-panel metadata store: user attributes keyed by OBJECT NAME
63 /// (solid / face / edge kernel name), NOT feature id — so a record survives
64 /// feature edits as long as the object's name persists. Persisted with the
65 /// model (a top-level `metadata` field in the history document); see the
66 /// [`crate::metadata`] module for the store + the object-info/measurement API.
67 pub metadata: crate::metadata::MetadataStore,
68 /// Derived preview data stays outside the recipe and undo history: the
69 /// document's own picture at a history edit serial (opened with it, or the
70 /// last one a save rendered), and the last picture a save produced by key.
71 document_thumbnail: std::cell::RefCell<Option<(u64, serde_json::Value)>>,
72 thumbnail_cache: std::cell::RefCell<Option<(Vec<u64>, serde_json::Value)>>,
73 /// Bumped whenever settings change so the renderer re-derives per-solid
74 /// base styles (a cheap key, not a per-frame diff).
75 pub settings_generation: u64,
76 /// The engine sets this whenever the camera/scene/emphasis changed; the
77 /// presentation shell renders only when it is set (R22 on-demand render —
78 /// the OrthoCameraIdle matrix-compare analogue, made explicit).
79 pub dirty: bool,
80 /// The modal reference-selection state (the ref-select widget). `Some` while
81 /// the user is picking references for a feature-dialog field; `None`
82 /// otherwise. The picked-name list here is the SINGLE source of truth while
83 /// active (the UI reads it back; the viewport appends to it on a pick).
84 pub ref_select: Option<RefSelectState>,
85 /// Which entity KINDS a plain viewport click may select (the selection
86 /// filter, mirroring the earlier `SelectionFilter.allowedSelectionTypes`). `select_top_at`
87 /// consults it via `pick_filtered`; see the appended `SelectionFilter` impl
88 /// block near the end of this file for the state + honoring logic.
89 pub selection_filter: SelectionFilter,
90 /// The transform-controls gizmo controller: which feature (if any) has the
91 /// move/rotate gizmo armed (via the in-viewport center-sphere toggle), plus the
92 /// in-flight handle drag. All the arm/drag/apply logic lives in the appended
93 /// transform-gizmo impl block near the end of this file.
94 pub transform_gizmo: TransformArm,
95 /// The COMPONENT Move gizmo controller (assemblies §8.5): which ACOMP
96 /// instance has the bbox-center move/rotate gizmo armed, the translate/
97 /// rotate cycle mode, and the commit-on-release drag. Exclusive with
98 /// `transform_gizmo` (shared widget slot). See `component_move.rs`.
99 pub component_move: ComponentMoveArm,
100 /// The active engine-native sketch edit (`Some` while in sketch mode). Holds
101 /// the live [`crate::sketch::SketchSession`] plus the pre-entry camera + roll
102 /// to restore on exit. All the enter/exit/new logic lives in the appended
103 /// sketch-mode impl block at the END of this file.
104 sketch_edit: Option<SketchEdit>,
105 /// The active engine-native 3D SKETCH edit (`Some` while in 3D sketch
106 /// mode): the live [`crate::sketch3d::Sketch3dSession`] plus the pre-entry
107 /// camera + roll. See `sketch3d_mode.rs`.
108 sketch3d_edit: Option<Sketch3dEdit>,
109 /// The `world_per_pixel` the 3D sketch overlay was last baked at (its
110 /// working-plane and reference-plane squares are pixel-sized); `0.0` =
111 /// not in 3D sketch mode.
112 pub(crate) sketch3d_overlay_wpp: f64,
113 /// Sketch-mode camera lock. When true (the default on every sketch entry), the
114 /// camera is held flat-on to the sketch plane and an empty drag only PANS —
115 /// no orbit. Toggling it back on re-faces the camera to the plane. Meaningful
116 /// only while `sketch_edit` is `Some`. See the sketch-mode impl block.
117 sketch_camera_locked: bool,
118 /// Set for ONE frame when the sketch entity-LIST panel hovers a row (it calls
119 /// [`sketch_hover_entity`](Self::sketch_hover_entity)). The viewport, which
120 /// draws AFTER the panel and would otherwise `sketch_clear_hover` because the
121 /// pointer is off the viewport, consumes this flag via
122 /// [`take_sketch_list_hover`](Self::take_sketch_list_hover) and keeps the
123 /// panel-set hover so list→canvas highlight survives the frame.
124 sketch_list_hover_active: bool,
125 /// Transient user-facing notices (e.g. a sketch solve that failed after an
126 /// edit). The shell drains them each frame via [`take_notices`](Self::take_notices)
127 /// into a toast overlay; the engine only queues. Replaces the swallowed
128 /// `eprintln!` on the interactive re-solve paths.
129 notices: Vec<String>,
130 /// Notices that are NOT errors — a warning, or a success such as "Updated 2
131 /// part(s)" — with their severity, queued by
132 /// [`push_notice_as`](Self::push_notice_as). Beside [`Self::notices`] rather
133 /// than in it, so every existing `push_notice` stays an error unchanged.
134 graded_notices: Vec<(NoticeSeverity, String)>,
135 /// Committed-sketch visibility: the feature ids whose persistent committed-sketch
136 /// overlay is HIDDEN (its Scene-tree checkbox off). Absent = visible. See the
137 /// committed-sketch impl block appended at the END of this file.
138 hidden_sketches: std::collections::HashSet<String>,
139 /// The committed-sketch feature ids whose overlay groups were fed on the LAST
140 /// [`refresh_committed_sketches`](Self::refresh_committed_sketches), so an id that
141 /// is no longer shown (rolled back, deleted, hidden, or became the active edit)
142 /// can have its now-stale groups cleared.
143 shown_sketch_ids: Vec<String>,
144 /// The board bodies fed on the LAST
145 /// [`refresh_board_geometry`](Self::refresh_board_geometry), in stack order,
146 /// so a body this build did not produce can be swept out of the scene.
147 /// Parallels [`shown_sketch_ids`].
148 shown_board_ids: Vec<String>,
149 /// The eCAD board those bodies were built from — the rebuild test. A
150 /// SCHEMATIC edit rewrites the whole `pcb` block with the same board inside
151 /// it, and this is what makes that edit free.
152 board_built_from: Option<brep_ecad_core::board::Board>,
153 /// The pin-to-net map those bodies' NET COLOURS were built from. The board
154 /// alone does not decide them — a copper island carries the net of the pads
155 /// it touches, and which net a pad carries is the SCHEMATIC's answer — so
156 /// this is the second half of the rebuild test. Empty for a board with no
157 /// placements, which can carry no net at all.
158 board_pin_nets: std::collections::BTreeMap<(brep_ecad_core::Uuid, String), String>,
159 /// What that build cost, for the Scene tree and the tests.
160 board_build: board_geometry::BoardBuild,
161 /// The built bodies themselves, KEPT across runs. The scene reconcile drops
162 /// every derived body, so without this a dense board would be re-meshed on
163 /// every history run; a clone of these is what goes back in instead.
164 board_displays: Vec<crate::scene::SolidDisplay>,
165 /// The named plane FRAMES the last history run resolved `(frame name, frame)`.
166 /// Stored so [`refresh_construction_datums`](Self::refresh_construction_datums)
167 /// (and datum selection) can display/pick the construction datum/plane frames
168 /// without re-running. Filled from the run's
169 /// [`crate::pipeline::SceneBuildReport::frames`]; the D/P filter is applied at
170 /// display time (a frame name maps to its producing feature TYPE via the
171 /// history). See the appended construction-datum impl block at the END.
172 construction_frames: Vec<(String, brep_kernel::Frame)>,
173 /// The solved sketch PROFILES the last history run produced `(sketch id,
174 /// profile)`. Stored so [`refresh_committed_sketches`](Self::refresh_committed_sketches)
175 /// can synthesize each committed sketch's SHEET SOLID (planar face + named
176 /// boundary edges + corner vertices) without re-running. Filled from the run's
177 /// [`crate::pipeline::SceneBuildReport::profiles`]; rollback / active-edit /
178 /// visibility gating is applied at display time.
179 sketch_profiles: Vec<(String, brep_kernel::SketchProfile)>,
180 /// The named PATH chains the last history run produced `(path name, curves)`.
181 /// Stored alongside [`sketch_profiles`](Self::sketch_profiles) so
182 /// [`refresh_committed_sketches`](Self::refresh_committed_sketches) can draw the
183 /// sketch geometry no closed profile covers: a sketch's OPEN chain publishes no
184 /// profile, so the sheet builder had nothing to draw it from and an open sketch
185 /// was invisible in 3D. Filled from the run's
186 /// [`crate::pipeline::SceneBuildReport::paths`]; the per-segment `{id}:G{gid}`
187 /// entries are the display input (the whole-chain `{id}` entry duplicates them
188 /// and `{id}:REF:{source}` is projected reference geometry).
189 sketch_paths: Vec<(String, Vec<brep_kernel::NurbsCurve>)>,
190 /// The named world POINTS the last history run produced `(point name, point)`.
191 /// Stored alongside [`sketch_paths`](Self::sketch_paths) so
192 /// [`refresh_committed_sketches`](Self::refresh_committed_sketches) can draw the
193 /// sketch points no segment covers: a points-only sketch (a hole-placement
194 /// sketch) publishes no profile and no path, so it had nothing to draw from
195 /// and was invisible in 3D. Filled from the run's
196 /// [`crate::pipeline::SceneBuildReport::points`]; the per-point `{id}:P{pid}`
197 /// entries are the display input (construction points are skipped at draw
198 /// time, like construction geometry).
199 sketch_points: Vec<(String, brep_kernel::ScenePoint)>,
200 /// The named axis LINES the last history run produced `(axis name, line)`.
201 /// Stored so the feature-dimension angle gizmo can resolve a revolve `axis`
202 /// reference to a world line without re-running. Filled from the run's
203 /// [`crate::pipeline::SceneBuildReport::axes`].
204 sketch_axes: Vec<(String, brep_kernel::Axis)>,
205 /// The wire-harness routing report of the last APPLIED run (`None` before
206 /// the first run). Filled from [`crate::pipeline::SceneBuildReport::wire_harness`];
207 /// the harness panel reads it, the committed-curve display reads the port
208 /// kinds off it. See the appended wire-harness impl block.
209 wire_harness_report: Option<brep_kernel::WireHarnessReport>,
210 /// The PORTS tail's report of the last APPLIED run (`None` before the first
211 /// run, and for a document that declares no `ports` block). Filled from
212 /// [`crate::pipeline::SceneBuildReport::ports`]; the Qualify panel reads it,
213 /// and it is the only place the tail's refused names and unresolved
214 /// references surface, the tail not being a feature.
215 ports_report: Option<brep_kernel::PortsReport>,
216 /// The PMI tail's report of the last APPLIED run (`pmi_ops.rs`).
217 pub(crate) pmi_report: Option<brep_kernel::PmiReport>,
218 /// The ACTIVE PMI view (engine memory, never persisted): its camera,
219 /// display state and explode poses are applied; its annotations drawn.
220 pub(crate) pmi_active_view: Option<String>,
221 /// The annotation whose form the PMI panel shows (engine memory). Never
222 /// assign `Some` here: go through [`EngineState::open_pmi_annotation`], the
223 /// one place that counts the open.
224 pub(crate) pmi_open_annotation: Option<String>,
225 /// The VIEW whose form the PMI panel shows (engine memory) — exclusive
226 /// with [`Self::pmi_open_annotation`], since the pane shows one dialog.
227 /// Never assign `Some` here: go through [`EngineState::open_pmi_view`].
228 pub(crate) pmi_open_view: Option<String>,
229 /// The annotation SELECTED in the PMI tree or by its viewport label: the
230 /// tree row's highlight and the label's accent. Cleared with the selection
231 /// (Esc / Clear / a click on empty space); read through
232 /// [`EngineState::pmi_selected_annotation`], which prunes a stale id.
233 pub(crate) pmi_selected_annotation: Option<String>,
234 /// The VIEW selected in the PMI tree — its row's highlight. Selecting a
235 /// view never ACTIVATES it (a double click does); exclusive with
236 /// [`Self::pmi_selected_annotation`], since the tree selects one row.
237 /// Read through [`EngineState::pmi_selected_view`], which prunes a stale id.
238 pub(crate) pmi_selected_view: Option<String>,
239 /// How many times a PMI dialog — an annotation's or a view's — has been
240 /// OPENED: monotonic, never reset, closes do not count. The app's dialog
241 /// door keys on it beside the subject so a re-open of the dialog that is
242 /// ALREADY open is still one open event (a viewport label double click on the
243 /// annotation whose form is up, which a user makes precisely because they
244 /// are on another tab).
245 pub(crate) pmi_dialog_opens: u64,
246 /// The modeling camera / visibility / wireframe remembered on entering
247 /// the PMI workbench, restored when a view deactivates or the workbench
248 /// is left.
249 pub(crate) pmi_modeling: Option<pmi_ops::PmiModelingSnapshot>,
250 /// The un-exploded displays of the solids the active view's explode
251 /// annotations posed, for an exact restore.
252 pub(crate) pmi_explode_originals: std::collections::HashMap<String, crate::scene::SolidDisplay>,
253 /// A balloon whose dragged bubble could not be re-projected yet because
254 /// the scene did not hold its occurrence's exact solids: re-derived when
255 /// the topology reply lands (`pmi_refresh_stale_balloon`).
256 pub(crate) pmi_balloon_stale: Option<String>,
257 /// `(world_per_pixel, view direction)` the PMI overlay was last baked at.
258 pub(crate) pmi_overlay_key: Option<(f64, [f64; 3])>,
259 pub(crate) pmi_hovered: Option<String>,
260 pub(crate) pmi_label_hover_active: bool,
261 /// The sheet the sheet viewport shows (engine memory, never persisted).
262 /// `None` = the 3D view (`sheet_ops.rs`).
263 pub(crate) sheet_open: Option<String>,
264 /// The sheet or placed view whose form the Sheets pane shows. Never assign
265 /// `Some` here: go through [`EngineState::open_sheet_object`], the one
266 /// place that counts the open.
267 pub(crate) sheet_open_object: Option<String>,
268 /// How many times a sheet object's form has been OPENED — the sheet half
269 /// of the same counter as [`Self::pmi_dialog_opens`], read by the
270 /// app's dialog door so a re-open of the object already open still counts.
271 pub(crate) sheet_object_opens: u64,
272 /// The placed view, sheet dimension or ordinate set SELECTED in the Sheets
273 /// tree or on the paper: the tree row's highlight and the object's accent
274 /// on the sheet. Cleared with the selection; read through
275 /// [`EngineState::sheet_selected_object`], which prunes a stale id.
276 pub(crate) sheet_selected_object: Option<String>,
277 /// The last projected sheet, kept until the model or the block moves: a
278 /// sheet is a hidden-line pass over the whole model and must never run
279 /// per frame.
280 pub(crate) sheet_cache: Option<sheet_ops::SheetCache>,
281 /// The sheet's exact passes on the runner and waiting for it.
282 pub(crate) sheet_lines: sheet_ops::SheetLinesWork,
283 /// The open document's name, as the shell means it (the tab, the file, the
284 /// root PRODUCT of a STEP export). Engine memory, never persisted: the
285 /// document does not carry its own name, the SHELL owns it, and it is here
286 /// because a drawing sheet's TITLE BLOCK prints it. Set every frame by the
287 /// app from the active tab (a no-op when unchanged).
288 pub(crate) document_name: String,
289 /// What the spline-anchor cage overlay was last fed for — `(spline id,
290 /// selected anchor, applied run generation)` — so the per-frame refresh
291 /// the history panel makes while an SP form is open is a no-op until one
292 /// of those moves. See `spline_edit.rs`.
293 spline_overlay_key: Option<(String, Option<usize>, u64)>,
294 /// Whether the ports placed components carry (`ACOMP1:PORT1`) draw their
295 /// sheets. The app sets it from the active workbench — one that shows the
296 /// Wire Harness panel; a document's own PORT features always draw.
297 component_ports_visible: bool,
298 /// The spline whose anchor editor is open (fed by the history panel with
299 /// the cage overlay), so a viewport click on one of its anchor dots selects
300 /// that anchor; the index of the last such pick, until the panel takes it.
301 spline_edit_feature: Option<String>,
302 spline_anchor_picked: Option<usize>,
303 /// The NURBS solid whose control-cage editor is open (fed by the history
304 /// panel with the cage overlay) and what that overlay was last fed for —
305 /// `(feature id, selected points, applied run generation)`. The selection
306 /// itself is the transform gizmo's (`TransformArm::cage`). See
307 /// `cage_edit.rs`.
308 cage_edit_feature: Option<String>,
309 cage_overlay_key: Option<(String, Vec<usize>, u64)>,
310 /// Construction datum/plane visibility: the frame NAMES whose datum plane is
311 /// HIDDEN (its Scene-tree checkbox off). Absent = visible. Mirrors
312 /// [`hidden_sketches`].
313 hidden_datums: std::collections::HashSet<String>,
314 /// The datum frame NAMES fed to the widget on the LAST
315 /// [`refresh_construction_datums`](Self::refresh_construction_datums). The datum
316 /// feed REPLACES its set wholesale each call, so this is a bookkeeping mirror of
317 /// what is currently shown (parallels [`shown_sketch_ids`]).
318 shown_datum_names: Vec<String>,
319 /// The history-run seam (M2a of the off-thread runner). Owns the scene-free
320 /// runner + its delta baseline (`name → last-emitted handle`) ACROSS reruns:
321 /// [`rerun_history`](Self::rerun_history) SUBMITS a run tagged with
322 /// [`run_generation`](Self::run_generation), and [`pump`](Self::pump) drains
323 /// the completed reply and [`apply_run_output`](Self::apply_run_output)s its
324 /// delta to [`Self::scene`]. The [`InlineRunner`](crate::runner::InlineRunner)
325 /// default runs synchronously (submit → immediate poll, byte-identical to the
326 /// old in-place reconcile); a background thread/worker impl slots in behind the
327 /// same trait in M2b/M3. Reset on a document switch
328 /// ([`set_history_json`](Self::set_history_json)) so a new model rebuilds fully.
329 pub(crate) runner: Box<dyn crate::runner::HistoryRunner>,
330 plugins: plugins::Plugins,
331 /// Monotonic run counter: bumped each time [`rerun_history`](Self::rerun_history)
332 /// SUBMITS a run, and stamped onto the reply so a stale reply (a newer run that
333 /// finished first) can be dropped. `run_generation != applied_generation` means
334 /// a run is in flight (always equal for the synchronous Inline runner).
335 run_generation: u64,
336 /// The generation of the last reply [`pump`](Self::pump) APPLIED — the high-water
337 /// mark that gates stale replies.
338 applied_generation: u64,
339 /// The latest [`crate::runner::RunProgress`] of the run in flight (a
340 /// background runner posts one before each feature it executes), or
341 /// `None` when nothing is running. Names what the spinner is waiting on.
342 run_progress: Option<crate::runner::RunProgress>,
343 /// The feature the last CANCELLED run was executing (`Some("")` when the
344 /// run was cancelled before any progress arrived), cleared by the next
345 /// submit. The history header shows it until the model is rebuilt.
346 cancelled_run: Option<String>,
347 /// A TRANSIENT expressions source the runs evaluate instead of the
348 /// document's own (the family table's row preview). Never written into
349 /// the history: not saved, not undone, not dirty. See
350 /// [`Self::set_expression_preview`].
351 expression_preview: Option<ExpressionPreview>,
352 /// How many run replies came back from the runner (applied or dropped as
353 /// stale). A run the runner coalesced away never replies, so this counts
354 /// the runs actually EXECUTED.
355 runs_replied: u64,
356 /// Displays shipped by replies [`pump`](Self::pump) DROPPED as superseded,
357 /// by solid name, latest wins. A background runner executes every run it
358 /// is handed and its delta baseline (`name → last-emitted handle`) advances
359 /// on each, so a later reply can say "unchanged, keep yours" about a
360 /// display that only ever travelled in a dropped reply (the reporter's
361 /// arrow-head drag: the frame that built the new radius was superseded by
362 /// the still-pointer frames that replayed its handle). The keep is served
363 /// from here when the scene's display is not the handle it names. Cleared
364 /// with the runner's baseline (document switch, cancel) and whenever a
365 /// fresh display for the name is applied.
366 superseded_displays: HashMap<String, crate::scene::SolidDisplay>,
367 /// A pending one-shot "frame the scene once the in-flight run lands" request.
368 /// Import / Open SUBMIT an async run (native [`ThreadRunner`], wasm worker) and
369 /// want to `zoom_to_fit` the RESULT — but the scene is still empty when they
370 /// return, so an immediate fit frames nothing (bbox empty → no-op). Instead they
371 /// set this flag and [`pump`](Self::pump) performs the fit on the first apply that
372 /// leaves the run no longer pending. Under the synchronous Inline runner the run
373 /// applies inside the submitting call's own `pump`, so the fit is still immediate.
374 pending_fit: bool,
375 /// EAGER provenance `name → creating-feature id` for the current resident
376 /// solids, shipped with each run ([`crate::pipeline::RunOutput::provenance`]) and
377 /// replaced wholesale in [`apply_run_output`](Self::apply_run_output). Answers
378 /// `creating_feature` + the Info tab's `creatingFeature` WITHOUT a cold
379 /// `execute_history` — the freeze side-door once the run lives off-thread.
380 pub(crate) provenance: std::collections::HashMap<String, String>,
381 /// EAGER ENTITY ORIGIN `face/edge NAME → ORIGINATING feature id` (FIRST writer in
382 /// timeline order), shipped with each run
383 /// ([`crate::pipeline::RunOutput::entity_origin`]) and replaced wholesale in
384 /// [`apply_run_output`](Self::apply_run_output). Unlike `provenance` (the SOLID's
385 /// LAST writer) this is the feature that gave the face/edge its NAME — the answer
386 /// `creating_feature` returns for a face/edge (the "Edit owning feature" action +
387 /// the Info tab's `creatingFeature`), so it rolls to the entity's origin, not the
388 /// owning solid's last producer.
389 pub(crate) entity_origin: std::collections::HashMap<String, String>,
390 /// Object-info MEASUREMENT cache `name → merged object-info JSON`, filled by
391 /// [`pump_queries`](crate::metadata) from the runner's replies and served every
392 /// frame a selection persists (so `object_info_json` fires ONE query per
393 /// selection, not one per frame). Invalidated on any geometry change
394 /// (`apply_run_output`) or metadata edit (`set_metadata_attribute`).
395 pub(crate) info_cache: std::collections::HashMap<String, String>,
396 /// In-flight measurement queries `id → object NAME` — the key needed to MERGE a
397 /// reply back into the info cache (inject `name` + `creatingFeature`, the latter
398 /// now resolved by the entity name itself). Cleared alongside `info_cache` on a
399 /// geometry change so a stale reply is dropped rather than caching a superseded
400 /// measurement.
401 pub(crate) pending_query: std::collections::HashMap<u64, String>,
402 /// Monotonic measurement-query id (pairs a [`crate::runner::MeasureReply`] back
403 /// with its `pending_query` entry).
404 pub(crate) next_query_id: u64,
405 /// Off-thread mesh reconstructions awaiting a runner reply.
406 pub(crate) pending_mesh_imports: std::collections::HashMap<u64, MeshImportDestination>,
407 pub(crate) mesh_preview_results: std::collections::VecDeque<crate::runner::MeshImportReply>,
408 /// Monotonic id for pairing mesh import replies with submissions.
409 pub(crate) next_mesh_import_id: u64,
410 /// Topology requests in flight (see [`crate::runner::TopologyRequest`]).
411 pub(crate) pending_topology: std::collections::HashSet<u64>,
412 /// Every resident handle asked for since the last applied run, answered or
413 /// not: a handle the runner could not clone is not asked for again until a
414 /// run brings new handles.
415 pub(crate) topology_asked: std::collections::HashSet<u32>,
416 /// Monotonic id for pairing topology replies with requests.
417 pub(crate) next_topology_id: u64,
418 /// The run's ASSEMBLY tail as the runner read it
419 /// ([`crate::pipeline::AssemblySync`]) — the constraint state, statuses, DOF
420 /// and overlay rows every assembly panel reads, adopted from the reply of the
421 /// last applied run. `None` for a componentless document. This is what
422 /// replaced the second, main-side `execute_history`: see `assembly_ops`.
423 pub(crate) assembly_sync: Option<crate::pipeline::AssemblySync>,
424 /// The scene's assembly COMPONENT records (deterministic id order), adopted
425 /// from [`Self::assembly_sync`] — the Assembly Structure tree's source.
426 /// Empty for componentless documents.
427 pub(crate) assembly_components: Vec<crate::pipeline::ComponentSnapshot>,
428 /// The [`Self::applied_generation`] the main-side kernel assembly SESSION was
429 /// last installed for (`None` = never). Only a constraint MUTATION needs one
430 /// — its solve runs against the resident geometry of the thread that asks —
431 /// so only `ensure_assembly_session` sets this. See `assembly_ops`.
432 pub(crate) assembly_session_generation: Option<u64>,
433 /// Every output name the last applied run CONSUMED
434 /// ([`crate::pipeline::RunOutput::consumed`]) — what the committed-sketch
435 /// refresh hides a consumed sketch by, and what it used to re-execute the
436 /// whole history on the UI thread to learn.
437 pub(crate) consumed_names: std::collections::HashSet<String>,
438 /// Re-entrancy guard for the parts-library resync in [`Self::pump`] (a
439 /// refused run re-runs, and `rerun_history` pumps).
440 pub(crate) library_resync: bool,
441 /// A run just RELEASED a sketch's edge links whose edges it could not
442 /// resolve (see [`Self::release_unresolved_sketch_links`]): the history
443 /// changed under the applied report, so [`Self::pump`] re-runs it once and
444 /// the report stops naming edges the sketch no longer lists.
445 pub(crate) sketch_links_released: bool,
446 /// The assembly-constraint viewport overlays — the cached
447 /// [`crate::constraint_overlays::ConstraintOverlay`] records the engine last
448 /// built from the kernel session (`assembly_overlay_json` + state), the source
449 /// of the drawn leader/arrow group, the label feed, and the grabbable-handle
450 /// hit regions. Refreshed after every history apply and every constraint
451 /// mutation; see the appended assembly-overlay impl block.
452 pub(crate) constraint_overlays: Vec<crate::constraint_overlays::ConstraintOverlay>,
453 /// A live GRAB on a constraint's distance-arrow / angle-arc handle (`Some`
454 /// between `constraint_drag_begin` and `constraint_drag_release`): the drag
455 /// previews the value locally and COMMITS on release via
456 /// `assembly_update_constraint_json` (which auto-solves).
457 pub(crate) constraint_drag: Option<ConstraintDrag>,
458 /// The `world_per_pixel` the constraint overlay buffers were last baked at
459 /// (screen-constant arc/rod sizing): `ensure_constraint_overlay_current`
460 /// re-bakes when the camera zoom moves it materially. `0.0` = never baked.
461 constraint_overlay_wpp: f64,
462 /// The `world_per_pixel` the FEATURE-DIMENSION gizmo group
463 /// (`feature-dim-leaders`) was last baked at. The leaders' rod/cone/origin
464 /// sphere — and the angular arc's whole world RADIUS — are
465 /// `pixels × world_per_pixel`, so a zoom makes the drawn gizmo stale;
466 /// `ensure_feature_dimension_overlay_current` re-bakes on a material move.
467 /// `0.0` = nothing baked (gizmo disarmed / not in dimension mode).
468 /// See `feature_dims`.
469 pub(crate) feature_dim_overlay_wpp: f64,
470 /// The `world_per_pixel` the live SKETCH overlay groups (geometry, points,
471 /// preview, `sketch-dim-leaders`, `sketch-constraint-glyphs`) were last baked
472 /// at — construction dashes, dimension arrowheads and constraint glyphs are
473 /// all screen-constant. `ensure_sketch_overlay_current` re-bakes on a material
474 /// zoom. `0.0` = nothing baked (not in sketch mode). See `sketch_input`.
475 pub(crate) sketch_overlay_wpp: f64,
476 /// The constraint id whose referenced ELEMENTS the panel/label hover is
477 /// currently highlighting (dedupe key so a held hover doesn't re-bump the
478 /// emphasis generation every frame).
479 constraint_hovered: Option<String>,
480 /// Set for ONE frame when a constraint LABEL hover applied the element
481 /// highlight; the viewport's modeling hover branch consumes it (mirrors
482 /// [`Self::take_sketch_list_hover`]) so the scene hover doesn't clobber it.
483 constraint_label_hover_active: bool,
484 /// The SCENE-TREE row the pointer is over, as the pick candidate its hover
485 /// lit (the row→viewport highlight). Kept so
486 /// [`scene_tree_hover_end`](Self::scene_tree_hover_end) clears ONLY a hover
487 /// the tree itself set — never one the viewport lit in the meantime — and as
488 /// the dedupe key that keeps a held row hover from re-bumping the emphasis
489 /// generation every frame. See `scene_query.rs`.
490 scene_tree_hovered: Option<pick::PickCandidate>,
491 /// Set for ONE frame while a Scene-tree row is hovered; the viewport's
492 /// modeling hover branch consumes it via
493 /// [`take_scene_tree_hover`](Self::take_scene_tree_hover) and skips the
494 /// pointer-off-viewport `clear_hover` (mirrors
495 /// [`Self::take_sketch_list_hover`]) so the row-set highlight survives.
496 scene_tree_hover_active: bool,
497 /// The DIALOG row the pointer is over — a feature form's reference line, its
498 /// read-only `Outputs` line, or the reference picker card's picked-name line
499 /// (the row→viewport highlight, the twin of the Scene tree's above). Its OWN
500 /// slot, not the tree's: History, Constraints, PMI and the Scene tree can all
501 /// be on screen at once in a split dock, and a shared slot would let the pane
502 /// that draws second end the hover the pane that drew first had just set. See
503 /// [`DialogHover`] for what it memoizes and `scene_query.rs` for the API.
504 dialog_hovered: Option<DialogHover>,
505 /// Set for ONE frame while a dialog row is hovered; the viewport's modeling
506 /// hover branch consumes it via [`take_dialog_hover`](Self::take_dialog_hover)
507 /// and skips the pointer-off-viewport `clear_hover` (mirrors
508 /// [`Self::take_scene_tree_hover`]) so the row-set highlight survives.
509 dialog_hover_active: bool,
510 /// The constraint SELECTED via its viewport label chip (`Some` = selected):
511 /// drives the chip's selected accent, the context bar's Delete-constraint
512 /// action, and clears with the selection (Esc / Clear / delete). Read
513 /// through [`Self::selected_constraint`], which prunes a stale id.
514 selected_constraint: Option<String>,
515 /// The feature SELECTED in the history tree, beside the geometry selection
516 /// that selecting it made. It stays selected only while that geometry
517 /// selection is unchanged — a viewport pick, Esc or Clear replaces the
518 /// geometry and so deselects the feature. Read through
519 /// [`Self::selected_feature`].
520 selected_feature: Option<(String, selection_ux::FeatureSelection)>,
521 /// How many times a constraint's dialog has been OPENED through
522 /// [`Self::assembly_set_constraint_open`] — the constraint half of the same
523 /// counter as [`Self::pmi_dialog_opens`], read by the app's dialog door
524 /// so a re-open of the constraint already open still counts. The flag
525 /// itself lives in the kernel (it is part of the saved document), so this
526 /// counts the OPENS this engine performed, not the flag's history.
527 pub(crate) constraint_opens: u64,
528 /// The STEP product structure a [`Self::probe_step_assembly`] parsed, held
529 /// until the import dialog's button decides its fate: consumed by
530 /// [`Self::import_probed_step_assembly`], dropped by
531 /// [`Self::discard_probed_step_assembly`] (Cancel), by the next probe, or by
532 /// a document switch. The ONE parse of a structured STEP import lives here —
533 /// the dialog needs the counts BEFORE the user chooses, and re-parsing
534 /// multi-MB Part-21 text on the way back would double the most expensive step
535 /// of the import. See `model_io`'s import block.
536 pub(crate) pending_step_assembly: Option<brep_kernel::StepAssembly>,
537 /// STEP probes submitted to the runner and not yet answered (their ids).
538 pub(crate) pending_step_probes: std::collections::HashSet<u64>,
539 /// Answered probes awaiting [`Self::take_step_probe`], oldest first.
540 pub(crate) step_probe_results: std::collections::VecDeque<(u64, StepProbeOutcome)>,
541 /// Monotonic id pairing a probe reply with its submission.
542 pub(crate) next_step_probe_id: u64,
543}
544
545/// What a STEP probe found, as the file panel consumes it (see
546/// [`EngineState::submit_step_probe`]).
547#[derive(Debug, Clone, PartialEq)]
548pub enum StepProbeOutcome {
549 /// Product structure: the parsed assembly is stashed for
550 /// [`EngineState::import_probed_step_assembly`]; these are its counts.
551 Structure(model_io::StepAssemblyProbe),
552 /// No structure worth keeping — take the flat lane.
553 Flat,
554 /// The text did not parse as Part 21; the flat lane refuses it with its
555 /// own wording.
556 Failed(String),
557}
558
559/// A live constraint-handle drag: which constraint + which `inputParams` field
560/// (`distance` / `angle`), the params snapshot the commit mutates, and the live
561/// preview value the overlay/label show while dragging.
562#[derive(Debug, Clone)]
563pub struct ConstraintDrag {
564 pub id: String,
565 pub field: &'static str,
566 pub params: serde_json::Value,
567 pub preview: f64,
568}
569
570/// WHO a finished reference-selection commits to: a history FEATURE's params
571/// (the original widget) or an ASSEMBLY CONSTRAINT's `inputParams` (same modal,
572/// different commit lane — see `assembly_ops::begin_ref_select_for_constraint`).
573#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
574pub enum RefSelectTarget {
575 #[default]
576 Feature,
577 AssemblyConstraint,
578 /// ONE SPLINE ANCHOR's port attachment: `path` is unused, the picked name
579 /// must be a PORT feature id (a click on a port's drawn sheet), and Finish
580 /// writes `persistentData.spline.points[index].attachment` (see
581 /// `spline_edit.rs`).
582 SplineAnchor { index: usize },
583 /// A PMI annotation's reference field: `feature_id` is the annotation id;
584 /// Finish writes the picked names into its params through the PMI block
585 /// (`pmi_ops.rs`). Vertex picks are `{solid}@x,y,z` in WORLD coordinates.
586 Pmi,
587 /// ONE CONNECTION POINT's reference field: `feature_id` is the point's
588 /// part-local ADDRESS (`J1.VCC`) — a connection point is data, not a
589 /// feature, so there is no id to carry — and `path` is the block key the
590 /// pick writes (`pointRef` / `directionRef`). Finish writes it through
591 /// `ports_ops::ports_commit_ref`, and NOTHING ROLLS: a point resolves at
592 /// the tail of the run, so the geometry it may name is what is on screen.
593 PortPoint { field: ports_ops::PortRefField },
594 /// A drawing-sheet object's reference field — a dimension's anchors, an
595 /// ordinate set's datum or members, a section's cutting line, a detail's
596 /// centre or rim: `feature_id` is the object id, the picks are ANCHORS
597 /// clicked on the open sheet's paper (`sheet_ops.rs`), and nothing rolls.
598 Sheet,
599}
600
601/// The modal state of the reference-selection widget while it is ACTIVE: which
602/// feature-dialog field is being filled, what it accepts, and the running list
603/// of picked kernel names (the source of truth — added by picking in the view,
604/// removed via the per-line X). See the ref-select methods below.
605#[derive(Debug, Clone, Default)]
606pub struct RefSelectState {
607 /// The id of the feature whose param is being edited.
608 pub feature_id: String,
609 /// The JSON path into that feature's `inputParams` the names write to
610 /// (`["targetSolid"]`, `["boolean","targets"]`, `["faceRef"]`, …).
611 pub path: Vec<String>,
612 /// A human label for the modal heading (the field's label).
613 pub label: String,
614 /// The allowed pick kinds (`["SOLID"]`, `["FACE"]`, …) — the type constraint.
615 pub filter: Vec<String>,
616 /// Whether the field takes a LIST of references (else a single one).
617 pub multiple: bool,
618 /// The picked kernel names — the running selection (source of truth).
619 pub names: Vec<String>,
620 /// The rollback step to restore on Finish/Cancel (the edited feature's own
621 /// step): entering the mode rolls to the pre-feature "before" state, so we
622 /// remember where to return.
623 pub restore_index: usize,
624 /// Which surface Finish commits the names to (feature params vs an
625 /// assembly constraint's `inputParams`). Defaults to [`RefSelectTarget::Feature`].
626 pub target: RefSelectTarget,
627 /// How many picks landed since the field was activated (a viewport click,
628 /// a fed name, a sheet anchor). A SINGLE-valued field drawn IN ITS FORM
629 /// finishes on its first pick — the form's consumer has nothing to wait
630 /// for once the one reference is in — and this is what says one landed,
631 /// as opposed to the seed the field opened with.
632 pub picks: usize,
633 /// Whether the field's own form drew the pick this frame. An in-form pick
634 /// has no card of its own, so a form that stops being drawn (its pane
635 /// tabbed away or closed, its subject gone) would strand the pick with
636 /// nothing on screen to end it; the shell finishes a pick nobody claimed.
637 pub claimed: bool,
638 /// The form asked for the pick to finish at the END of this frame rather
639 /// than now: an edit elsewhere on a feature's form while its pick runs.
640 /// Deferred because the viewport pane may draw AFTER the form in the same
641 /// frame and the click that caused the edit (a text field losing focus)
642 /// may itself be the pick — finishing at once would hand that click to
643 /// the ordinary selection instead.
644 pub finish_requested: bool,
645}
646
647impl Default for EngineState {
648 fn default() -> Self {
649 Self {
650 scene: RenderScene::new(),
651 camera: ViewCamera::default(),
652 controls: ArcballControls::new(),
653 settings: RenderSettings::default(),
654 emphasis: Emphasis::default(),
655 widgets: WidgetRegistry::new(),
656 history: History::default(),
657 history_report: String::new(),
658 metadata: crate::metadata::MetadataStore::new(),
659 document_thumbnail: std::cell::RefCell::new(None),
660 thumbnail_cache: std::cell::RefCell::new(None),
661 settings_generation: 1,
662 dirty: true,
663 ref_select: None,
664 selection_filter: SelectionFilter::default(),
665 transform_gizmo: TransformArm::default(),
666 component_move: ComponentMoveArm::default(),
667 sketch_edit: None,
668 sketch3d_edit: None,
669 sketch3d_overlay_wpp: 0.0,
670 sketch_camera_locked: true,
671 sketch_list_hover_active: false,
672 notices: Vec::new(),
673 graded_notices: Vec::new(),
674 hidden_sketches: std::collections::HashSet::new(),
675 shown_sketch_ids: Vec::new(),
676 shown_board_ids: Vec::new(),
677 board_built_from: None,
678 board_pin_nets: Default::default(),
679 board_build: board_geometry::BoardBuild::default(),
680 board_displays: Vec::new(),
681 construction_frames: Vec::new(),
682 sketch_profiles: Vec::new(),
683 sketch_paths: Vec::new(),
684 sketch_points: Vec::new(),
685 sketch_axes: Vec::new(),
686 wire_harness_report: None,
687 ports_report: None,
688 sheet_open: None,
689 sheet_open_object: None,
690 sheet_object_opens: 0,
691 sheet_selected_object: None,
692 sheet_cache: None,
693 sheet_lines: Default::default(),
694 document_name: String::new(),
695 pmi_report: None,
696 pmi_active_view: None,
697 pmi_open_annotation: None,
698 pmi_open_view: None,
699 pmi_selected_annotation: None,
700 pmi_selected_view: None,
701 pmi_dialog_opens: 0,
702 pmi_modeling: None,
703 pmi_explode_originals: std::collections::HashMap::new(),
704 pmi_balloon_stale: None,
705 pmi_overlay_key: None,
706 pmi_hovered: None,
707 pmi_label_hover_active: false,
708 spline_overlay_key: None,
709 component_ports_visible: false,
710 spline_edit_feature: None,
711 spline_anchor_picked: None,
712 cage_edit_feature: None,
713 cage_overlay_key: None,
714 hidden_datums: std::collections::HashSet::new(),
715 shown_datum_names: Vec::new(),
716 runner: Box::new(crate::runner::InlineRunner::new()),
717 plugins: plugins::Plugins::default(),
718 interference_sessions: Default::default(),
719 interference_sequence: 0,
720 illustration_snapshot: None,
721 geometry_diagnostics: Default::default(),
722 run_generation: 0,
723 applied_generation: 0,
724 run_progress: None,
725 cancelled_run: None,
726 expression_preview: None,
727 runs_replied: 0,
728 superseded_displays: HashMap::new(),
729 pending_fit: false,
730 provenance: std::collections::HashMap::new(),
731 entity_origin: std::collections::HashMap::new(),
732 info_cache: std::collections::HashMap::new(),
733 pending_query: std::collections::HashMap::new(),
734 next_query_id: 0,
735 pending_mesh_imports: std::collections::HashMap::new(),
736 mesh_preview_results: std::collections::VecDeque::new(),
737 next_mesh_import_id: 0,
738 pending_topology: std::collections::HashSet::new(),
739 topology_asked: std::collections::HashSet::new(),
740 next_topology_id: 0,
741 assembly_components: Vec::new(),
742 assembly_sync: None,
743 assembly_session_generation: None,
744 consumed_names: std::collections::HashSet::new(),
745 library_resync: false,
746 sketch_links_released: false,
747 constraint_overlays: Vec::new(),
748 constraint_drag: None,
749 constraint_overlay_wpp: 0.0,
750 feature_dim_overlay_wpp: 0.0,
751 sketch_overlay_wpp: 0.0,
752 constraint_hovered: None,
753 constraint_label_hover_active: false,
754 scene_tree_hovered: None,
755 scene_tree_hover_active: false,
756 dialog_hovered: None,
757 dialog_hover_active: false,
758 selected_constraint: None,
759 selected_feature: None,
760 constraint_opens: 0,
761 pending_step_assembly: None,
762 pending_step_probes: std::collections::HashSet::new(),
763 step_probe_results: std::collections::VecDeque::new(),
764 next_step_probe_id: 1,
765 }
766 }
767}
768
769impl EngineState {
770 pub fn new() -> Self {
771 Self::default()
772 }
773
774 fn pick_options(&self) -> PickOptions {
775 PickOptions {
776 double_sided: self.settings.pick_double_sided,
777 ..PickOptions::default()
778 }
779 }
780}
781
782/// Has the camera's `world_per_pixel` moved MATERIALLY (>0.5%) away from the
783/// value an overlay group's screen-constant sizing was baked at?
784///
785/// The ONE judgement every camera-keyed overlay re-bake shares
786/// (`ensure_overlays_current` and the per-group ensures it drives), so
787/// "what counts as a zoom" cannot drift between them. `baked == 0.0` means
788/// "never baked" — a state each caller handles itself, so it reads as NOT
789/// stale here. The band is what keeps a quiet frame quiet: float jitter in the
790/// camera never trips it, so there is no per-frame re-bake loop.
791pub(crate) fn overlay_wpp_stale(baked: f64, now: f64) -> bool {
792 baked > 0.0 && (now - baked).abs() > baked * 0.005
793}
794
795// ============================================================================
796// MODULE MAP — engine_state is split into topic children. THIS file is the
797// module root: it keeps the EngineState/RefSelectState structs, Default, the
798// constructor + shared pick_options, the child `mod` declarations, and the
799// re-exports that preserve the original `engine_state::*` public paths.
800// Append new work to the matching child (or add a new child + re-export here).
801// ============================================================================
802
803/// The assembly surface (Wave-3): main-side session sync + component
804/// projection, constraint CRUD with the document fold (pose-authority
805/// contract), the insert-component flow, component fix/select actions, the
806/// constraint flavor of the reference picker, and `document_signature` — the
807/// ONE parts-library `sourceSignature` hash every writer shares.
808mod assembly_ops;
809/// Assembly-constraint viewport overlays: overlay refresh from the kernel
810/// session, the grabbable distance/angle handle pick + drag preview +
811/// `assembly_update_constraint_json` commit, movedSolids re-tessellation, the
812/// drag-path document fold, label feed + element hover, and tests.
813mod assembly_overlay;
814/// BOM export: the `{partName, sourceKey, quantity}` parts list off the
815/// main-side parts library + live component projection, serialized as CSV /
816/// JSON for the file dialog's Export modal.
817mod bom;
818/// Camera & view commands (zoom-to-fit, resize, pointer/wheel ingestion,
819/// projection, standard views, camera state/matrices, world→screen) plus the
820/// widget-overlay feeds (datums/overlay/dimensions/transform JSON), the
821/// ViewCube (incl. `apply_look_direction`), `datum_pick`, the widget
822/// transform-handle hover/pick/drag path, and `dimension_anchors_json`.
823mod camera_widgets;
824/// Committed-sketch persistent overlays: sheet-solid synthesis from stored
825/// profiles (`refresh_committed_sketches`), per-sketch visibility, the
826/// Scene-tree rows + `sketch_entities_json`, and the committed-sketch tests.
827/// The board's 3D bodies (substrate + copper + via barrels), derived from the
828/// document's `pcb` block after every run — see the module notes.
829mod board_geometry;
830mod committed_sketches;
831/// The COMPONENT Move gizmo (assemblies §8.5): translate→rotate→off cycle at
832/// the member-bbox center, fixed-refusal, free-move drag with the pose commit
833/// (and re-solve) on release. See `ComponentMoveArm`.
834mod component_move;
835/// Assembly COMPONENT read surface: the app-side component projection derived
836/// from the history's ACOMP features + the scene's namespaced solid names
837/// (`component_of_solid` / `component_info` / `component_bbox_center`), and its
838/// tests + shared assembly fixtures.
839mod components;
840/// Construction datum/plane display: frame→feature mapping,
841/// `refresh_construction_datums`, datum visibility + selection + entity rows,
842/// and the construction-datum tests.
843mod construction_datums;
844/// Expressions & configurator surface (`expressions_json`, `set_expressions`,
845/// `configurator_json`, `expression_variables_json`) + parsing helpers + tests.
846mod expressions;
847pub use expressions::ExpressionPreview;
848/// The feature-dimension gizmo (dimension arrows / angle arc / center-sphere
849/// toggle): arm state, `__brep`-style annotation JSON, overlay publishing,
850/// drag + set-value writeback, `FEATURE_DIM_OVERLAY`, fd_* math helpers, tests.
851mod feature_dims;
852/// The assembly interference check: the bbox-prefiltered pairwise
853/// non-destructive INTERSECT sweep over component instances
854/// (`interference_check`), its report types, and the pure pair planner.
855mod interference;
856mod batch;
857mod plugins;
858mod illustration;
859/// History runs & the feature CRUD surface: `run_history_json`, the
860/// runner/pump/apply seam, history JSON accessors, roll/update/add/delete/
861/// reorder, engine undo/redo, `load_model_and_fit`,
862/// and the history-cache rollback tests.
863mod history_ops;
864/// Model I/O: `import_step_feature`, `export_step_text`, `export_stl_text`,
865/// `triangle_normal`, and the io tests.
866mod model_io;
867/// Construction PLANES as ordinary pick candidates: the combined
868/// scene+plane-card candidate list (`pick_candidates_at`), the planes-aware
869/// single-hit pick (`pick_top_at`), the shared candidate ordering, and the
870/// plane-pick tests. Read its header for how a plane competes for a pick.
871mod plane_pick;
872/// Scene & object queries: `pick_json`/`hover_json`, settings + emphasis +
873/// visibility + color overrides, `scene_listing_json`, `depth_range_bbox`,
874/// `scene_entities_json`/select-by-name, mass properties + object info, tests.
875mod scene_query;
876/// The selection-filter state + kind gating (`SelectionFilter`,
877/// `select_filtered_at`, hide-selected) and its tests.
878mod selection_filter;
879/// Selection & hover UX: clear/select-top/selection JSON, the modal
880/// ref-select widget (incl. `set_json_at`), hover/candidate cycling, and the
881/// live sketch overlay feed (`set_sketch_overlay`/`clear_sketch_overlay`) +
882/// selection UX tests.
883mod selection_ux;
884/// Sketch editing ops appended after dimensions: dimension labels/value/drag,
885/// sketch undo/redo + diagnostics dump, trim, external edge refs
886/// (pick/link/reproject), and hand-draw strokes.
887mod sketch_edit_ops;
888/// Sketch-mode viewport input: overlay refresh, uv picking, hover/click/drag,
889/// the draw tools + pending-geometry helpers, and delete-selection.
890mod sketch_input;
891/// Sketch mode session: `SketchDrag`/`SketchEdit` state, plane-frame helpers,
892/// enter/exit/new sketch, and the camera lock.
893mod sketch_mode;
894/// 3D sketch mode: `Sketch3dEdit`, enter/exit/new, the cursor-to-3D mapping,
895/// the draw tools, reference linking, constraint edits and undo.
896mod sketch3d_mode;
897pub use sketch3d_mode::{Sketch3dCursorHit, Sketch3dEdit};
898/// Sketch entity-list panel rows + notices + solver settings
899/// (`SketchEntityRow`), and the constraint palette/actions
900/// (`SketchConstraintAction`, add-constraint builders, ground/construction
901/// toggles, cleanup).
902mod sketch_panel;
903/// The transform-controls gizmo: `GizmoMode`/`TransformArm`, arm/drag/apply,
904/// pose ⇄ params JSON, quaternion helpers (`rotate_euler_xyz_f64`), tests.
905mod transform_gizmo;
906/// The wire-harness surface: the document's `wireHarness` block (connection
907/// add / edit / remove / bundles toggle — checkpointed document edits that
908/// re-run the history, whose tail routes them), the applied run's routing
909/// report, the endpoint list, and the panel's hover highlight.
910mod wire_harness_ops;
911/// The PORTS surface: the ports tail's report of the last applied run — the
912/// one place the tail's resolved points, refused names and unresolved
913/// references reach a caller, the tail not being a feature.
914mod ports_ops;
915pub use ports_ops::PortRefField;
916pub use selection_ux::RefSelectField;
917// Every harness wire as a BOM line with its cut length (MF QTY), or the reason
918// the last applied run cannot give one.
919mod wire_bom;
920mod pmi_ops;
921mod pmi_section;
922pub use pmi_ops::{pmi_view_params, pmi_view_schema, world_vertex_ref, PmiModelingSnapshot, PmiViewPatch};
923// Drawing sheets: the `sheets` block, the open sheet and the projection cache.
924mod sheet_ops;
925pub(crate) mod pmi_overlay;
926/// The 3D spline-anchor editor: the resolved anchor list, the value edits
927/// (position / distances / flip / side / add / remove / reorder / detach),
928/// the anchor flavour of the transform gizmo, the port-attach flavour of the
929/// reference picker, and the direction-cage overlay.
930mod spline_edit;
931/// The NURBS Solid CONTROL-CAGE editor: the cage's points through the
932/// kernel's resolver, the multi-point selection that arms the transform
933/// gizmo at its centroid, the drag write-back, loop insertion, reset and the
934/// cage overlay.
935mod cage_edit;
936
937// Re-exports preserving the original `engine_state::*` public surface.
938pub use assembly_ops::{document_signature, ComponentInsert};
939pub use board_geometry::{
940 default_color as board_default_color, BoardBuild, BOARD_SOLID_PREFIX, SUBSTRATE_SOLID,
941 VIAS_SOLID,
942};
943pub use bom::PART_ATTRIBUTES;
944pub use component_move::ComponentMoveArm;
945pub use components::ComponentInfo;
946pub use interference::{InterferencePair, InterferenceReport};
947pub use model_io::{
948 EmbeddedOnly, PartSink, StepAssemblyImport, StepAssemblyProbe, StepAssemblyReport,
949};
950pub use selection_filter::SelectionFilter;
951pub use sketch_mode::{SketchDrag, SketchEdit};
952pub use sketch_panel::{NoticeSeverity, SketchConstraintAction, SketchEntityRow};
953pub use wire_harness_ops::ConnectionPatch;
954pub use wire_bom::{WireBomLine, WireLengthState};
955pub use spline_edit::SplineAnchorRow;
956pub use cage_edit::{CageArm, CageSelect, CageSummary, CAGE_PICK_RADIUS_PX};
957pub use transform_gizmo::{GizmoMode, TransformArm};
958pub(crate) use transform_gizmo::rotate_euler_xyz_f64;
959
960
961
962
963
964/// Reconstruction results either append to a document or remain uncommitted.
965#[derive(Clone, Copy, PartialEq, Eq)]
966pub(crate) enum MeshImportDestination {
967 Document,
968 Preview,
969}