STEP: open/insert/drag-drop
Import base feature (FeatureKind::Import, timeline type BaseFeature) holding the STEP text and the file's product tree; bodies named from the file. doc.open accepts .step/.stp (any case) as a new design; Insert STEP (FusionImportCommandFromToolbar) adds one to the current design; File menu, SOLID > INSERT, drag-and-drop (insert into a non-empty design, else open), 'solvecraft part.step', MCP open. Features (fillet, hole, sketch-on-face cut, shell) work on imported bodies. Imports are cached so a file is read once per command. Measure tightens its chord tolerance for small radii. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
ed611ed7ba
commit
f3dbb12b23
18 files changed
+360
-18
No files matched your search
@@ -280,6 +280,17 @@ pub enum FeatureKind {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
angle: Option<String>,
|
||||
},
|
||||
/// A non-parametric base feature: bodies imported from a STEP file. The STEP text is kept
|
||||
/// in the design (it is the B-rep), so the design reopens without the original file.
|
||||
Import {
|
||||
/// Source file name (display only).
|
||||
file: String,
|
||||
/// The STEP (ISO 10303-21) text.
|
||||
step: String,
|
||||
/// The file's product/assembly tree, kept for components.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
components: Vec<solvecraft_kernel::StepNode>,
|
||||
},
|
||||
}
|
||||
|
||||
/// One loft section: profiles of a sketch.
|
||||
@@ -379,6 +390,7 @@ impl FeatureKind {
|
||||
FeatureKind::Draft { .. } => "DraftFeature",
|
||||
FeatureKind::Split { .. } => "SplitBodyFeature",
|
||||
FeatureKind::Move { .. } => "MoveFeature",
|
||||
FeatureKind::Import { .. } => "BaseFeature",
|
||||
}
|
||||
}
|
||||
/// Default name prefix (`Extrude` → `Extrude1`).
|
||||
@@ -405,6 +417,7 @@ impl FeatureKind {
|
||||
FeatureKind::Draft { .. } => "Draft",
|
||||
FeatureKind::Split { .. } => "Split",
|
||||
FeatureKind::Move { .. } => "Move",
|
||||
FeatureKind::Import { .. } => "Import",
|
||||
}
|
||||
}
|
||||
/// Every expression the feature uses.
|
||||
@@ -465,6 +478,7 @@ impl FeatureKind {
|
||||
v.push(a);
|
||||
}
|
||||
}
|
||||
FeatureKind::Import { .. } => {}
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
@@ -1100,5 +1100,29 @@ fn eval_feature(doc: &Document, vals: &BTreeMap<String, Value>, f: &Feature, st:
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
FeatureKind::Import { step, .. } => {
|
||||
let imp = kernel::step_import_shared(step)?;
|
||||
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 = unique_body_name(st, base);
|
||||
st.bodies.push(ModelBody::new(name, b.body.clone(), f.id));
|
||||
}
|
||||
if !imp.warnings.is_empty() {
|
||||
let n = imp.warnings.len();
|
||||
let shown: Vec<&str> = imp.warnings.iter().take(3).map(String::as_str).collect();
|
||||
let more = if n > 3 { format!(" (+{} more)", n - 3) } else { String::new() };
|
||||
*warning = Some(format!("{}{more}", shown.join("; ")));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `base`, or `base (2)`, `base (3)`… if a body already has that name.
|
||||
fn unique_body_name(st: &ModelState, base: &str) -> String {
|
||||
let base = if base.trim().is_empty() { "Body" } else { base.trim() };
|
||||
if st.body(base).is_none() {
|
||||
return base.to_string();
|
||||
}
|
||||
(2..).map(|i| format!("{base} ({i})")).find(|n| st.body(n).is_none()).unwrap_or_else(|| base.to_string())
|
||||
}
|
||||
Reference in new issue
Block a user