use serde::{Deserialize, Serialize};
use crate::feature_pipeline::Env;
pub(crate) mod exports;
pub(crate) mod flexible;
pub(crate) mod infer;
pub(crate) mod lifecycle;
pub(crate) mod mapping;
pub(crate) mod constraints;
pub use exports::{
assembly_add_constraint_json, assembly_apply_document_json,
assembly_apply_inferred_constraints_json, assembly_component_entities_json, assembly_dof_json,
assembly_infer_constraints_json, assembly_inferable_types_json,
assembly_move_constraint_json, assembly_overlay_json, assembly_pose_updates_json,
assembly_drag_solve_json, assembly_frame_solve_json, assembly_remove_constraint_json, assembly_resolve_refs_json,
assembly_run_solve_json,
assembly_set_constraint_enabled_json, assembly_set_constraint_open_json,
assembly_state_json, assembly_statuses_json, assembly_update_constraint_json,
assembly_add_constraint_impl, assembly_apply_document_impl, assembly_move_constraint_impl,
assembly_drag_solve_impl, assembly_frame_solve_impl, assembly_flip_plan_json, assembly_rebase_mechanical_impl, assembly_remove_constraint_impl, assembly_run_solve_impl,
assembly_set_constraint_enabled_impl, assembly_set_constraint_open_impl,
assembly_update_constraint_impl,
assembly_session_export, assembly_session_import, assembly_session_serial, RetiredSession, SessionTransfer,
};
pub use constraints::{
constraint_hidden_params, constraint_schema_catalogue, constraint_type, ConstraintTypeDef,
CONSTRAINT_TYPES,
};
pub use mapping::transform_to_pose_params;
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct AssemblyState {
#[serde(default)]
pub constraints: Vec<ConstraintEntry>,
#[serde(default, rename = "idCounter")]
pub id_counter: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ConstraintEntry {
#[serde(rename = "type")]
pub constraint_type: String,
#[serde(rename = "inputParams", default = "empty_object")]
pub input_params: serde_json::Value,
#[serde(rename = "persistentData", default = "empty_object")]
pub persistent_data: serde_json::Value,
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub open: bool,
}
fn empty_object() -> serde_json::Value {
serde_json::Value::Object(serde_json::Map::new())
}
fn default_true() -> bool {
true
}
impl ConstraintEntry {
pub fn id(&self) -> &str {
self.input_params
.get("id")
.and_then(|value| value.as_str())
.unwrap_or("")
}
pub fn elements(&self) -> Vec<String> {
self.input_params
.get("elements")
.and_then(|value| value.as_array())
.map(|items| {
items
.iter()
.filter_map(|item| item.as_str())
.map(str::to_string)
.collect()
})
.unwrap_or_default()
}
pub fn flag(&self, key: &str) -> bool {
self.input_params
.get(key)
.and_then(|value| value.as_bool())
.unwrap_or(false)
}
pub fn number(&self, key: &str, env: &Env, default: f64) -> Result<f64, String> {
match self.input_params.get(key) {
None | Some(serde_json::Value::Null) => Ok(default),
Some(serde_json::Value::Number(number)) => number
.as_f64()
.ok_or_else(|| format!("param `{key}` is not a finite number")),
Some(serde_json::Value::String(source)) => env
.eval(source)
.map_err(|error| format!("param `{key}`: {error}")),
Some(other) => Err(format!(
"param `{key}` must be a number or expression string, found {other}"
)),
}
}
pub fn set_persistent(&mut self, key: &str, value: serde_json::Value) {
if !self.persistent_data.is_object() {
self.persistent_data = empty_object();
}
if let Some(map) = self.persistent_data.as_object_mut() {
map.insert(key.to_string(), value);
}
}
pub fn remove_persistent(&mut self, key: &str) {
if let Some(map) = self.persistent_data.as_object_mut() {
map.remove(key);
}
}
pub fn persistent(&self, key: &str) -> Option<&serde_json::Value> {
self.persistent_data.get(key)
}
pub fn set_status(&mut self, status: &str, message: impl Into<String>, satisfied: bool) {
self.set_persistent("status", serde_json::Value::String(status.to_string()));
self.set_persistent("message", serde_json::Value::String(message.into()));
self.set_persistent("satisfied", serde_json::Value::Bool(satisfied));
if status != "duplicate" {
self.remove_persistent("duplicateConstraintIDs");
self.remove_persistent("duplicateSignature");
}
if status != "satisfied" && status != "adjusted" {
self.remove_persistent("error");
self.remove_persistent("kernel");
}
if status != "conflict" {
self.remove_persistent("conflictWith");
self.remove_persistent("suppressSuggested");
}
}
pub fn status(&self) -> &str {
self.persistent("status")
.and_then(|value| value.as_str())
.unwrap_or("")
}
}
pub(crate) fn finish_history_run(
request: &crate::feature_pipeline::HistoryRequest,
scene: &mut crate::feature_pipeline::SceneMap,
env: &Env,
) {
let mut state = request.assembly.clone().unwrap_or_default();
let mut outcome = if state.constraints.is_empty() {
lifecycle::LifecycleOutcome::empty()
} else {
lifecycle::run_constraints(&mut state, scene, env)
};
follow_pattern_seeds(scene, &mut outcome);
exports::install_session(
state,
scene.clone(),
request.expressions.clone(),
request.configurator.clone(),
outcome,
);
}
fn follow_pattern_seeds(
scene: &mut crate::feature_pipeline::SceneMap,
outcome: &mut lifecycle::LifecycleOutcome,
) {
let follows: Vec<(String, crate::AffineTransform)> = scene
.components
.values()
.filter_map(|record| {
let follow = record.pattern.as_ref()?;
let seed = scene.components.get(&follow.seed)?;
let delta = match &follow.driver {
Some(driver) => {
let driver = scene.components.get(driver)?.transform;
let inverse = driver.rigid_inverse().ok()?;
let compose = crate::feature_pipeline::component::compose;
compose(&compose(&driver, &follow.delta).ok()?, &inverse).ok()?
}
None => follow.delta,
};
let target = crate::feature_pipeline::component::compose(&delta, &seed.transform).ok()?;
let moved = target
.elements
.iter()
.zip(record.transform.elements.iter())
.any(|(new, old)| (new - old).abs() > 1e-12);
moved.then(|| (record.id.clone(), target))
})
.collect();
for (id, target) in follows {
if crate::feature_pipeline::component::update_component_transform(scene, &id, target).is_ok() {
outcome.moved_solids.extend(scene.component_solids(&id));
}
}
}