Put Move/Copy Sketch Entities on the toolbar, behind a window with distances, an angle and a copy flag (#735)
Sketch Move gets a toolbar placement in Sketch > Modify and a Move/Copy tool window with X and Y distances, an angle, an optional centre point and a Create Copy flag, replacing the context-menu route that ran the command with no parameters.
This commit is contained in:
1 parent
de1479029d
commit
facff1050e
10 files changed
+319
-8
No files matched your search
@@ -6,8 +6,11 @@
|
||||
//! issue #162 item 1: "Offset does not create a constraint, the size of the offset seems
|
||||
//! random"). Mirror joins them here, which ran as a bare click flow: entities until a
|
||||
//! right-click, then one more click for the line, with nothing on screen saying what was
|
||||
//! picked. `sketch.fillet`, the three `sketch.chamfer.*` commands, the sketch patterns and
|
||||
//! `sketch.move` are the same gap and join this module later, each its own change.
|
||||
//! picked. Move joins them too, which had no toolbar placement, no tool and no dialog: its only
|
||||
//! route into the UI was the sketch context menu, and that went through the same
|
||||
//! empty-parameter fallback and reported an engine error. `sketch.fillet`, the three
|
||||
//! `sketch.chamfer.*` commands and the sketch patterns are the same gap and join this module
|
||||
//! later, each its own change.
|
||||
|
||||
use serde_json::{Map, Value, json};
|
||||
use solidcraft_engine::Sel;
|
||||
@@ -32,6 +35,10 @@ pub enum Mod {
|
||||
/// its own, so the window's whole job is to show what is picked and let a pick be changed
|
||||
/// before anything is committed.
|
||||
Mirror,
|
||||
/// `sketch.move`: how far the picked entities move along the sketch's own axes, how far they
|
||||
/// turn about the centre, and whether the originals stay behind. The command already
|
||||
/// carries the copy flag, so the Create Copy row needs no engine change.
|
||||
Move { x: String, y: String, angle: String, copy: bool },
|
||||
}
|
||||
|
||||
impl Mod {
|
||||
@@ -40,6 +47,7 @@ impl Mod {
|
||||
Mod::Scale { .. } => "SKETCH SCALE",
|
||||
Mod::Offset { .. } => "OFFSET",
|
||||
Mod::Mirror => "MIRROR",
|
||||
Mod::Move { .. } => "MOVE/COPY",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +56,7 @@ impl Mod {
|
||||
match self {
|
||||
Mod::Scale { factor } => Some(("Factor", ValueKind::Unitless, factor)),
|
||||
Mod::Offset { distance, .. } => Some(("Distance", ValueKind::Length, distance)),
|
||||
Mod::Move { x, .. } => Some(("X Distance", ValueKind::Length, x)),
|
||||
Mod::Mirror => None,
|
||||
}
|
||||
}
|
||||
@@ -59,6 +68,7 @@ fn inputs(k: &Mod) -> Vec<SelInput> {
|
||||
Mod::Offset { .. } => vec![SelInput::new("Curves", CURVES, true)],
|
||||
// AXES is the mask that takes a straight sketch curve (`selection::SelInput::accepts`).
|
||||
Mod::Mirror => vec![SelInput::new("Entities", CURVES | POINTS, true), SelInput::new("Mirror line", AXES, false)],
|
||||
Mod::Move { .. } => vec![SelInput::new("Entities", CURVES | POINTS, true), SelInput::new("Center", POINTS, false)],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +78,7 @@ pub fn start(_app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
|
||||
"sketch.scale" => Mod::Scale { factor: "2".into() },
|
||||
"sketch.offset" => Mod::Offset { distance: "2 mm".into(), chain: true, flip: false },
|
||||
"sketch.mirror" => Mod::Mirror,
|
||||
"sketch.move" => Mod::Move { x: "0 mm".into(), y: "0 mm".into(), angle: "0 deg".into(), copy: false },
|
||||
_ => return None,
|
||||
};
|
||||
let i = inputs(&k);
|
||||
@@ -96,6 +107,20 @@ pub fn rows(ui: &mut egui::Ui, k: &mut Mod) -> bool {
|
||||
}
|
||||
// Mirror has no value of its own: its inputs are the whole window.
|
||||
Mod::Mirror => {}
|
||||
Mod::Move { x, y, angle, copy } => {
|
||||
row_label(ui, "X Distance");
|
||||
enter |= field(ui, x);
|
||||
ui.end_row();
|
||||
row_label(ui, "Y Distance");
|
||||
enter |= field(ui, y);
|
||||
ui.end_row();
|
||||
row_label(ui, "Angle");
|
||||
enter |= field(ui, angle);
|
||||
ui.end_row();
|
||||
row_label(ui, "Create Copy");
|
||||
check(ui, copy);
|
||||
ui.end_row();
|
||||
}
|
||||
}
|
||||
enter
|
||||
}
|
||||
@@ -142,6 +167,22 @@ fn factor(app: &SolveApp, text: &str) -> Result<f64, String> {
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// A typed field as the command wants it: evaluated against the document's parameters, so an
|
||||
/// expression works, with a bare number read in the design's own units. An empty field is zero,
|
||||
/// the way an untouched row should leave the geometry where it is.
|
||||
fn value(app: &SolveApp, text: &str, kind: ValueKind, label: &str) -> Result<f64, String> {
|
||||
let text = text.trim();
|
||||
if text.is_empty() {
|
||||
return Ok(0.0);
|
||||
}
|
||||
let doc = &app.session.doc;
|
||||
let v = doc.eval(&doc.with_design_unit(text, kind), kind).map_err(|e| format!("{label}: {e}"))?;
|
||||
if !v.is_finite() {
|
||||
return Err(format!("{label} must be a number"));
|
||||
}
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
/// A typed distance as the command wants it: the field evaluated against the document's
|
||||
/// parameters, with the sign reversed when Flip is set. A bare number means the design's units,
|
||||
/// as a typed sketch value does everywhere else.
|
||||
@@ -155,8 +196,8 @@ fn signed_length(app: &SolveApp, text: &str, flip: bool) -> Result<f64, String>
|
||||
}
|
||||
|
||||
/// The command a sketch modify dialog makes. `sketch.scale` takes a plain evaluated number for
|
||||
/// its factor and `sketch.offset` one for its distance, not an expression string: neither has a
|
||||
/// model parameter to carry one, unlike `sketch.fillet`'s radius.
|
||||
/// its factor, `sketch.offset` one for its distance and `sketch.move` its distances and angle,
|
||||
/// not expression strings: none has a model parameter to carry one, unlike `sketch.fillet`'s radius.
|
||||
pub fn commands(app: &SolveApp, k: &Mod, inputs: &[SelInput], extra: &Map<String, Value>) -> Result<Vec<(String, Value)>, String> {
|
||||
let (cmd, mut params) = match k {
|
||||
Mod::Scale { factor: text } => {
|
||||
@@ -196,6 +237,23 @@ pub fn commands(app: &SolveApp, k: &Mod, inputs: &[SelInput], extra: &Map<String
|
||||
}
|
||||
("sketch.mirror", json!({"entities": entities, "line": line}))
|
||||
}
|
||||
Mod::Move { x, y, angle, copy } => {
|
||||
let entities = entity_ids(inputs, 0);
|
||||
if entities.is_empty() {
|
||||
return Err("Select the sketch curves or points to move first".into());
|
||||
}
|
||||
let dx = value(app, x, ValueKind::Length, "X Distance")?;
|
||||
let dy = value(app, y, ValueKind::Length, "Y Distance")?;
|
||||
let deg = value(app, angle, ValueKind::Angle, "Angle")?.to_degrees();
|
||||
if dx.abs() < 1e-9 && dy.abs() < 1e-9 && deg.abs() < 1e-9 {
|
||||
return Err("Type a distance or an angle to move by".into());
|
||||
}
|
||||
let mut p = json!({"entities": entities, "translate": [dx, dy], "angle": deg, "copy": copy});
|
||||
if let Some(center) = items(inputs, 1).first().and_then(point_id) {
|
||||
p["center"] = json!(center);
|
||||
}
|
||||
("sketch.move", p)
|
||||
}
|
||||
};
|
||||
if let Value::Object(m) = &mut params {
|
||||
for (key, value) in extra {
|
||||
|
||||
Reference in new issue
Block a user