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:
bherbruckandClaude Opus 5.5 committed 2026-10-07 21:01:04 +00:00
1 parent ed611ed7ba
commit f3dbb12b23
18 files changed
+360 -18

No files matched your search

+4
View File
@@ -361,6 +361,10 @@ pub fn paint(p: &Painter, r: Rect, name: &str, ink: Color32, fill: Color32, acce
pen.poly(&[(4.0, 10.0), (4.0, 20.0), (16.0, 20.0), (16.0, 10.0)], pen.fill, ink);
pen.arrow((10.0, 14.0), (20.0, 4.0), a);
}
"import" => {
pen.iso_box(4.0, 12.0, 12.0, 7.0, 7.0);
pen.arrow((20.0, 3.0), (12.0, 11.0), a);
}
"home" => {
pen.poly(&[(4.0, 12.0), (12.0, 4.0), (20.0, 12.0)], pen.fill, ink);
pen.poly(&[(6.0, 12.0), (18.0, 12.0), (18.0, 20.0), (6.0, 20.0)], pen.fill, ink);
+26 -1
View File
@@ -166,6 +166,12 @@ impl SolveApp {
/// that have them, otherwise run it with defaults.
pub fn start(&mut self, id: &str) {
self.tool = None;
if id == "FusionImportCommandFromToolbar" {
if let Some(p) = self.services.pick_open.as_ref().and_then(|f| f()) {
self.insert_path(&p);
}
return;
}
if id == "SketchStop" {
let r = self.run(id, json!({}));
if r.is_ok()
@@ -240,6 +246,25 @@ impl SolveApp {
}
}
/// Insert a STEP file's bodies into the current design (an Import base feature).
pub fn insert_path(&mut self, path: &str) {
if let Ok(r) = self.run("FusionImportCommandFromToolbar", json!({ "path": path })) {
self.fit_view();
if let Some(w) = r["warnings"].as_array().filter(|w| !w.is_empty()) {
self.set_status(format!("imported with {} warning(s): {}", w.len(), w.first().and_then(Value::as_str).unwrap_or("")), false);
}
}
}
/// A dropped file: a STEP file joins a design that has features, anything else opens.
pub fn drop_path(&mut self, path: &str) {
if solvecraft_engine::io::is_step_path(path) && !self.session.doc.features.is_empty() {
self.insert_path(path);
} else {
self.open_path(path);
}
}
/// Frame the model at once (programmatic use; the toolbar animates).
pub fn fit_view(&mut self) {
self.cam_anim = None;
@@ -321,7 +346,7 @@ impl SolveApp {
for f in ctx.input(|i| i.raw.dropped_files.clone()) {
let p = f.path().to_string_lossy().to_string();
if !p.is_empty() {
self.open_path(&p);
self.drop_path(&p);
}
}
}
+1
View File
@@ -30,6 +30,7 @@ fn icon_of(k: &FeatureKind) -> &'static str {
FeatureKind::Draft { .. } => "draft",
FeatureKind::Split { .. } => "split",
FeatureKind::Move { .. } => "move",
FeatureKind::Import { .. } => "import",
}
}
+4
View File
@@ -127,6 +127,10 @@ fn file_menu(app: &mut SolveApp, ctx: &egui::Context) {
}
close = true;
}
if item(ui, "Insert STEP…", "") {
app.start("FusionImportCommandFromToolbar");
close = true;
}
if item(ui, "Save", "Ctrl+S") {
save(app);
close = true;