Give solid.move the copy half of its Move/Copy label (#498)
solid.move takes copy: true to keep the source bodies and transform a new copy of each, with a Create Copy box in the Move dialog. Designs without it save and load exactly as before.
This commit is contained in:
1 parent
69e54f85d2
commit
7d00df0a3f
15 files changed
+395
-11
No files matched your search
@@ -1854,7 +1854,12 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
combo(ui, "sw_op", &OP_LABELS, operation);
|
||||
ui.end_row();
|
||||
}
|
||||
k @ Kind::Move { .. } => enter |= crate::dialogs_move::rows(ui, k),
|
||||
k @ Kind::Move { .. } => {
|
||||
enter |= crate::dialogs_move::rows(ui, k);
|
||||
row_label(ui, "Create Copy");
|
||||
extra_check(ui, &mut d.extra, "copy");
|
||||
ui.end_row();
|
||||
}
|
||||
Kind::Hole { diameter, depth, kind, cb_diameter, cb_depth, cs_diameter, cs_angle, opts } => {
|
||||
row_label(ui, "Extents");
|
||||
let cur = if to_object_on(&d.inputs) { 1 } else { usize::from(opts.all) * 2 };
|
||||
@@ -2195,9 +2200,12 @@ fn extra_choice(ui: &mut egui::Ui, extra: &mut serde_json::Map<String, Value>, i
|
||||
/// A checkbox stored in the dialog's extra parameters.
|
||||
fn extra_check(ui: &mut egui::Ui, extra: &mut serde_json::Map<String, Value>, key: &str) {
|
||||
let mut on = extra.get(key).and_then(Value::as_bool).unwrap_or(false);
|
||||
if ui.checkbox(&mut on, "").changed() {
|
||||
let r = ui.checkbox(&mut on, "");
|
||||
if r.changed() {
|
||||
extra.insert(key.into(), json!(on));
|
||||
}
|
||||
// The box carries no text of its own, so a scenario reaches it by its row handle.
|
||||
publish_row(&r);
|
||||
}
|
||||
|
||||
/// A value field stored in the dialog's extra parameters (`default` until edited).
|
||||
@@ -3776,9 +3784,9 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
d
|
||||
}
|
||||
FeatureKind::Move { bodies, translate, rotate_axis, angle } => {
|
||||
FeatureKind::Move { bodies, translate, rotate_axis, angle, copy } => {
|
||||
let mut d = start("solid.move")?;
|
||||
crate::dialogs_move::for_feature(&mut d, bodies, translate, *rotate_axis, angle.as_ref());
|
||||
crate::dialogs_move::for_feature(&mut d, bodies, translate, *rotate_axis, angle.as_ref(), *copy);
|
||||
d
|
||||
}
|
||||
other => {
|
||||
|
||||
@@ -226,6 +226,9 @@ pub fn params(app: &SolveApp, d: &Dialog) -> Result<Value, String> {
|
||||
p["translate"] = num(at - from);
|
||||
}
|
||||
}
|
||||
if d.extra.get("copy").and_then(Value::as_bool).unwrap_or(false) {
|
||||
p["copy"] = json!(true);
|
||||
}
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
@@ -285,7 +288,7 @@ pub fn drag(app: &SolveApp, d: &mut Dialog, g: Drag) {
|
||||
|
||||
/// The dialog for editing a move: Free Move with its translation and, about a world axis
|
||||
/// through the origin, its angle (another axis is kept as it is).
|
||||
pub fn for_feature(d: &mut Dialog, bodies: &[String], translate: &[String; 3], axis: Option<Vec3>, angle: Option<&String>) {
|
||||
pub fn for_feature(d: &mut Dialog, bodies: &[String], translate: &[String; 3], axis: Option<Vec3>, angle: Option<&String>, copy: bool) {
|
||||
let [x, y, z] = translate.clone();
|
||||
let mut k = kind(FREE);
|
||||
if let Kind::Move { x: kx, y: ky, z: kz, rx, ry, rz, .. } = &mut k {
|
||||
@@ -311,4 +314,7 @@ pub fn for_feature(d: &mut Dialog, bodies: &[String], translate: &[String; 3], a
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect();
|
||||
}
|
||||
if copy {
|
||||
d.extra.insert("copy".into(), json!(true));
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,7 @@
|
||||
"ui.counterbore": "Tiefenbohrung",
|
||||
"ui.countersink": "Senkung",
|
||||
"ui.create": "Erstellen",
|
||||
"ui.create_copy": "Kopie erstellen",
|
||||
"ui.create_sketch": "Skizze erstellen",
|
||||
"ui.current": "aktuell",
|
||||
"ui.curvature": "Krümmung",
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
"ui.counterbore": "Counterbore",
|
||||
"ui.countersink": "Countersink",
|
||||
"ui.create": "Create",
|
||||
"ui.create_copy": "Create Copy",
|
||||
"ui.create_sketch": "Create Sketch",
|
||||
"ui.current": "current",
|
||||
"ui.curvature": "Curvature",
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
"ui.counterbore": "座ぐり",
|
||||
"ui.countersink": "皿もみ",
|
||||
"ui.create": "作成",
|
||||
"ui.create_copy": "コピーを作成",
|
||||
"ui.create_sketch": "スケッチを作成",
|
||||
"ui.current": "現在",
|
||||
"ui.curvature": "曲率",
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
"ui.counterbore": "카운터보어",
|
||||
"ui.countersink": "카운터싱크",
|
||||
"ui.create": "만들기",
|
||||
"ui.create_copy": "복사본 만들기",
|
||||
"ui.create_sketch": "스케치 만들기",
|
||||
"ui.current": "현재",
|
||||
"ui.curvature": "곡률",
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
[
|
||||
{"note": "solid.move Create Copy: the box keeps the original body where it was and adds a transformed copy beside it. Escape commits nothing, a typed distance reaches the command unchanged, and reopening the feature brings the box back ticked."},
|
||||
{"call": "engine.execute", "params": {"command": "solid.box", "params": {"length": 20, "width": 20, "height": 20}}},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
|
||||
{"note": "Cancelled: the window opens, the box is ticked, a distance is typed, Escape leaves the design exactly as it was."},
|
||||
{"start": "solid.move"},
|
||||
{"click": {"world": [10, 10, 20]}},
|
||||
{"expect": {"dialog": "Move", "bodies": 1, "features": 1, "errors": 0}},
|
||||
{"click": {"handle": "dialog:Create Copy"}},
|
||||
{"click": {"handle": "dialog:X Distance"}},
|
||||
{"key": "A", "cmd": true},
|
||||
{"text": "30 mm"},
|
||||
{"expect": {"dialog": "copy\": Bool(true)"}},
|
||||
{"key": "Escape"},
|
||||
{"expect": {"dialog": null, "errors": 0, "bodies": 1, "features": 1, "volume": 8000}},
|
||||
{"expect": {"bbox": {"body": "Body1", "min": [0, 0, 0], "max": [20, 20, 20]}}},
|
||||
|
||||
{"note": "Typed: the 30 mm reaches solid.move, the original stays at the origin, the copy lands 30 mm along X with the same volume."},
|
||||
{"start": "solid.move"},
|
||||
{"click": {"world": [10, 10, 20]}},
|
||||
{"click": {"handle": "dialog:Create Copy"}},
|
||||
{"click": {"handle": "dialog:X Distance"}},
|
||||
{"key": "A", "cmd": true},
|
||||
{"text": "30 mm"},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"dialog": null, "errors": 0, "bodies": 2, "volume": 16000}},
|
||||
{"expect": {"bbox": {"body": "Body1", "min": [0, 0, 0], "max": [20, 20, 20]}}},
|
||||
{"expect": {"bbox": {"body": "Body1 (2)", "min": [30, 0, 0], "max": [50, 20, 20]}}},
|
||||
{"expect": {"body_volume": {"body": "Body1", "volume": 8000}}},
|
||||
{"expect": {"body_volume": {"body": "Body1 (2)", "volume": 8000}}},
|
||||
|
||||
{"note": "The saved flag comes back: editing the move reopens the window with the box ticked and the distance filled in."},
|
||||
{"call": "ui.editFeature", "params": {"feature": "Move1"}},
|
||||
{"expect": {"dialog": "x: \"30 mm\""}},
|
||||
{"expect": {"dialog": "copy\": Bool(true)"}},
|
||||
{"key": "Escape"},
|
||||
{"expect": {"dialog": null, "errors": 0, "bodies": 2}},
|
||||
|
||||
{"note": "Dragged: the triad moves a copy the same way, and the original is left behind."},
|
||||
{"start": "solid.move"},
|
||||
{"click": {"world": [10, 10, 20]}},
|
||||
{"click": {"handle": "dialog:Create Copy"}},
|
||||
{"drag": [{"gizmo": "x"}, {"gizmo": "x", "by": [20, 0, 0]}]},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"dialog": null, "errors": 0, "bodies": 3}},
|
||||
{"expect": {"bbox": {"body": "Body1", "min": [0, 0, 0], "max": [20, 20, 20]}}},
|
||||
{"shot": "solid_move_copy"}
|
||||
]
|
||||
Reference in new issue
Block a user