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
81#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
88pub struct AssemblyState {
89 #[serde(default)]
90 pub constraints: Vec<ConstraintEntry>,
91 #[serde(default, rename = "idCounter")]
92 pub id_counter: u64,
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
100pub struct ConstraintEntry {
101 #[serde(rename = "type")]
102 pub constraint_type: String,
103 #[serde(rename = "inputParams", default = "empty_object")]
104 pub input_params: serde_json::Value,
105 #[serde(rename = "persistentData", default = "empty_object")]
106 pub persistent_data: serde_json::Value,
107 #[serde(default = "default_true")]
108 pub enabled: bool,
109 #[serde(default)]
110 pub open: bool,
111}
112
113fn empty_object() -> serde_json::Value {
114 serde_json::Value::Object(serde_json::Map::new())
115}
116
117fn default_true() -> bool {
118 true
119}
120
121impl ConstraintEntry {
122 pub fn id(&self) -> &str {
124 self.input_params
125 .get("id")
126 .and_then(|value| value.as_str())
127 .unwrap_or("")
128 }
129
130 pub fn elements(&self) -> Vec<String> {
132 self.input_params
133 .get("elements")
134 .and_then(|value| value.as_array())
135 .map(|items| {
136 items
137 .iter()
138 .filter_map(|item| item.as_str())
139 .map(str::to_string)
140 .collect()
141 })
142 .unwrap_or_default()
143 }
144
145 pub fn flag(&self, key: &str) -> bool {
147 self.input_params
148 .get(key)
149 .and_then(|value| value.as_bool())
150 .unwrap_or(false)
151 }
152
153 pub fn number(&self, key: &str, env: &Env, default: f64) -> Result<f64, String> {
156 match self.input_params.get(key) {
157 None | Some(serde_json::Value::Null) => Ok(default),
158 Some(serde_json::Value::Number(number)) => number
159 .as_f64()
160 .ok_or_else(|| format!("param `{key}` is not a finite number")),
161 Some(serde_json::Value::String(source)) => env
162 .eval(source)
163 .map_err(|error| format!("param `{key}`: {error}")),
164 Some(other) => Err(format!(
165 "param `{key}` must be a number or expression string, found {other}"
166 )),
167 }
168 }
169
170 pub fn set_persistent(&mut self, key: &str, value: serde_json::Value) {
172 if !self.persistent_data.is_object() {
173 self.persistent_data = empty_object();
174 }
175 if let Some(map) = self.persistent_data.as_object_mut() {
176 map.insert(key.to_string(), value);
177 }
178 }
179
180 pub fn remove_persistent(&mut self, key: &str) {
182 if let Some(map) = self.persistent_data.as_object_mut() {
183 map.remove(key);
184 }
185 }
186
187 pub fn persistent(&self, key: &str) -> Option<&serde_json::Value> {
189 self.persistent_data.get(key)
190 }
191
192 pub fn set_status(&mut self, status: &str, message: impl Into<String>, satisfied: bool) {
195 self.set_persistent("status", serde_json::Value::String(status.to_string()));
196 self.set_persistent("message", serde_json::Value::String(message.into()));
197 self.set_persistent("satisfied", serde_json::Value::Bool(satisfied));
198 if status != "duplicate" {
199 self.remove_persistent("duplicateConstraintIDs");
200 self.remove_persistent("duplicateSignature");
201 }
202 if status != "satisfied" && status != "adjusted" {
203 self.remove_persistent("error");
204 self.remove_persistent("kernel");
205 }
206 if status != "conflict" {
207 self.remove_persistent("conflictWith");
208 self.remove_persistent("suppressSuggested");
209 }
210 }
211
212 pub fn status(&self) -> &str {
214 self.persistent("status")
215 .and_then(|value| value.as_str())
216 .unwrap_or("")
217 }
218}
219
220pub(crate) fn finish_history_run(
230 request: &crate::feature_pipeline::HistoryRequest,
231 scene: &mut crate::feature_pipeline::SceneMap,
232 env: &Env,
233) {
234 let mut state = request.assembly.clone().unwrap_or_default();
235 let mut outcome = if state.constraints.is_empty() {
236 lifecycle::LifecycleOutcome::empty()
237 } else {
238 lifecycle::run_constraints(&mut state, scene, env)
239 };
240 follow_pattern_seeds(scene, &mut outcome);
241 exports::install_session(
242 state,
243 scene.clone(),
244 request.expressions.clone(),
245 request.configurator.clone(),
246 outcome,
247 );
248}
249
250fn follow_pattern_seeds(
255 scene: &mut crate::feature_pipeline::SceneMap,
256 outcome: &mut lifecycle::LifecycleOutcome,
257) {
258 let follows: Vec<(String, crate::AffineTransform)> = scene
259 .components
260 .values()
261 .filter_map(|record| {
262 let follow = record.pattern.as_ref()?;
263 let seed = scene.components.get(&follow.seed)?;
264 let delta = match &follow.driver {
265 Some(driver) => {
267 let driver = scene.components.get(driver)?.transform;
268 let inverse = driver.rigid_inverse().ok()?;
269 let compose = crate::feature_pipeline::component::compose;
270 compose(&compose(&driver, &follow.delta).ok()?, &inverse).ok()?
271 }
272 None => follow.delta,
273 };
274 let target = crate::feature_pipeline::component::compose(&delta, &seed.transform).ok()?;
275 let moved = target
276 .elements
277 .iter()
278 .zip(record.transform.elements.iter())
279 .any(|(new, old)| (new - old).abs() > 1e-12);
280 moved.then(|| (record.id.clone(), target))
281 })
282 .collect();
283 for (id, target) in follows {
284 if crate::feature_pipeline::component::update_component_transform(scene, &id, target).is_ok() {
285 outcome.moved_solids.extend(scene.component_solids(&id));
286 }
287 }
288}
289