Skip to main content

brep_render/engine_state/
simulation_refs.rs

1//! A clip's parts picked in the viewport: the Simulation pane's forms are the
2//! app's schema-driven dialogs, and a visibility or transparency clip's parts
3//! are a `reference_selection` field — the same pick every feature dialog
4//! runs ([`EngineState::begin_ref_select`] and kin). This is its lane
5//! ([`RefSelectTarget::Simulation`]): begin on a clip's field, and on Finish
6//! write the picked names into the clip through `simulation_patch` as ONE
7//! undo step. Nothing rolls (a clip names the finished assembly, which is
8//! what is on screen) and nothing re-runs.
9
10use super::{EngineState, RefSelectState, RefSelectTarget};
11use crate::engine_state::selection_filter::SelectionFilter;
12use serde_json::json;
13
14impl EngineState {
15    /// Enter reference selection for clip `clip`'s list field at `path`
16    /// (`["occurrences"]`). The filter is the schema's (`SOLID`, `COMPONENT`:
17    /// a click on component geometry answers as its component).
18    pub fn begin_ref_select_for_simulation(
19        &mut self,
20        clip: &str,
21        path: Vec<String>,
22        label: String,
23        filter: Vec<String>,
24        multiple: bool,
25        seed_names: Vec<String>,
26    ) {
27        let restore_index = self.history.rollback();
28        self.selection_filter = SelectionFilter::from_ref_filter(&filter);
29        self.ref_select = Some(RefSelectState {
30            feature_id: clip.to_string(),
31            path,
32            label,
33            filter,
34            multiple,
35            names: seed_names,
36            restore_index,
37            target: RefSelectTarget::Simulation,
38            picks: 0,
39            claimed: false,
40            finish_requested: false,
41        });
42        self.sync_ref_select_emphasis();
43    }
44
45    /// Commit a finished clip pick: every name as the component that holds it
46    /// (a solid `ACOMP2:Part` stands for `ACOMP2`; a component pick is one
47    /// already), without repeats, in pick order, written at `path`'s field
48    /// of the clip as one undo step. The clip's study is found by the clip's
49    /// id (ids are unique across the block).
50    pub(crate) fn simulation_commit_refs(&mut self, clip: &str, path: &[String], names: &[String]) -> Result<(), String> {
51        let block = self.simulation()?.ok_or("the document has no motion study")?;
52        let study = block
53            .studies
54            .iter()
55            .find(|study| study.clips.iter().any(|c| c.id == clip))
56            .map(|study| study.id.clone())
57            .ok_or_else(|| format!("no clip '{clip}'"))?;
58        let mut occurrences: Vec<String> = Vec::new();
59        for name in names {
60            let occurrence = self.component_of_solid(name).unwrap_or_else(|| name.clone());
61            if !occurrence.is_empty() && !occurrences.contains(&occurrence) {
62                occurrences.push(occurrence);
63            }
64        }
65        let field = path.last().map(String::as_str).unwrap_or("occurrences");
66        self.simulation_patch(&study, Some(clip), &json!({ field: occurrences }), None)?;
67        // A pick is a discrete change (it ends with Finish): the next edit of
68        // the same clip's field is its own undo step.
69        self.history.break_coalescing();
70        Ok(())
71    }
72}
73