Files
OneCAD/crates/ui-egui/src/stand_up.rs
T
bherbruckandClaude Opus 5.5 27529d9742 Imports become components; STEP assemblies as nested components (#1386) (#1426)
* 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>
2026-10-15 11:04:49 -04:00

84 lines
3.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Standing up a Y-up file (#3). Many CAD programs save parts with Y up, so a STEP or IGES file
//! from them opens lying on its back in a Z-up design. After such a file opens, SolidCraft
//! offers to stand it up: a quarter turn about X (Y → Z) of the component the file came in as
//! (#1386), so an assembly's parts and instances turn together, in one step that can be undone. Preferences › Design › "Offer to stand up Y-up
//! files" turns the offer off; with Y up as the default orientation it isn't needed.
use serde_json::json;
use crate::SolveApp;
/// The bodies a just-opened file brought (`before`: body names before it opened, none for a
/// new design), and the import's component placement from the command's `result`: offer to
/// stand them up.
pub fn offer(app: &mut SolveApp, path: &str, before: &[String], result: Option<&serde_json::Value>) {
let io = &solidcraft_engine::io::is_step_path;
if !(io(path) || solidcraft_engine::io::is_iges_path(path)) || !app.preferences.offer_stand_up || app.preferences.up_axis != "z" {
return;
}
let bodies: Vec<String> = app.session.world_state().bodies.iter().map(|b| b.name.clone()).filter(|n| !before.contains(n)).collect();
if !bodies.is_empty() {
app.stand_up = Some(bodies);
app.stand_up_occurrence = result.and_then(|r| r.get("occurrence")).and_then(serde_json::Value::as_u64);
}
}
/// Turn the offered bodies a quarter turn about X (Y up becomes Z up).
pub fn stand_up(app: &mut SolveApp) -> Result<(), String> {
let Some(bodies) = app.stand_up.take() else { return Err("no file is waiting to be stood up".into()) };
match app.stand_up_occurrence.take() {
// The import's component turns about its parent's origin, contents and all.
Some(occurrence) => app.run("occurrence.move", json!({ "occurrence": occurrence, "axis": [1, 0, 0], "angle": "90 deg" })),
None => app.run("solid.move", json!({ "bodies": bodies, "axis": [1, 0, 0], "angle": "90 deg" })),
}
.map_err(|e| e.to_string())?;
app.animate_view("home");
Ok(())
}
/// The offer, under the toolbar.
pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
if app.stand_up.is_none() {
return;
}
let mut act: Option<&str> = None;
crate::frame::window(ctx, crate::i18n::tr_label("Stand this part up?"), crate::frame::Width::Normal)
.id(egui::Id::new("sc_stand_up"))
.collapsible(false)
.resizable(false)
.anchor(egui::Align2::CENTER_TOP, egui::vec2(0.0, 140.0))
.show(ctx, |ui| {
ui.label(crate::i18n::tr_label("Files from many CAD programs have Y up, so they open lying on their back here (Z up)."));
ui.label(crate::i18n::tr_label("Stand it up: a quarter turn about X, as one Move feature you can undo."));
ui.add_space(6.0);
ui.horizontal(|ui| {
if ui.button(crate::i18n::tr_label("Stand it up")).clicked() {
act = Some("stand");
}
if ui.button(crate::i18n::tr_label("Leave it")).clicked() {
act = Some("leave");
}
if ui.button("Don't ask again").on_hover_text("Preferences › Design turns the offer back on").clicked() {
act = Some("never");
}
});
});
match act {
Some("stand") => {
if let Err(e) = stand_up(app) {
app.set_status(e, true);
}
}
Some("leave") => {
app.stand_up = None;
app.stand_up_occurrence = None;
}
Some("never") => {
app.stand_up = None;
app.stand_up_occurrence = None;
app.preferences.offer_stand_up = false;
}
_ => {}
}
}