component.insert_design (a saved part as a linked component) had no button, so an assembly of separately saved parts looked impossible. It is now on the ASSEMBLE panel of every tab and in the File menu; it asks for the file, and a STEP or mesh file picked there is inserted as Insert STEP or Insert Mesh would. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4823c293dd
commit
b156b5e09c
5 files changed
+64
-3
No files matched your search
@@ -16,7 +16,7 @@ use crate::params::{bad, str_, vec3};
|
||||
use crate::{EngineError, Result, Session};
|
||||
|
||||
pub static COMMANDS: &[CommandSpec] = &[
|
||||
CommandSpec::new("component.insert_design", "Insert Design", insert_design).icon("component").params(
|
||||
CommandSpec::new("component.insert_design", "Insert Design", insert_design).at("SOLID", "ASSEMBLE").icon("component").params(
|
||||
"path: a .solidcraft design → a new component linked to it, holding its solid bodies as placed in that design; \
|
||||
name? (default: the design's name); parent?: component id or name (default: the active one); translate?: [x,y,z]",
|
||||
),
|
||||
|
||||
@@ -679,6 +679,21 @@ impl SolveApp {
|
||||
self.pick_open_command(id, json!({}));
|
||||
return;
|
||||
}
|
||||
if id == "component.insert_design" {
|
||||
// Another design becomes a linked component; a STEP or mesh file picked here is
|
||||
// inserted the way Insert STEP or Insert Mesh would (#1240).
|
||||
self.pick_open(|app, path| {
|
||||
let Some(path) = path else { return };
|
||||
if is_design_path(&path) {
|
||||
if app.run("component.insert_design", json!({ "path": path })).is_ok() {
|
||||
app.fit_view();
|
||||
}
|
||||
} else {
|
||||
app.insert_path_async(&path);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
if matches!(id, "file.insert_step" | "file.insert_mesh") {
|
||||
self.pick_open(|app, path| {
|
||||
if let Some(path) = path {
|
||||
@@ -1419,3 +1434,10 @@ pub fn now_ms() -> f64 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod picker_test_support;
|
||||
|
||||
/// A SolidCraft design file (`.solidcraft`, or a design saved as JSON), as opposed to a STEP or
|
||||
/// mesh file.
|
||||
fn is_design_path(path: &str) -> bool {
|
||||
let ext = std::path::Path::new(path).extension().and_then(|e| e.to_str()).unwrap_or("").to_ascii_lowercase();
|
||||
matches!(ext.as_str(), "solidcraft" | "solvecraft" | "json")
|
||||
}
|
||||
@@ -390,6 +390,10 @@ fn file_menu(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
app.start("file.insert_mesh");
|
||||
close = true;
|
||||
}
|
||||
if item_if(ui, &format!("{}…", app.tr_label("Insert Design")), "", shown) {
|
||||
app.start("component.insert_design");
|
||||
close = true;
|
||||
}
|
||||
if item_if(ui, app.tr("ui.save"), "Ctrl+S", shown) {
|
||||
save(app);
|
||||
close = true;
|
||||
|
||||
@@ -11,6 +11,7 @@ const ASSEMBLE: &[&str] = &[
|
||||
"component.create",
|
||||
"joint.create",
|
||||
"joint.as_built",
|
||||
"component.insert_design",
|
||||
"joint.origin",
|
||||
"joint.rigid_group",
|
||||
"joint.drive",
|
||||
@@ -212,4 +213,36 @@ mod tests {
|
||||
assert!(ids("PLASTIC", "MODIFY").contains(&"solid.shell"));
|
||||
assert!(panels("PLASTIC").is_some_and(|p| p.first() == Some(&"SETUP")));
|
||||
}
|
||||
|
||||
/// #1240: a saved part reaches an assembly from the ASSEMBLE panel (and the File menu) as a
|
||||
/// linked component, and a STEP file picked there is inserted as Insert STEP would.
|
||||
#[test]
|
||||
fn insert_design_is_on_the_assemble_panel_and_inserts_a_saved_part() {
|
||||
use serde_json::json;
|
||||
for tab in ["SOLID", "SHEET METAL", "PLASTIC"] {
|
||||
assert!(ids(tab, "ASSEMBLE").contains(&"component.insert_design"), "{tab}");
|
||||
}
|
||||
assert!(!ids("SOLID", "ASSEMBLE")[..3].contains(&"component.insert_design"), "Component, Joint and As-built Joint keep the big buttons");
|
||||
let mut h = crate::scenario::Harness::new();
|
||||
let part = h.dir.join("part1.solidcraft");
|
||||
let mut s = solidcraft_engine::Session::default();
|
||||
s.execute("solid.box", &json!({"length": 10, "width": 20, "height": 30})).unwrap();
|
||||
s.execute("file.save", &json!({"path": part})).unwrap();
|
||||
s.execute("file.export", &json!({"path": h.dir.join("part2.step")})).unwrap();
|
||||
h.step(&json!({"choose_file": "part1.solidcraft"})).unwrap();
|
||||
h.step(&json!({"start": "component.insert_design"})).unwrap();
|
||||
h.frames(3);
|
||||
let links = h.app.session.execute("component.links", &json!({})).unwrap();
|
||||
assert_eq!(links["links"][0]["name"], "part1", "{links}");
|
||||
assert_eq!(h.app.session.world_state().bodies.len(), 1);
|
||||
h.step(&json!({"choose_file": "part2.step"})).unwrap();
|
||||
h.step(&json!({"start": "component.insert_design"})).unwrap();
|
||||
for _ in 0..600 {
|
||||
if h.app.session.world_state().bodies.len() == 2 {
|
||||
break;
|
||||
}
|
||||
h.frames(1);
|
||||
}
|
||||
assert_eq!(h.app.session.world_state().bodies.len(), 2, "the STEP file's body is inserted");
|
||||
}
|
||||
}
|
||||
@@ -64,8 +64,10 @@ instead, so two origins given at one point with the same z stay as they are. For
|
||||
drive many steps, `quiet: true` leaves the per-part degrees of freedom out of joint answers, and
|
||||
`motion.play` with `quiet: true` gives the poses of only the parts that moved.
|
||||
|
||||
**Insert Design** (`component.insert_design {path, translate?}`) adds a component linked to
|
||||
another saved design, so each part can live in its own document. The part's solid bodies come
|
||||
**Insert Design** (`component.insert_design {path, translate?}`, on the **ASSEMBLE** panel and
|
||||
in the **File** menu) adds a component linked to another saved design, so each part can live in
|
||||
its own document. A STEP or mesh file picked there is inserted as **Insert STEP** or **Insert
|
||||
Mesh** would. The part's solid bodies come
|
||||
in as they sit in that design, and they are kept in the assembly, so it opens even without the
|
||||
part file. The link is stored relative to the assembly's folder once the assembly is saved.
|
||||
After the part file changes, **Update Links** (`component.update_links`) reads it again.
|
||||
|
||||
Reference in new issue
Block a user