* Imports become components: a STEP assembly's tree as nested components (#1386) Every imported file (STEP, IGES, 3MF, STL, OBJ: inserted, dropped, opened as a design, or file.insert_step / file.insert_mesh / doc.open from scripts, the CLI and MCP) now comes in as a new component named after the file, with its Import feature inside it. A STEP assembly's products become nested components; a part placed more than once is one component with an occurrence per placement, and the Import feature skips the file's later copies (`skip`, format 21 note). The contents are held as one rigid group (rigid joints, glyphs hidden); moving an inner part is refused until component.unlock ("Unlock Contents") releases it. Y up turns the import's component. Replies report component, component_name and occurrence. Undo is one step; designs with loose imports are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep an import's rigid group hidden when the window loads the file in the background Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Import instances' skip list is format 22: format 21 ships in 0.12.0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Catalog entries for Unlock Contents Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d43c414b7b
commit
27529d9742
37 files changed
+968
-103
No files matched your search
+25
-7
@@ -4681,15 +4681,12 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::Import { step, .. } => {
|
||||
FeatureKind::Import { step, skip, .. } => {
|
||||
let imp = kernel::step_import_shared(step)?;
|
||||
let digest = crate::config::payload_digest(step);
|
||||
// Files from board and assembly tools give thousands of bodies the same name: name
|
||||
// them from a set, not by searching the bodies for every candidate (cubic).
|
||||
let mut names = BodyNames::new(st);
|
||||
for (k, b) in imp.bodies.iter().enumerate() {
|
||||
let base = f.body_names.get(k).filter(|n| !n.trim().is_empty()).unwrap_or(&b.name);
|
||||
let name = names.unique(base);
|
||||
let names = import_body_names(st, &f.body_names, &imp, skip);
|
||||
for (b, name) in imp.bodies.iter().zip(names) {
|
||||
let Some(name) = name else { continue };
|
||||
let b = crate::lineage::numbered_tool(b.body.clone().with_import_certificate(&digest, f.id)?);
|
||||
st.bodies.push(ModelBody::traced_new(name, b, f.id));
|
||||
}
|
||||
@@ -4722,6 +4719,27 @@ fn mesh_bodies(meshes: &[crate::MeshData]) -> Result<Vec<(String, Body)>> {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// The names an Import feature gives the file's bodies, added after the bodies of `st`, in the
|
||||
/// file's order: `None` for a body it skips (a later instance of a repeated part). `body_names`
|
||||
/// are the feature's stored names (by index), else the file's own.
|
||||
pub fn import_body_names(st: &ModelState, body_names: &[String], imp: &kernel::StepImport, skip: &[u64]) -> Vec<Option<String>> {
|
||||
let skip: std::collections::HashSet<u64> = skip.iter().copied().collect();
|
||||
// Files from board and assembly tools give thousands of bodies the same name: name them
|
||||
// from a set, not by searching the bodies for every candidate (cubic).
|
||||
let mut names = BodyNames::new(st);
|
||||
imp.bodies
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(k, b)| {
|
||||
if skip.contains(&(k as u64)) {
|
||||
return None;
|
||||
}
|
||||
let base = body_names.get(k).filter(|n| !n.trim().is_empty()).unwrap_or(&b.name);
|
||||
Some(names.unique(base))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// [`unique_body_name`] for many bodies in a row: the same names, in time linear in the bodies.
|
||||
struct BodyNames {
|
||||
taken: std::collections::HashSet<String>,
|
||||
|
||||
Reference in new issue
Block a user