brep_kernel/feature_pipeline/
assembly.rs1use serde::{Deserialize, Serialize};
43
44use crate::feature_pipeline::Env;
45
46pub(crate) mod exports;
47pub(crate) mod flexible;
48pub(crate) mod infer;
49pub(crate) mod lifecycle;
50pub(crate) mod mapping;
51pub(crate) mod constraints;
52
53pub use exports::{
54 assembly_add_constraint_json, assembly_apply_document_json,
55 assembly_apply_inferred_constraints_json, assembly_component_entities_json, assembly_dof_json,
56 assembly_infer_constraints_json, assembly_inferable_types_json,
57 assembly_move_constraint_json, assembly_overlay_json, assembly_pose_updates_json,
58 assembly_drag_solve_json, assembly_frame_solve_json, assembly_remove_constraint_json, assembly_resolve_refs_json,
59 assembly_run_solve_json,
60 assembly_set_constraint_enabled_json, assembly_set_constraint_open_json,
61 assembly_state_json, assembly_statuses_json, assembly_update_constraint_json,
62 assembly_add_constraint_impl, assembly_apply_document_impl, assembly_move_constraint_impl,
65 assembly_drag_solve_impl, assembly_frame_solve_impl, assembly_flip_plan_json, assembly_rebase_mechanical_impl, assembly_remove_constraint_impl, assembly_run_solve_impl,
66 assembly_set_constraint_enabled_impl, assembly_set_constraint_open_impl,
67 assembly_update_constraint_impl,
68 assembly_session_export, assembly_session_import, assembly_session_serial, RetiredSession, SessionTransfer,
70};
71pub use constraints::{
72 constraint_hidden_params, constraint_schema_catalogue, constraint_type, ConstraintTypeDef,
73 CONSTRAINT_TYPES,
74};
75pub use mapping::transform_to_pose_params;
80
81pub const FIXED_IS_NOT_A_CONSTRAINT: &str = "Fixed is not a constraint: a part is fixed by its own Fixed setting \
84(the component's `isFixed`). Use component_set_fixed (Fix/Unfix on the component's row or menu), or give the \
85component `isFixed: true` in its params";
86
87#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
94pub struct AssemblyState {
95 #[serde(default)]
96 pub constraints: Vec<ConstraintEntry>,
97 #[serde(default, rename = "idCounter")]
98 pub id_counter: u64,
99}
100
101#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
106pub struct ConstraintEntry {
107 #[serde(rename = "type")]
108 pub constraint_type: String,
109 #[serde(rename = "inputParams", default = "empty_object")]
110 pub input_params: serde_json::Value,
111 #[serde(rename = "persistentData", default = "empty_object")]
112 pub persistent_data: serde_json::Value,
113 #[serde(default = "default_true")]
114 pub enabled: bool,
115 #[serde(default)]
116 pub open: bool,
117}
118
119fn empty_object() -> serde_json::Value {
120 serde_json::Value::Object(serde_json::Map::new())
121}
122
123fn default_true() -> bool {
124 true
125}
126
127impl ConstraintEntry {
128 pub fn id(&self) -> &str {
130 self.input_params
131 .get("id")
132 .and_then(|value| value.as_str())
133 .unwrap_or("")
134 }
135
136 pub fn elements(&self) -> Vec<String> {
138 self.input_params
139 .get("elements")
140 .and_then(|value| value.as_array())
141 .map(|items| {
142 items
143 .iter()
144 .filter_map(|item| item.as_str())
145 .map(str::to_string)
146 .collect()
147 })
148 .unwrap_or_default()
149 }
150
151 pub fn flag(&self, key: &str) -> bool {
153 self.input_params
154 .get(key)
155 .and_then(|value| value.as_bool())
156 .unwrap_or(false)
157 }
158
159 pub fn number(&self, key: &str, env: &Env, default: f64) -> Result<f64, String> {
162 match self.input_params.get(key) {
163 None | Some(serde_json::Value::Null) => Ok(default),
164 Some(serde_json::Value::Number(number)) => number
165 .as_f64()
166 .ok_or_else(|| format!("param `{key}` is not a finite number")),
167 Some(serde_json::Value::String(source)) => env
168 .eval(source)
169 .map_err(|error| format!("param `{key}`: {error}")),
170 Some(other) => Err(format!(
171 "param `{key}` must be a number or expression string, found {other}"
172 )),
173 }
174 }
175
176 pub fn set_persistent(&mut self, key: &str, value: serde_json::Value) {
178 if !self.persistent_data.is_object() {
179 self.persistent_data = empty_object();
180 }
181 if let Some(map) = self.persistent_data.as_object_mut() {
182 map.insert(key.to_string(), value);
183 }
184 }
185
186 pub fn remove_persistent(&mut self, key: &str) {
188 if let Some(map) = self.persistent_data.as_object_mut() {
189 map.remove(key);
190 }
191 }
192
193 pub fn persistent(&self, key: &str) -> Option<&serde_json::Value> {
195 self.persistent_data.get(key)
196 }
197
198 pub fn set_status(&mut self, status: &str, message: impl Into<String>, satisfied: bool) {
201 self.set_persistent("status", serde_json::Value::String(status.to_string()));
202 self.set_persistent("message", serde_json::Value::String(message.into()));
203 self.set_persistent("satisfied", serde_json::Value::Bool(satisfied));
204 if status != "duplicate" {
205 self.remove_persistent("duplicateConstraintIDs");
206 self.remove_persistent("duplicateSignature");
207 }
208 if status != "satisfied" && status != "adjusted" {
209 self.remove_persistent("error");
210 self.remove_persistent("kernel");
211 }
212 if status != "conflict" {
213 self.remove_persistent("conflictWith");
214 self.remove_persistent("suppressSuggested");
215 }
216 }
217
218 pub fn status(&self) -> &str {
220 self.persistent("status")
221 .and_then(|value| value.as_str())
222 .unwrap_or("")
223 }
224}
225
226pub(crate) fn finish_history_run(
236 request: &crate::feature_pipeline::HistoryRequest,
237 scene: &mut crate::feature_pipeline::SceneMap,
238 env: &Env,
239) {
240 let mut state = request.assembly.clone().unwrap_or_default();
241 let mut outcome = if state.constraints.is_empty() {
242 lifecycle::LifecycleOutcome::empty()
243 } else {
244 lifecycle::run_constraints(&mut state, scene, env)
245 };
246 follow_pattern_seeds(scene, &mut outcome);
247 exports::install_session(
248 state,
249 scene.clone(),
250 request.expressions.clone(),
251 request.configurator.clone(),
252 outcome,
253 );
254}
255
256fn follow_pattern_seeds(
261 scene: &mut crate::feature_pipeline::SceneMap,
262 outcome: &mut lifecycle::LifecycleOutcome,
263) {
264 let follows: Vec<(String, crate::AffineTransform)> = scene
265 .components
266 .values()
267 .filter_map(|record| {
268 let follow = record.pattern.as_ref()?;
269 let seed = scene.components.get(&follow.seed)?;
270 let delta = match &follow.driver {
271 Some(driver) => {
273 let driver = scene.components.get(driver)?.transform;
274 let inverse = driver.rigid_inverse().ok()?;
275 let compose = crate::feature_pipeline::component::compose;
276 compose(&compose(&driver, &follow.delta).ok()?, &inverse).ok()?
277 }
278 None => follow.delta,
279 };
280 let target = crate::feature_pipeline::component::compose(&delta, &seed.transform).ok()?;
281 let moved = target
282 .elements
283 .iter()
284 .zip(record.transform.elements.iter())
285 .any(|(new, old)| (new - old).abs() > 1e-12);
286 moved.then(|| (record.id.clone(), target))
287 })
288 .collect();
289 for (id, target) in follows {
290 if crate::feature_pipeline::component::update_component_transform(scene, &id, target).is_ok() {
291 outcome.moved_solids.extend(scene.component_solids(&id));
292 }
293 }
294}
295