Command ids are SolveCraft's own: 173 ids that were another product's internal command ids become dotted, lower-case ids (<area>.<thing>[.<variant>]: solid.extrude, sketch.rectangle.two_point, sheet.flange, joint.drive, ...), in the registry, toolbar, dialogs, shortcuts, palette, control channel, MCP, examples, scenarios and docs
The old ids live only in crates/engine/src/legacy_ids.rs (the legacy migration table): find_command maps them, so old scripts, recipes and control calls keep working, and saved shortcuts, recent and pinned commands are migrated on load (designs store no command ids). Parity maps the observed catalog ids to ours through xtask/data/fusion-ids.tsv. The agent rule now reads: our own dotted id, never another product's. Cargo.toml's repository points at github.com/bherbruck/solvecraft. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
740360d7c3
commit
d10312b7fd
158 files changed
+3611
-3205
No files matched your search
@@ -125,9 +125,7 @@ pub fn folder_items(app: &SolveApp, component: u64, folder: &str) -> Vec<Item> {
|
||||
let mut v = Vec::new();
|
||||
if folder == "bodies" {
|
||||
let bodies: Vec<String> = all.iter().map(|e| e.key.clone()).collect();
|
||||
v.push(
|
||||
Item::action("FusionCreateComponentsFromBodiesCommand", "Create Components from Bodies", "component").with(json!({ "bodies": bodies })),
|
||||
);
|
||||
v.push(Item::action("component.from_bodies", "Create Components from Bodies", "component").with(json!({ "bodies": bodies })));
|
||||
}
|
||||
v.push(
|
||||
Item::action("ui.newGroup", if folder == "sketches" { "New Sketch Group" } else { "New Group" }, "folder")
|
||||
@@ -578,7 +576,7 @@ fn doc_settings(app: &mut SolveApp, ui: &mut egui::Ui, depth: usize) {
|
||||
}
|
||||
let params = format!("Parameters ({})", app.session.doc.params.len());
|
||||
if draw_row(ui, ui.id().with("params"), &Row { depth: depth + 1, icon: "params", label: ¶ms, ..Default::default() }).clicked {
|
||||
app.start("ChangeParameterCommand");
|
||||
app.start("parameters.change");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1352,7 +1350,7 @@ fn occurrence_panel(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
gizmo(app, ctx, &mut m);
|
||||
if (m.translate, m.angle_deg) != before {
|
||||
// The pending move is the typed offset from where the occurrence is now.
|
||||
let _ = app.session.execute("AsBuiltPositionsCmd", &json!({}));
|
||||
let _ = app.session.execute("component.revert_position", &json!({}));
|
||||
let _ = app.run(
|
||||
"occurrence.move",
|
||||
json!({ "occurrence": m.occurrence, "translate": m.translate, "axis": [0, 0, 1], "angle": m.angle_deg.to_radians(), "capture": false }),
|
||||
@@ -1360,15 +1358,15 @@ fn occurrence_panel(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
}
|
||||
match action {
|
||||
Some("capture") => {
|
||||
let _ = app.run("SnapshotCmd", json!({}));
|
||||
let _ = app.run("component.capture_position", json!({}));
|
||||
app.tree.occurrence_move = None;
|
||||
}
|
||||
Some(_) => {
|
||||
let _ = app.run("AsBuiltPositionsCmd", json!({}));
|
||||
let _ = app.run("component.revert_position", json!({}));
|
||||
app.tree.occurrence_move = None;
|
||||
}
|
||||
None if !open => {
|
||||
let _ = app.run("AsBuiltPositionsCmd", json!({}));
|
||||
let _ = app.run("component.revert_position", json!({}));
|
||||
app.tree.occurrence_move = None;
|
||||
}
|
||||
None => app.tree.occurrence_move = Some(m),
|
||||
@@ -1445,7 +1443,7 @@ pub const SAVE: &str = "__save";
|
||||
/// that capture, revert or keep moving.
|
||||
pub fn needs_capture(app: &SolveApp, what: &str) -> bool {
|
||||
!app.session.pending_moves.is_empty()
|
||||
&& !matches!(what, "SnapshotCmd" | "AsBuiltPositionsCmd" | "occurrence.move" | "UndoCommand" | "RedoCommand")
|
||||
&& !matches!(what, "component.capture_position" | "component.revert_position" | "occurrence.move" | "edit.undo" | "edit.redo")
|
||||
&& app.tree.capture_prompt.is_none()
|
||||
}
|
||||
|
||||
@@ -1454,8 +1452,8 @@ pub fn needs_capture(app: &SolveApp, what: &str) -> bool {
|
||||
pub fn answer_capture(app: &mut SolveApp, action: &str) -> Result<(), String> {
|
||||
let Some(what) = app.tree.capture_prompt.clone() else { return Err("no Capture Position question is open".into()) };
|
||||
match action {
|
||||
"capture" => drop(app.run("SnapshotCmd", json!({}))),
|
||||
"revert" => drop(app.run("AsBuiltPositionsCmd", json!({}))),
|
||||
"capture" => drop(app.run("component.capture_position", json!({}))),
|
||||
"revert" => drop(app.run("component.revert_position", json!({}))),
|
||||
"cancel" => {
|
||||
app.tree.capture_prompt = None;
|
||||
return Ok(());
|
||||
|
||||
@@ -113,8 +113,8 @@ impl Item {
|
||||
fn cmd(app: &SolveApp, id: &str, label: &str) -> Item {
|
||||
let info = solvecraft_engine::find_command(id).map(|c| c.info(&app.session));
|
||||
let shortcut = match id {
|
||||
"UndoCommand" => "Ctrl+Z".to_string(),
|
||||
"RedoCommand" => "Ctrl+Y".to_string(),
|
||||
"edit.undo" => "Ctrl+Z".to_string(),
|
||||
"edit.redo" => "Ctrl+Y".to_string(),
|
||||
_ => crate::keymap::effective(app, id).unwrap_or_default(),
|
||||
};
|
||||
Item {
|
||||
@@ -257,23 +257,23 @@ pub fn radial_items(app: &SolveApp) -> Vec<Item> {
|
||||
return vec![
|
||||
repeat,
|
||||
delete,
|
||||
cmd(app, "DrawPolyline", "Line"),
|
||||
cmd(app, "ShapeRectangleTwoPoint", "Rectangle"),
|
||||
cmd(app, "sketch.line", "Line"),
|
||||
cmd(app, "sketch.rectangle.two_point", "Rectangle"),
|
||||
act("ui.finishSketch", "Finish Sketch", "finish"),
|
||||
cmd(app, "CircleCenterRadius", "Circle"),
|
||||
cmd(app, "UndoCommand", "Undo").on(!app.session.undo.is_empty()),
|
||||
cmd(app, "SketchDimension", "Sketch Dimension"),
|
||||
cmd(app, "sketch.circle.center", "Circle"),
|
||||
cmd(app, "edit.undo", "Undo").on(!app.session.undo.is_empty()),
|
||||
cmd(app, "sketch.dimension", "Sketch Dimension"),
|
||||
];
|
||||
}
|
||||
vec![
|
||||
repeat,
|
||||
delete,
|
||||
cmd(app, "FusionPressPullCommand", "Press Pull"),
|
||||
cmd(app, "FusionHoleCommand", "Hole"),
|
||||
cmd(app, "SketchCreate", "Create Sketch"),
|
||||
cmd(app, "Extrude", "Extrude"),
|
||||
cmd(app, "UndoCommand", "Undo").on(!app.session.undo.is_empty()),
|
||||
cmd(app, "FusionMoveCommand", "Move/Copy"),
|
||||
cmd(app, "solid.press_pull", "Press Pull"),
|
||||
cmd(app, "solid.hole", "Hole"),
|
||||
cmd(app, "sketch.create", "Create Sketch"),
|
||||
cmd(app, "solid.extrude", "Extrude"),
|
||||
cmd(app, "edit.undo", "Undo").on(!app.session.undo.is_empty()),
|
||||
cmd(app, "solid.move", "Move/Copy"),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -330,23 +330,23 @@ fn viewport_items(app: &SolveApp) -> Vec<Item> {
|
||||
if app.session.active_sketch.is_some() {
|
||||
v.extend(sketch_entity_items(app));
|
||||
v.push(Item::sep());
|
||||
v.push(cmd(app, "TrimSketchCmd", "Trim"));
|
||||
v.push(cmd(app, "Offset", "Offset"));
|
||||
v.push(cmd(app, "sketch.trim", "Trim"));
|
||||
v.push(cmd(app, "sketch.offset", "Offset"));
|
||||
v.push(Item::sep());
|
||||
v.push(act("ui.finishSketch", "Finish Sketch", "finish"));
|
||||
v.push(Item::sep());
|
||||
v.push(cmd(app, "UndoCommand", "Undo").on(!app.session.undo.is_empty()));
|
||||
v.push(cmd(app, "RedoCommand", "Redo").on(!app.session.redo.is_empty()));
|
||||
v.push(cmd(app, "edit.undo", "Undo").on(!app.session.undo.is_empty()));
|
||||
v.push(cmd(app, "edit.redo", "Redo").on(!app.session.redo.is_empty()));
|
||||
return v;
|
||||
}
|
||||
if has(|s| matches!(s, Sel::Face { .. })) {
|
||||
let face = sel.iter().find_map(|s| if let Sel::Face { body, .. } = s { Some(body.clone()) } else { None }).unwrap_or_default();
|
||||
let (feature, sketch) = body_feature_and_sketch(app, &face);
|
||||
let hidden = app.ui.hidden_bodies.contains(&face);
|
||||
v.push(cmd(app, "SketchCreate", "Create Sketch"));
|
||||
v.push(cmd(app, "Extrude", "Extrude"));
|
||||
v.push(cmd(app, "ConstructionPlaneOffsetFromPlaneCommand", "Offset Plane"));
|
||||
v.push(cmd(app, "FusionShellBodyCommand", "Shell"));
|
||||
v.push(cmd(app, "sketch.create", "Create Sketch"));
|
||||
v.push(cmd(app, "solid.extrude", "Extrude"));
|
||||
v.push(cmd(app, "construct.plane.offset", "Offset Plane"));
|
||||
v.push(cmd(app, "solid.shell", "Shell"));
|
||||
v.push(act("ui.editFeature", "Edit Feature", "").with(json!({ "feature": feature })).on(feature.is_some()));
|
||||
v.push(act("ui.editSketch", "Edit Profile Sketch", "sketch").with(json!({ "sketch": sketch })).on(sketch.is_some()));
|
||||
v.push(Item::sep());
|
||||
@@ -359,13 +359,13 @@ fn viewport_items(app: &SolveApp) -> Vec<Item> {
|
||||
v.push(act("ui.findBrowser", "Find in Browser", "").with(json!({ "body": face })));
|
||||
v.push(act("ui.findTimeline", "Find in Timeline", "").with(json!({ "body": face })));
|
||||
v.push(Item::sep());
|
||||
v.push(cmd(app, "FusionPressPullCommand", "Press Pull"));
|
||||
v.push(cmd(app, "MeasureCommand", "Measure"));
|
||||
v.push(cmd(app, "solid.press_pull", "Press Pull"));
|
||||
v.push(cmd(app, "inspect.measure", "Measure"));
|
||||
} else if has(|s| matches!(s, Sel::Edge { .. })) {
|
||||
let body = sel.iter().find_map(|s| if let Sel::Edge { body, .. } = s { Some(body.clone()) } else { None }).unwrap_or_default();
|
||||
let hidden = app.ui.hidden_bodies.contains(&body);
|
||||
v.push(cmd(app, "FusionFilletEdgesCommand", "Fillet"));
|
||||
v.push(cmd(app, "FusionChamferCommand", "Chamfer"));
|
||||
v.push(cmd(app, "solid.fillet", "Fillet"));
|
||||
v.push(cmd(app, "solid.chamfer", "Chamfer"));
|
||||
v.push(Item::sep());
|
||||
v.push(act("ui.delete", "Delete", "delete").key("Del"));
|
||||
v.push(act(if hidden { "ui.show" } else { "ui.hide" }, "Show/Hide", "eye").key("V").with(json!({ "bodies": [body] })));
|
||||
@@ -373,12 +373,12 @@ fn viewport_items(app: &SolveApp) -> Vec<Item> {
|
||||
v.push(act("ui.findBrowser", "Find in Browser", "").with(json!({ "body": body })));
|
||||
v.push(Item::sep());
|
||||
v.push(act("ui.tangentChain", "Select Tangent Chain", ""));
|
||||
v.push(cmd(app, "MeasureCommand", "Measure"));
|
||||
v.push(cmd(app, "inspect.measure", "Measure"));
|
||||
} else if has(|s| matches!(s, Sel::Body { .. })) {
|
||||
v.extend(body_items(app, &sel_bodies(app), false));
|
||||
} else if has(|s| matches!(s, Sel::Profile { .. })) {
|
||||
v.push(cmd(app, "Extrude", "Extrude"));
|
||||
v.push(cmd(app, "Revolve", "Revolve"));
|
||||
v.push(cmd(app, "solid.extrude", "Extrude"));
|
||||
v.push(cmd(app, "solid.revolve", "Revolve"));
|
||||
} else {
|
||||
// Empty space.
|
||||
v.push(act("ui.nav", "Pan", "pan").with(json!({ "mode": "pan" })));
|
||||
@@ -391,11 +391,11 @@ fn viewport_items(app: &SolveApp) -> Vec<Item> {
|
||||
v.push(act("ui.showAll", "Show All", "eye").on(anything_hidden));
|
||||
v.push(act("ui.showAll", "Unisolate", "").on(!app.ui.hidden_bodies.is_empty()));
|
||||
v.push(Item::sep());
|
||||
v.push(cmd(app, "Extrude", "Extrude"));
|
||||
v.push(cmd(app, "FusionFilletEdgesCommand", "Fillet"));
|
||||
v.push(cmd(app, "solid.extrude", "Extrude"));
|
||||
v.push(cmd(app, "solid.fillet", "Fillet"));
|
||||
v.push(Item::sep());
|
||||
v.push(cmd(app, "UndoCommand", "Undo").on(!app.session.undo.is_empty()));
|
||||
v.push(cmd(app, "RedoCommand", "Redo").on(!app.session.redo.is_empty()));
|
||||
v.push(cmd(app, "edit.undo", "Undo").on(!app.session.undo.is_empty()));
|
||||
v.push(cmd(app, "edit.redo", "Redo").on(!app.session.redo.is_empty()));
|
||||
v.push(Item::sep());
|
||||
if app.session.section.is_some() {
|
||||
v.push(act("ui.unsection", "Remove Section", "section"));
|
||||
@@ -418,7 +418,7 @@ fn body_items(app: &SolveApp, bodies: &[String], browser: bool) -> Vec<Item> {
|
||||
let items = json!(bodies.iter().map(|b| json!({"type": "body", "name": b})).collect::<Vec<_>>());
|
||||
let mut v = vec![
|
||||
act("ui.moveBodies", "Move/Copy", "move").key("M").with(json!({ "bodies": names })).on(!locked),
|
||||
cmd(app, "FusionCreateComponentsFromBodiesCommand", "Create Components from Bodies").with(json!({ "bodies": names })),
|
||||
cmd(app, "component.from_bodies", "Create Components from Bodies").with(json!({ "bodies": names })),
|
||||
Item::sep(),
|
||||
act("ui.material", "Physical Material", "").with(json!({ "bodies": names })),
|
||||
act("ui.appearance", "Appearance", "").key("A").on(false),
|
||||
@@ -428,7 +428,7 @@ fn body_items(app: &SolveApp, bodies: &[String], browser: bool) -> Vec<Item> {
|
||||
act("ui.export", "Export…", "export").with(json!({ "bodies": names })),
|
||||
Item::sep(),
|
||||
act("ui.delete", "Delete", "delete").key("Del").with(json!({ "items": items })).on(!locked),
|
||||
cmd(app, "SoftDeleteCommand", "Remove").with(json!({ "bodies": names })).on(!locked),
|
||||
cmd(app, "solid.remove", "Remove").with(json!({ "bodies": names })).on(!locked),
|
||||
act("ui.rename", "Rename", "").with(json!({ "body": bodies.first() })).on(one),
|
||||
Item::sep(),
|
||||
act(if all_hidden { "ui.show" } else { "ui.hide" }, "Show/Hide", "eye").key("V").with(json!({ "bodies": names })),
|
||||
@@ -499,19 +499,15 @@ fn component_items(app: &SolveApp, id: u64) -> Vec<Item> {
|
||||
v.push(act("ui.activate", "Activate", "component").with(json!({ "component": id })).on(!active));
|
||||
v.push(cmd(app, "occurrence.ground", if grounded { "Unground" } else { "Ground" }).with(json!({ "occurrence": oid, "grounded": !grounded })));
|
||||
v.push(act("ui.moveOccurrence", "Move/Copy", "move").key("M").with(json!({ "occurrence": oid })).on(!grounded && occ.is_some()));
|
||||
v.push(cmd(app, "SnapshotCmd", "Capture Position").on(!app.session.pending_moves.is_empty()));
|
||||
v.push(cmd(app, "AsBuiltPositionsCmd", "Revert Position").on(!app.session.pending_moves.is_empty()));
|
||||
v.push(cmd(app, "component.capture_position", "Capture Position").on(!app.session.pending_moves.is_empty()));
|
||||
v.push(cmd(app, "component.revert_position", "Revert Position").on(!app.session.pending_moves.is_empty()));
|
||||
v.push(Item::sep());
|
||||
} else {
|
||||
v.push(act("ui.activate", "Activate", "component").with(json!({ "component": id })).on(!active));
|
||||
}
|
||||
v.push(cmd(app, "FusionCreateNewComponentCommand", "New Component").with(json!({ "parent": id })));
|
||||
v.push(cmd(app, "component.create", "New Component").with(json!({ "parent": id })));
|
||||
if id == 0 {
|
||||
v.push(
|
||||
cmd(app, "FusionCreateComponentsFromBodiesCommand", "Create Components from Bodies")
|
||||
.with(json!({ "bodies": names }))
|
||||
.on(!bodies.is_empty()),
|
||||
);
|
||||
v.push(cmd(app, "component.from_bodies", "Create Components from Bodies").with(json!({ "bodies": names })).on(!bodies.is_empty()));
|
||||
}
|
||||
v.push(Item::sep());
|
||||
v.push(act("ui.material", "Physical Material", "").with(json!({ "bodies": names })).on(!bodies.is_empty()));
|
||||
@@ -568,10 +564,10 @@ fn sketch_entity_items(app: &SolveApp) -> Vec<Item> {
|
||||
}
|
||||
let mut v = Vec::new();
|
||||
if curves.is_empty() && points.is_empty() {
|
||||
v.push(cmd(app, "DrawPolyline", "Line"));
|
||||
v.push(cmd(app, "ShapeRectangleTwoPoint", "Rectangle"));
|
||||
v.push(cmd(app, "CircleCenterRadius", "Circle"));
|
||||
v.push(cmd(app, "SketchDimension", "Sketch Dimension"));
|
||||
v.push(cmd(app, "sketch.line", "Line"));
|
||||
v.push(cmd(app, "sketch.rectangle.two_point", "Rectangle"));
|
||||
v.push(cmd(app, "sketch.circle.center", "Circle"));
|
||||
v.push(cmd(app, "sketch.dimension", "Sketch Dimension"));
|
||||
return v;
|
||||
}
|
||||
let ents: Vec<String> = curves.iter().chain(points.iter()).cloned().collect();
|
||||
@@ -581,41 +577,41 @@ fn sketch_entity_items(app: &SolveApp) -> Vec<Item> {
|
||||
}
|
||||
v.push(act("ui.fix", "Fix/Unfix", "c_fix").with(json!({ "entities": ents })));
|
||||
v.push(act("ui.delete", "Delete", "delete").key("Del"));
|
||||
v.push(cmd(app, "SketchDimension", "Sketch Dimension").with(json!({ "entities": ents })).on(ents.len() <= 2));
|
||||
v.push(cmd(app, "sketch.dimension", "Sketch Dimension").with(json!({ "entities": ents })).on(ents.len() <= 2));
|
||||
let mut c = Vec::new();
|
||||
let n = ents.len();
|
||||
let (l, r, p) = (lines.len(), rounds.len(), points.len());
|
||||
if l == 1 && n == 1 {
|
||||
c.push(cmd(app, "ConstraintHorizontalVertical", "Horizontal/Vertical").with(json!({ "line": lines[0] })));
|
||||
c.push(cmd(app, "sketch.constraint.horizontal_vertical", "Horizontal/Vertical").with(json!({ "line": lines[0] })));
|
||||
}
|
||||
if p == 2 && n == 2 {
|
||||
c.push(cmd(app, "ConstraintCoincident", "Coincident").with(json!({ "a": points[0], "b": points[1] })));
|
||||
c.push(cmd(app, "ConstraintHorizontalVertical", "Horizontal/Vertical").with(json!({ "points": points })));
|
||||
c.push(cmd(app, "sketch.constraint.coincident", "Coincident").with(json!({ "a": points[0], "b": points[1] })));
|
||||
c.push(cmd(app, "sketch.constraint.horizontal_vertical", "Horizontal/Vertical").with(json!({ "points": points })));
|
||||
}
|
||||
if p == 1 && curves.len() == 1 && n == 2 {
|
||||
c.push(cmd(app, "ConstraintCoincident", "Coincident").with(json!({ "a": picked[0], "b": picked[1] })));
|
||||
c.push(cmd(app, "sketch.constraint.coincident", "Coincident").with(json!({ "a": picked[0], "b": picked[1] })));
|
||||
if l == 1 {
|
||||
c.push(cmd(app, "ConstraintMidPoint", "MidPoint").with(json!({ "point": points[0], "line": lines[0] })));
|
||||
c.push(cmd(app, "sketch.constraint.midpoint", "MidPoint").with(json!({ "point": points[0], "line": lines[0] })));
|
||||
}
|
||||
}
|
||||
if l == 2 && n == 2 {
|
||||
let ab = json!({ "a": lines[0], "b": lines[1] });
|
||||
c.push(cmd(app, "ConstraintParallel", "Parallel").with(ab.clone()));
|
||||
c.push(cmd(app, "ConstraintPerpendicular", "Perpendicular").with(ab.clone()));
|
||||
c.push(cmd(app, "ConstraintCollinear", "Collinear").with(ab.clone()));
|
||||
c.push(cmd(app, "ConstraintEqual", "Equal").with(ab));
|
||||
c.push(cmd(app, "sketch.constraint.parallel", "Parallel").with(ab.clone()));
|
||||
c.push(cmd(app, "sketch.constraint.perpendicular", "Perpendicular").with(ab.clone()));
|
||||
c.push(cmd(app, "sketch.constraint.collinear", "Collinear").with(ab.clone()));
|
||||
c.push(cmd(app, "sketch.constraint.equal", "Equal").with(ab));
|
||||
}
|
||||
if r == 2 && n == 2 {
|
||||
let ab = json!({ "a": rounds[0], "b": rounds[1] });
|
||||
c.push(cmd(app, "ConstraintConcentric", "Concentric").with(ab.clone()));
|
||||
c.push(cmd(app, "ConstraintEqual", "Equal").with(ab.clone()));
|
||||
c.push(cmd(app, "ConstraintTangent", "Tangent").with(ab));
|
||||
c.push(cmd(app, "sketch.constraint.concentric", "Concentric").with(ab.clone()));
|
||||
c.push(cmd(app, "sketch.constraint.equal", "Equal").with(ab.clone()));
|
||||
c.push(cmd(app, "sketch.constraint.tangent", "Tangent").with(ab));
|
||||
}
|
||||
if l == 1 && r == 1 && n == 2 {
|
||||
c.push(cmd(app, "ConstraintTangent", "Tangent").with(json!({ "a": picked[0], "b": picked[1] })));
|
||||
c.push(cmd(app, "sketch.constraint.tangent", "Tangent").with(json!({ "a": picked[0], "b": picked[1] })));
|
||||
}
|
||||
if p == 2 && l == 1 && n == 3 {
|
||||
c.push(cmd(app, "ConstraintSymmetry", "Symmetry").with(json!({ "a": points[0], "b": points[1], "line": lines[0] })));
|
||||
c.push(cmd(app, "sketch.constraint.symmetry", "Symmetry").with(json!({ "a": points[0], "b": points[1], "line": lines[0] })));
|
||||
}
|
||||
if !c.is_empty() {
|
||||
v.push(Item::heading("Constraints"));
|
||||
@@ -678,7 +674,7 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
|
||||
"ui.moveBodies" => {
|
||||
let items: Vec<Value> = bodies.iter().map(|b| json!({"type": "body", "name": b})).collect();
|
||||
let _ = app.run("select.set", json!({ "items": items }));
|
||||
app.start("FusionMoveCommand");
|
||||
app.start("solid.move");
|
||||
}
|
||||
"ui.rename" => {
|
||||
let what = if let Some(b) = p.get("body").and_then(Value::as_str) {
|
||||
@@ -700,7 +696,7 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
|
||||
};
|
||||
start_rename(app, what, at);
|
||||
}
|
||||
"ui.properties" => match app.session.execute("MeasureCommand", &json!({ "bodies": bodies })) {
|
||||
"ui.properties" => match app.session.execute("inspect.measure", &json!({ "bodies": bodies })) {
|
||||
Ok(v) => app.menu.props = Some((if bodies.len() == 1 { bodies.join("") } else { format!("{} bodies", bodies.len()) }, v)),
|
||||
Err(e) => app.set_status(e.to_string(), true),
|
||||
},
|
||||
@@ -708,7 +704,7 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
|
||||
let name = bodies.first().cloned().unwrap_or_else(|| app.session.doc.name.clone());
|
||||
if let Some(path) = app.services.pick_save.as_ref().and_then(|f| f(&format!("{name}.step"), &["step", "stp", "igs", "stl", "3mf", "obj"]))
|
||||
{
|
||||
let _ = app.run("ExportCommand", json!({ "path": path, "bodies": bodies }));
|
||||
let _ = app.run("file.export", json!({ "path": path, "bodies": bodies }));
|
||||
}
|
||||
}
|
||||
"ui.findBrowser" => {
|
||||
@@ -728,11 +724,11 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
|
||||
"ui.tangentChain" => tangent_chain(app),
|
||||
"ui.origin" => app.ui.show_origin = !app.ui.show_origin,
|
||||
"ui.clear" => drop(app.run("select.clear", json!({}))),
|
||||
"ui.unsection" => drop(app.run("FusionHalfSectionViewCommand", json!({"clear": true}))),
|
||||
"ui.unsection" => drop(app.run("inspect.section", json!({"clear": true}))),
|
||||
"ui.fit" => app.animate_view("fit"),
|
||||
"ui.home" => app.animate_view("home"),
|
||||
// One command for the whole selection: all fixed unless all already are.
|
||||
"ui.fix" => drop(app.run("ConstraintFix", json!({ "entities": strs(&p, "entities") }))),
|
||||
"ui.fix" => drop(app.run("sketch.constraint.fix", json!({ "entities": strs(&p, "entities") }))),
|
||||
"ui.editSketch" => {
|
||||
if let Some(id) = id_of(&p, "sketch") {
|
||||
app.edit_sketch(id);
|
||||
@@ -827,12 +823,12 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
|
||||
"ui.material" => {
|
||||
let items: Vec<Value> = bodies.iter().map(|b| json!({"type": "body", "name": b})).collect();
|
||||
let _ = app.run("select.set", json!({ "items": items }));
|
||||
app.start("PhysicalMaterialCommand");
|
||||
app.start("material.assign");
|
||||
}
|
||||
"ui.saveMesh" => {
|
||||
let name = bodies.first().cloned().unwrap_or_else(|| app.session.doc.name.clone());
|
||||
if let Some(path) = app.services.pick_save.as_ref().and_then(|f| f(&format!("{name}.stl"), &["stl"])) {
|
||||
let _ = app.run("FusionSaveAsSTLCommand", json!({ "path": path, "bodies": bodies }));
|
||||
let _ = app.run("file.save_mesh", json!({ "path": path, "bodies": bodies }));
|
||||
}
|
||||
}
|
||||
"ui.exportDxf" => {
|
||||
@@ -848,7 +844,7 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
|
||||
let n = app.session.world_state().sketch(id).map_or(0, |s| s.profiles.len());
|
||||
let items: Vec<Value> = (0..n).map(|i| json!({"type": "profile", "sketch": id, "index": i})).collect();
|
||||
let _ = app.run("select.set", json!({ "items": items }));
|
||||
app.start("Extrude");
|
||||
app.start("solid.extrude");
|
||||
}
|
||||
}
|
||||
"ui.lookAt" => {
|
||||
@@ -1000,7 +996,7 @@ fn commit_rename(app: &mut SolveApp, r: &Rename) {
|
||||
}
|
||||
}
|
||||
}
|
||||
RenameWhat::Feature(f) => drop(app.run("FusionRenameTimelineEntryCommand", json!({ "feature": f, "name": name }))),
|
||||
RenameWhat::Feature(f) => drop(app.run("timeline.rename", json!({ "feature": f, "name": name }))),
|
||||
RenameWhat::Component(c) => drop(app.run("component.rename", json!({ "component": c, "name": name }))),
|
||||
RenameWhat::Group(g) => crate::browser::group_action(app, "ui.renameGroup", &json!({ "group": g, "name": name })),
|
||||
RenameWhat::Canvas(c) => drop(app.run("canvas.edit", json!({ "canvas": c, "name": name }))),
|
||||
|
||||
+119
-126
@@ -288,8 +288,8 @@ impl Dialog {
|
||||
let s = &app.session;
|
||||
let has_bodies = !s.world_state().bodies.is_empty();
|
||||
let mut d = match id {
|
||||
"SketchCreate" => Dialog::new(Kind::Sketch, vec![SelInput::new("Plane", PLANES | PLANAR_FACES, false)]),
|
||||
"Extrude" => Dialog::new(
|
||||
"sketch.create" => Dialog::new(Kind::Sketch, vec![SelInput::new("Plane", PLANES | PLANAR_FACES, false)]),
|
||||
"solid.extrude" => Dialog::new(
|
||||
Kind::Extrude {
|
||||
distance: "10 mm".into(),
|
||||
direction: 0,
|
||||
@@ -302,30 +302,30 @@ impl Dialog {
|
||||
},
|
||||
vec![SelInput::new("Profiles", PROFILES | PLANAR_FACES, true)],
|
||||
),
|
||||
"Revolve" => Dialog::new(
|
||||
"solid.revolve" => Dialog::new(
|
||||
Kind::Revolve { angle: "360 deg".into(), operation: 0 },
|
||||
vec![SelInput::new("Profile", PROFILES, true), SelInput::new("Axis", AXES, false)],
|
||||
),
|
||||
"FusionFilletEdgesCommand" => Dialog::new(fillet_kind("2 mm", false), vec![SelInput::new("Edges", EDGES | FACES, true)]),
|
||||
"FusionChamferCommand" => Dialog::new(fillet_kind("1 mm", true), vec![SelInput::new("Edges", EDGES | FACES, true)]),
|
||||
"FusionShellBodyCommand" => Dialog::new(Kind::Shell { thickness: "2 mm".into() }, vec![SelInput::new("Faces/Body", FACES, true)]),
|
||||
"FusionDraftCommand" => Dialog::new(
|
||||
"solid.fillet" => Dialog::new(fillet_kind("2 mm", false), vec![SelInput::new("Edges", EDGES | FACES, true)]),
|
||||
"solid.chamfer" => Dialog::new(fillet_kind("1 mm", true), vec![SelInput::new("Edges", EDGES | FACES, true)]),
|
||||
"solid.shell" => Dialog::new(Kind::Shell { thickness: "2 mm".into() }, vec![SelInput::new("Faces/Body", FACES, true)]),
|
||||
"solid.draft" => Dialog::new(
|
||||
Kind::Draft { angle: "5 deg".into() },
|
||||
vec![SelInput::new("Faces", FACES, true), SelInput::new("Neutral plane", PLANES | PLANAR_FACES, false)],
|
||||
),
|
||||
"MirrorCommand" => {
|
||||
"solid.mirror" => {
|
||||
Dialog::new(Kind::Mirror, vec![SelInput::new("Objects", BODIES, true), SelInput::new("Mirror Plane", PLANES | PLANAR_FACES, false)])
|
||||
}
|
||||
"FusionRibCommand" | "FusionWebCommand" => Dialog::new(
|
||||
Kind::Rib { web: id == "FusionWebCommand", thickness: "2 mm".into(), depth: String::new(), flip: false },
|
||||
"solid.rib" | "solid.web" => Dialog::new(
|
||||
Kind::Rib { web: id == "solid.web", thickness: "2 mm".into(), depth: String::new(), flip: false },
|
||||
vec![SelInput::new("Curves", CURVES, true)],
|
||||
),
|
||||
"EmbossCmd" => Dialog::new(Kind::Emboss { depth: "1 mm".into(), deboss: false }, vec![SelInput::new("Profiles", PROFILES, true)]),
|
||||
"FusionReplaceFaceCommand" => Dialog::new(
|
||||
"solid.emboss" => Dialog::new(Kind::Emboss { depth: "1 mm".into(), deboss: false }, vec![SelInput::new("Profiles", PROFILES, true)]),
|
||||
"solid.replace_face" => Dialog::new(
|
||||
Kind::ReplaceFace,
|
||||
vec![SelInput::new("Faces", PLANAR_FACES, true), SelInput::new("Target", PLANES | PLANAR_FACES, false)],
|
||||
),
|
||||
"AlignCmd" => Dialog::new(
|
||||
"solid.align" => Dialog::new(
|
||||
Kind::Align { flip: false },
|
||||
vec![
|
||||
SelInput::new("Bodies", BODIES, true),
|
||||
@@ -333,85 +333,78 @@ impl Dialog {
|
||||
SelInput::new("To", FACES | selection::VERTICES, false),
|
||||
],
|
||||
),
|
||||
"SoftDeleteCommand" => Dialog::new(Kind::Remove, vec![SelInput::new("Bodies", BODIES, true)]),
|
||||
"PatternOnPath" => Dialog::new(
|
||||
"solid.remove" => Dialog::new(Kind::Remove, vec![SelInput::new("Bodies", BODIES, true)]),
|
||||
"solid.pattern.path" => Dialog::new(
|
||||
Kind::PathPattern { count: "4".into(), spacing: "20 mm".into() },
|
||||
vec![SelInput::new("Objects", BODIES, true), SelInput::new("Path", CURVES, true)],
|
||||
),
|
||||
"PrimitivePipe" => Dialog::new(Kind::Pipe { diameter: "5 mm".into(), wall: String::new() }, vec![SelInput::new("Path", CURVES, true)]),
|
||||
"StockModelCommand" => Dialog::new(Kind::Stock { margin: "2 mm".into() }, vec![SelInput::new("Bodies (all if none)", BODIES, true)]),
|
||||
"ConstructionPlaneOffsetFromPlaneCommand" => {
|
||||
Dialog::new(Kind::OffsetPlane { offset: "10 mm".into() }, vec![SelInput::new("Plane", PLANES, false)])
|
||||
}
|
||||
"ConstructionPlaneAtAngleCommand" => Dialog::new(
|
||||
"solid.pipe" => Dialog::new(Kind::Pipe { diameter: "5 mm".into(), wall: String::new() }, vec![SelInput::new("Path", CURVES, true)]),
|
||||
"solid.bounding_solid" => Dialog::new(Kind::Stock { margin: "2 mm".into() }, vec![SelInput::new("Bodies (all if none)", BODIES, true)]),
|
||||
"construct.plane.offset" => Dialog::new(Kind::OffsetPlane { offset: "10 mm".into() }, vec![SelInput::new("Plane", PLANES, false)]),
|
||||
"construct.plane.at_angle" => Dialog::new(
|
||||
Kind::AnglePlane { angle: "45 deg".into() },
|
||||
vec![SelInput::new("Plane", PLANES, false), SelInput::new("Axis", AXES, false)],
|
||||
),
|
||||
"FusionSplitBodyCommand" => Dialog::new(
|
||||
"solid.split_body" => Dialog::new(
|
||||
Kind::Split,
|
||||
vec![SelInput::new("Body to split", BODIES, false), SelInput::new("Splitting plane", PLANES | PLANAR_FACES, false)],
|
||||
),
|
||||
"ModifyScale" => Dialog::new(Kind::Scale { factor: "2".into() }, vec![SelInput::new("Bodies", BODIES, true)]),
|
||||
"FusionOffsetFacesCommand" => {
|
||||
Dialog::new(Kind::OffsetFaces { distance: "2 mm".into() }, vec![SelInput::new("Faces", PLANAR_FACES, true)])
|
||||
}
|
||||
"FusionThreadCommand" => {
|
||||
"solid.scale" => Dialog::new(Kind::Scale { factor: "2".into() }, vec![SelInput::new("Bodies", BODIES, true)]),
|
||||
"solid.offset_face" => Dialog::new(Kind::OffsetFaces { distance: "2 mm".into() }, vec![SelInput::new("Faces", PLANAR_FACES, true)]),
|
||||
"solid.thread" => {
|
||||
Dialog::new(Kind::Thread { designation: String::new(), length: String::new() }, vec![SelInput::new("Cylindrical face", FACES, false)])
|
||||
}
|
||||
"PhysicalMaterialCommand" => Dialog::new(Kind::Material { index: 1 }, vec![SelInput::new("Bodies", BODIES, true)]),
|
||||
"FusionHalfSectionViewCommand" => {
|
||||
"material.assign" => Dialog::new(Kind::Material { index: 1 }, vec![SelInput::new("Bodies", BODIES, true)]),
|
||||
"inspect.section" => {
|
||||
Dialog::new(Kind::Section { offset: "0 mm".into(), flip: false }, vec![SelInput::new("Plane", PLANES | PLANAR_FACES, false)])
|
||||
}
|
||||
"MeasureCommand" => {
|
||||
"inspect.measure" => {
|
||||
Dialog::new(Kind::Measure { result: None, of: Vec::new() }, vec![SelInput::new("Items", FACES | EDGES | selection::VERTICES, true)])
|
||||
}
|
||||
"PatternRectangular" => Dialog::new(
|
||||
"solid.pattern.rectangular" => Dialog::new(
|
||||
Kind::PatternRect { count: "3".into(), spacing: "20 mm".into(), count2: "1".into(), spacing2: "20 mm".into() },
|
||||
vec![SelInput::new("Objects", BODIES, true), SelInput::new("Direction", AXES, false), SelInput::new("Direction 2", AXES, false)],
|
||||
),
|
||||
"PatternCircular" => Dialog::new(
|
||||
"solid.pattern.circular" => Dialog::new(
|
||||
Kind::PatternCirc { count: "6".into(), angle: "360 deg".into() },
|
||||
vec![SelInput::new("Objects", BODIES, true), SelInput::new("Axis", AXES, false)],
|
||||
),
|
||||
"SolidLoft" => Dialog::new(Kind::Loft { operation: usize::from(has_bodies) }, vec![SelInput::new("Profiles", PROFILES, true)]),
|
||||
"Sweep" => Dialog::new(
|
||||
"solid.loft" => Dialog::new(Kind::Loft { operation: usize::from(has_bodies) }, vec![SelInput::new("Profiles", PROFILES, true)]),
|
||||
"solid.sweep" => Dialog::new(
|
||||
Kind::Sweep { operation: usize::from(has_bodies) },
|
||||
vec![SelInput::new("Profile", PROFILES, true), SelInput::new("Path", CURVES, true)],
|
||||
),
|
||||
"FusionMoveCommand" => Dialog::new(crate::dialogs_move::kind(0), crate::dialogs_move::inputs(0)),
|
||||
"FusionHoleCommand" => Dialog::new(hole_defaults(), vec![SelInput::new("Face", PLANAR_FACES, true)]),
|
||||
"PrimitiveBox" => Dialog::new(
|
||||
"solid.move" => Dialog::new(crate::dialogs_move::kind(0), crate::dialogs_move::inputs(0)),
|
||||
"solid.hole" => Dialog::new(hole_defaults(), vec![SelInput::new("Face", PLANAR_FACES, true)]),
|
||||
"solid.box" => Dialog::new(
|
||||
Kind::Primitive {
|
||||
cmd: "PrimitiveBox",
|
||||
cmd: "solid.box",
|
||||
fields: vec![("length", "20".into()), ("width", "20".into()), ("height", "20".into())],
|
||||
operation: 0,
|
||||
},
|
||||
vec![],
|
||||
),
|
||||
"PrimitiveCylinder" => Dialog::new(
|
||||
Kind::Primitive { cmd: "PrimitiveCylinder", fields: vec![("diameter", "20".into()), ("height", "20".into())], operation: 0 },
|
||||
"solid.cylinder" => Dialog::new(
|
||||
Kind::Primitive { cmd: "solid.cylinder", fields: vec![("diameter", "20".into()), ("height", "20".into())], operation: 0 },
|
||||
vec![],
|
||||
),
|
||||
"PrimitiveSphere" => {
|
||||
Dialog::new(Kind::Primitive { cmd: "PrimitiveSphere", fields: vec![("diameter", "20".into())], operation: 0 }, vec![])
|
||||
"solid.sphere" => Dialog::new(Kind::Primitive { cmd: "solid.sphere", fields: vec![("diameter", "20".into())], operation: 0 }, vec![]),
|
||||
"solid.torus" => {
|
||||
Dialog::new(Kind::Primitive { cmd: "solid.torus", fields: vec![("major", "20".into()), ("minor", "5".into())], operation: 0 }, vec![])
|
||||
}
|
||||
"PrimitiveTorus" => Dialog::new(
|
||||
Kind::Primitive { cmd: "PrimitiveTorus", fields: vec![("major", "20".into()), ("minor", "5".into())], operation: 0 },
|
||||
vec![],
|
||||
),
|
||||
"PrimitiveCoil" => Dialog::new(
|
||||
"solid.coil" => Dialog::new(
|
||||
Kind::Primitive {
|
||||
cmd: "PrimitiveCoil",
|
||||
cmd: "solid.coil",
|
||||
fields: vec![("diameter", "40".into()), ("revolutions", "4".into()), ("pitch", "8".into()), ("section_size", "3".into())],
|
||||
operation: 0,
|
||||
},
|
||||
vec![],
|
||||
),
|
||||
"FusionCombineCommand" => Dialog::new(
|
||||
"solid.combine" => Dialog::new(
|
||||
Kind::Combine { operation: 1, keep_tools: false },
|
||||
vec![SelInput::new("Target Body", BODIES, false), SelInput::new("Tool Bodies", BODIES, true)],
|
||||
),
|
||||
"ChangeParameterCommand" => Dialog::new(Kind::Params { new_name: String::new(), new_expr: String::new() }, vec![]),
|
||||
"parameters.change" => Dialog::new(Kind::Params { new_name: String::new(), new_expr: String::new() }, vec![]),
|
||||
_ => {
|
||||
let (kind, inputs) = crate::dialogs_assembly::start(app, id)
|
||||
.or_else(|| crate::dialogs_sheet::start(app, id))
|
||||
@@ -768,10 +761,10 @@ fn title(k: &Kind) -> &'static str {
|
||||
Kind::Move { .. } => "MOVE",
|
||||
Kind::Hole { .. } => "HOLE",
|
||||
Kind::Primitive { cmd, .. } => match *cmd {
|
||||
"PrimitiveBox" => "BOX",
|
||||
"PrimitiveCylinder" => "CYLINDER",
|
||||
"PrimitiveSphere" => "SPHERE",
|
||||
"PrimitiveCoil" => "COIL",
|
||||
"solid.box" => "BOX",
|
||||
"solid.cylinder" => "CYLINDER",
|
||||
"solid.sphere" => "SPHERE",
|
||||
"solid.coil" => "COIL",
|
||||
_ => "TORUS",
|
||||
},
|
||||
Kind::Combine { .. } => "COMBINE",
|
||||
@@ -1627,7 +1620,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
app.dialog = Some(d);
|
||||
} else if let Some((_, marker)) = d.editing {
|
||||
// Editing done: put the timeline marker back where it was.
|
||||
let _ = app.run("timeline.rollTo", marker.map(|m| json!({ "position": m })).unwrap_or_else(|| json!({})));
|
||||
let _ = app.run("timeline.roll_to", marker.map(|m| json!({ "position": m })).unwrap_or_else(|| json!({})));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1640,7 +1633,7 @@ pub fn cancel(app: &mut SolveApp) {
|
||||
if let Some(d) = app.dialog.take()
|
||||
&& let Some((_, marker)) = d.editing
|
||||
{
|
||||
let _ = app.run("timeline.rollTo", marker.map(|m| json!({ "position": m })).unwrap_or_else(|| json!({})));
|
||||
let _ = app.run("timeline.roll_to", marker.map(|m| json!({ "position": m })).unwrap_or_else(|| json!({})));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1681,7 +1674,7 @@ fn params_table(app: &mut SolveApp, ui: &mut egui::Ui, new_name: &mut String, ne
|
||||
ui.data_mut(|dd| dd.insert_temp(id, e.clone()));
|
||||
}
|
||||
if r.lost_focus() && e != p.expr {
|
||||
let _ = app.run("ChangeParameterCommand", json!({"name": p.name, "expression": e}));
|
||||
let _ = app.run("parameters.change", json!({"name": p.name, "expression": e}));
|
||||
ui.data_mut(|dd| dd.remove::<String>(id));
|
||||
}
|
||||
let v = vals.get(&p.name).map(|v| if p.unit == "deg" { format!("{:.3}°", v.v.to_degrees()) } else { format!("{:.4} {}", v.v, p.unit) });
|
||||
@@ -1698,7 +1691,7 @@ fn params_table(app: &mut SolveApp, ui: &mut egui::Ui, new_name: &mut String, ne
|
||||
ui.add(egui::TextEdit::singleline(new_expr).hint_text("expression, e.g. 25 mm").desired_width(160.0));
|
||||
if ui.button("Add").clicked()
|
||||
&& !new_name.is_empty()
|
||||
&& app.run("ChangeParameterCommand", json!({"name": new_name, "expression": new_expr})).is_ok()
|
||||
&& app.run("parameters.change", json!({"name": new_name, "expression": new_expr})).is_ok()
|
||||
{
|
||||
new_name.clear();
|
||||
new_expr.clear();
|
||||
@@ -1715,7 +1708,7 @@ pub fn start_sketch(app: &mut SolveApp, sel: &Sel) {
|
||||
_ => return,
|
||||
};
|
||||
let before = app.cam;
|
||||
if app.run("SketchCreate", json!({ "plane": plane })).is_ok() {
|
||||
if app.run("sketch.create", json!({ "plane": plane })).is_ok() {
|
||||
app.pre_sketch_cam = Some(before);
|
||||
look_at_sketch(app);
|
||||
// On a face: look at the picked point, not the plane's origin (a corner of the face),
|
||||
@@ -1817,11 +1810,11 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
let mut out = Vec::new();
|
||||
let (sketch, idx) = profiles();
|
||||
if !sketch.is_null() {
|
||||
out.push(("Extrude".to_string(), with(json!({"sketch": sketch, "profiles": idx}))));
|
||||
out.push(("solid.extrude".to_string(), with(json!({"sketch": sketch, "profiles": idx}))));
|
||||
}
|
||||
// Each planar body face extrudes on its own.
|
||||
for p in face_points(0) {
|
||||
out.push(("Extrude".to_string(), with(json!({ "face": p }))));
|
||||
out.push(("solid.extrude".to_string(), with(json!({ "face": p }))));
|
||||
}
|
||||
return Ok(out);
|
||||
}
|
||||
@@ -1835,7 +1828,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
};
|
||||
let (sketch, idx) = profiles();
|
||||
(
|
||||
"Revolve",
|
||||
"solid.revolve",
|
||||
json!({"sketch": sketch, "profiles": idx, "axis": axis, "angle": angle, "operation": OPS.get(*operation).copied().unwrap_or("new")}),
|
||||
)
|
||||
}
|
||||
@@ -1879,27 +1872,27 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
if *ctype > 0 && *flip {
|
||||
p["flip"] = json!(true);
|
||||
}
|
||||
("FusionChamferCommand", p)
|
||||
("solid.chamfer", p)
|
||||
} else {
|
||||
("FusionFilletEdgesCommand", json!({"edges": edges, "radius": radius}))
|
||||
("solid.fillet", json!({"edges": edges, "radius": radius}))
|
||||
}
|
||||
}
|
||||
Kind::Shell { thickness } => {
|
||||
need(0, "faces to remove")?;
|
||||
("FusionShellBodyCommand", json!({"faces": face_points(0), "thickness": thickness}))
|
||||
("solid.shell", json!({"faces": face_points(0), "thickness": thickness}))
|
||||
}
|
||||
Kind::Draft { angle } => {
|
||||
need(0, "faces")?;
|
||||
need(1, "the neutral plane")?;
|
||||
let neutral = plane_value(s, sels(d, 1).first()).ok_or("the neutral plane must be a plane or a planar face")?;
|
||||
("FusionDraftCommand", json!({"faces": face_points(0), "angle": angle, "neutral": neutral}))
|
||||
("solid.draft", json!({"faces": face_points(0), "angle": angle, "neutral": neutral}))
|
||||
}
|
||||
Kind::Mirror => {
|
||||
need(0, "objects")?;
|
||||
need(1, "the mirror plane")?;
|
||||
let features = pattern_features(s, sels(d, 0));
|
||||
let plane = plane_value(s, sels(d, 1).first()).ok_or("the mirror plane must be a plane or a planar face")?;
|
||||
("MirrorCommand", json!({"features": features, "plane": plane}))
|
||||
("solid.mirror", json!({"features": features, "plane": plane}))
|
||||
}
|
||||
Kind::PatternRect { count, spacing, count2, spacing2 } => {
|
||||
need(0, "objects")?;
|
||||
@@ -1915,7 +1908,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
p["count2"] = json!(count2);
|
||||
p["spacing2"] = json!(spacing2);
|
||||
}
|
||||
("PatternRectangular", p)
|
||||
("solid.pattern.rectangular", p)
|
||||
}
|
||||
Kind::PatternCirc { count, angle } => {
|
||||
need(0, "objects")?;
|
||||
@@ -1928,7 +1921,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
}
|
||||
None => return Err("select an axis".into()),
|
||||
};
|
||||
("PatternCircular", json!({"features": pattern_features(s, sels(d, 0)), "axis": axis, "count": count, "angle": angle}))
|
||||
("solid.pattern.circular", json!({"features": pattern_features(s, sels(d, 0)), "axis": axis, "count": count, "angle": angle}))
|
||||
}
|
||||
Kind::Loft { operation } => {
|
||||
need(0, "profiles of two or more sketches")?;
|
||||
@@ -1946,7 +1939,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
return Err("pick profiles in two or more sketches".into());
|
||||
}
|
||||
let sections: Vec<Value> = sections.into_iter().map(|(sk, v)| json!({"sketch": sk, "profiles": v})).collect();
|
||||
("SolidLoft", json!({"sections": sections, "operation": OPS.get(*operation).copied().unwrap_or("new")}))
|
||||
("solid.loft", json!({"sections": sections, "operation": OPS.get(*operation).copied().unwrap_or("new")}))
|
||||
}
|
||||
Kind::Sweep { operation } => {
|
||||
need(0, "a profile")?;
|
||||
@@ -1955,7 +1948,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
let path: Vec<String> = sels(d, 1).iter().filter_map(|x| if let Sel::SketchCurve { id } = x { Some(id.clone()) } else { None }).collect();
|
||||
let path_sketch = curves_sketch(app, &path, sketch.as_u64()).ok_or("the path curves must be in one sketch")?;
|
||||
(
|
||||
"Sweep",
|
||||
"solid.sweep",
|
||||
json!({"sketch": sketch, "profiles": idx, "path_sketch": path_sketch, "path": path, "operation": OPS.get(*operation).copied().unwrap_or("new")}),
|
||||
)
|
||||
}
|
||||
@@ -1965,7 +1958,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
Some(Sel::Plane { name }) => name.clone(),
|
||||
_ => return Err("pick an origin or construction plane".into()),
|
||||
};
|
||||
("ConstructionPlaneOffsetFromPlaneCommand", json!({"base": base, "offset": offset}))
|
||||
("construct.plane.offset", json!({"base": base, "offset": offset}))
|
||||
}
|
||||
Kind::AnglePlane { angle } => {
|
||||
need(0, "a plane")?;
|
||||
@@ -1981,7 +1974,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
}
|
||||
None => d.extra.get("axis").cloned().ok_or("pick an axis")?,
|
||||
};
|
||||
("ConstructionPlaneAtAngleCommand", json!({"base": base, "axis": axis, "angle": angle}))
|
||||
("construct.plane.at_angle", json!({"base": base, "axis": axis, "angle": angle}))
|
||||
}
|
||||
Kind::Rib { web, thickness, depth, flip } => {
|
||||
need(0, "open sketch curves")?;
|
||||
@@ -1991,12 +1984,12 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
if !depth.trim().is_empty() {
|
||||
p["depth"] = json!(depth);
|
||||
}
|
||||
(if *web { "FusionWebCommand" } else { "FusionRibCommand" }, p)
|
||||
(if *web { "solid.web" } else { "solid.rib" }, p)
|
||||
}
|
||||
Kind::Emboss { depth, deboss } => {
|
||||
need(0, "profiles")?;
|
||||
let (sketch, idx) = profiles();
|
||||
("EmbossCmd", json!({"sketch": sketch, "profiles": idx, "depth": depth, "mode": if *deboss { "deboss" } else { "emboss" }}))
|
||||
("solid.emboss", json!({"sketch": sketch, "profiles": idx, "depth": depth, "mode": if *deboss { "deboss" } else { "emboss" }}))
|
||||
}
|
||||
Kind::ReplaceFace => {
|
||||
need(0, "faces to move")?;
|
||||
@@ -2007,7 +2000,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
Some(Sel::Face { point, .. }) => p["target_face"] = pt(*point),
|
||||
_ => return Err("pick a plane or planar face".into()),
|
||||
}
|
||||
("FusionReplaceFaceCommand", p)
|
||||
("solid.replace_face", p)
|
||||
}
|
||||
Kind::Align { flip } => {
|
||||
need(0, "bodies")?;
|
||||
@@ -2021,11 +2014,11 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
_ => return Err("pick faces or vertices".into()),
|
||||
}
|
||||
}
|
||||
("AlignCmd", p)
|
||||
("solid.align", p)
|
||||
}
|
||||
Kind::Remove => {
|
||||
need(0, "bodies")?;
|
||||
("SoftDeleteCommand", json!({"bodies": body_names(0)}))
|
||||
("solid.remove", json!({"bodies": body_names(0)}))
|
||||
}
|
||||
Kind::PathPattern { count, spacing } => {
|
||||
need(0, "objects")?;
|
||||
@@ -2038,7 +2031,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
} else {
|
||||
p["bodies"] = json!(body_names(0));
|
||||
}
|
||||
("PatternOnPath", p)
|
||||
("solid.pattern.path", p)
|
||||
}
|
||||
Kind::Pipe { diameter, wall } => {
|
||||
need(0, "the path")?;
|
||||
@@ -2048,14 +2041,14 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
if !wall.trim().is_empty() {
|
||||
p["wall"] = json!(wall);
|
||||
}
|
||||
("PrimitivePipe", p)
|
||||
("solid.pipe", p)
|
||||
}
|
||||
Kind::Stock { margin } => {
|
||||
let mut p = json!({"margin": margin});
|
||||
if !sels(d, 0).is_empty() {
|
||||
p["bodies"] = json!(body_names(0));
|
||||
}
|
||||
("StockModelCommand", p)
|
||||
("solid.bounding_solid", p)
|
||||
}
|
||||
Kind::Split => {
|
||||
need(0, "the body to split")?;
|
||||
@@ -2069,15 +2062,15 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
}
|
||||
_ => return Err("pick a plane or a planar face".into()),
|
||||
};
|
||||
("FusionSplitBodyCommand", json!({"body": body, "plane": plane}))
|
||||
("solid.split_body", json!({"body": body, "plane": plane}))
|
||||
}
|
||||
Kind::Scale { factor } => {
|
||||
need(0, "bodies")?;
|
||||
("ModifyScale", json!({"bodies": body_names(0), "factor": factor}))
|
||||
("solid.scale", json!({"bodies": body_names(0), "factor": factor}))
|
||||
}
|
||||
Kind::OffsetFaces { distance } => {
|
||||
need(0, "planar faces")?;
|
||||
("FusionOffsetFacesCommand", json!({"faces": face_points(0), "distance": distance}))
|
||||
("solid.offset_face", json!({"faces": face_points(0), "distance": distance}))
|
||||
}
|
||||
Kind::Thread { designation, length } => {
|
||||
need(0, "a cylindrical face")?;
|
||||
@@ -2088,12 +2081,12 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
if !length.trim().is_empty() {
|
||||
p["length"] = json!(length);
|
||||
}
|
||||
("FusionThreadCommand", p)
|
||||
("solid.thread", p)
|
||||
}
|
||||
Kind::Material { index } => {
|
||||
need(0, "bodies")?;
|
||||
let name = solvecraft_engine::doc::MATERIALS.get(*index).map(|(n, _)| *n).unwrap_or("Default");
|
||||
("PhysicalMaterialCommand", json!({"bodies": body_names(0), "material": name}))
|
||||
("material.assign", json!({"bodies": body_names(0), "material": name}))
|
||||
}
|
||||
Kind::Section { offset, flip } => {
|
||||
need(0, "a plane or planar face")?;
|
||||
@@ -2113,9 +2106,9 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
}
|
||||
_ => return Err("pick a plane or a planar face".into()),
|
||||
};
|
||||
("FusionHalfSectionViewCommand", json!({"plane": plane, "offset": offset, "flip": flip}))
|
||||
("inspect.section", json!({"plane": plane, "offset": offset, "flip": flip}))
|
||||
}
|
||||
Kind::Move { .. } => return Ok(vec![("FusionMoveCommand".into(), crate::dialogs_move::params(app, d)?)]),
|
||||
Kind::Move { .. } => return Ok(vec![("solid.move".into(), crate::dialogs_move::params(app, d)?)]),
|
||||
Kind::Hole { diameter, depth, kind, cb_diameter, cb_depth, cs_diameter, cs_angle, opts } => {
|
||||
need(0, if opts.multiple { "sketch points" } else { "a face position" })?;
|
||||
let ty = match *kind {
|
||||
@@ -2166,7 +2159,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
out.push(("FusionHoleCommand".to_string(), params));
|
||||
out.push(("solid.hole".to_string(), params));
|
||||
}
|
||||
return Ok(out);
|
||||
}
|
||||
@@ -2184,13 +2177,13 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
let target = body_names(0).into_iter().next().unwrap_or_default();
|
||||
let tools: Vec<String> = body_names(1).into_iter().filter(|n| *n != target).collect();
|
||||
(
|
||||
"FusionCombineCommand",
|
||||
"solid.combine",
|
||||
json!({"target": target, "tools": tools, "operation": OPS.get(*operation).copied().unwrap_or("join"), "keep_tools": keep_tools}),
|
||||
)
|
||||
}
|
||||
Kind::EditParam { name, expr } => ("ChangeParameterCommand", json!({"name": name, "expression": expr})),
|
||||
Kind::Rename { feature, name } => ("FusionRenameTimelineEntryCommand", json!({"feature": feature, "name": name})),
|
||||
Kind::ConfirmDelete { feature, .. } => ("FusionDeleteCommand", json!({ "features": [feature.to_string()] })),
|
||||
Kind::EditParam { name, expr } => ("parameters.change", json!({"name": name, "expression": expr})),
|
||||
Kind::Rename { feature, name } => ("timeline.rename", json!({"feature": feature, "name": name})),
|
||||
Kind::ConfirmDelete { feature, .. } => ("timeline.delete", json!({ "features": [feature.to_string()] })),
|
||||
Kind::Sketch | Kind::Params { .. } | Kind::Measure { .. } | Kind::Preferences => return Ok(Vec::new()),
|
||||
Kind::Assembly(k) => return crate::dialogs_assembly::commands(app, k, &d.inputs),
|
||||
Kind::Sheet(k) => return crate::dialogs_sheet::commands(app, k, &d.inputs, &d.extra),
|
||||
@@ -2380,7 +2373,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
let start = |cmd: &str| Dialog::for_command(app, cmd);
|
||||
let mut d = match &f.kind {
|
||||
FeatureKind::Extrude { sketch, profiles, extent, operation, targets } => {
|
||||
let mut d = start("Extrude")?;
|
||||
let mut d = start("solid.extrude")?;
|
||||
d.kind = Kind::Extrude {
|
||||
distance: extent.distance.clone(),
|
||||
direction: match (extent.direction, &extent.distance2) {
|
||||
@@ -2415,7 +2408,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Revolve { sketch, profiles, axis, angle, operation, targets } => {
|
||||
let mut d = start("Revolve")?;
|
||||
let mut d = start("solid.revolve")?;
|
||||
d.kind = Kind::Revolve { angle: angle.clone(), operation: op_index(operation) };
|
||||
let items = st.sketch(*sketch).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
@@ -2436,7 +2429,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
FeatureKind::Fillet { edges, radius, .. } | FeatureKind::Chamfer { edges, distance: radius, .. } => {
|
||||
let chamfer = matches!(f.kind, FeatureKind::Chamfer { .. });
|
||||
let mut d = start(if chamfer { "FusionChamferCommand" } else { "FusionFilletEdgesCommand" })?;
|
||||
let mut d = start(if chamfer { "solid.chamfer" } else { "solid.fillet" })?;
|
||||
d.kind = fillet_kind(radius, chamfer);
|
||||
if let Kind::Fillet { chain, ctype, distance2, angle, flip, .. } = &mut d.kind {
|
||||
*chain = false;
|
||||
@@ -2458,7 +2451,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Shell { faces, thickness, .. } => {
|
||||
let mut d = start("FusionShellBodyCommand")?;
|
||||
let mut d = start("solid.shell")?;
|
||||
d.kind = Kind::Shell { thickness: thickness.clone() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, *p)).collect();
|
||||
@@ -2466,7 +2459,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Draft { faces, angle, neutral, pull, .. } => {
|
||||
let mut d = start("FusionDraftCommand")?;
|
||||
let mut d = start("solid.draft")?;
|
||||
d.kind = Kind::Draft { angle: angle.clone() };
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, *p)).collect();
|
||||
@@ -2478,7 +2471,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Hole { position, direction, diameter, depth, hole, points, thread } => {
|
||||
let mut d = start("FusionHoleCommand")?;
|
||||
let mut d = start("solid.hole")?;
|
||||
let mut k = hole_defaults();
|
||||
if let Kind::Hole { diameter: dia, depth: dep, kind, cb_diameter, cb_depth, cs_diameter, cs_angle, opts } = &mut k {
|
||||
*dia = diameter.clone();
|
||||
@@ -2527,9 +2520,9 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Box { corner, length, width, height, operation } => {
|
||||
let mut d = start("PrimitiveBox")?;
|
||||
let mut d = start("solid.box")?;
|
||||
d.kind = Kind::Primitive {
|
||||
cmd: "PrimitiveBox",
|
||||
cmd: "solid.box",
|
||||
fields: vec![("length", length.clone()), ("width", width.clone()), ("height", height.clone())],
|
||||
operation: op_index(operation),
|
||||
};
|
||||
@@ -2537,9 +2530,9 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Cylinder { base, axis, radius, height, operation } => {
|
||||
let mut d = start("PrimitiveCylinder")?;
|
||||
let mut d = start("solid.cylinder")?;
|
||||
d.kind = Kind::Primitive {
|
||||
cmd: "PrimitiveCylinder",
|
||||
cmd: "solid.cylinder",
|
||||
fields: vec![("radius", radius.clone()), ("height", height.clone())],
|
||||
operation: op_index(operation),
|
||||
};
|
||||
@@ -2548,15 +2541,15 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Sphere { center, radius, operation } => {
|
||||
let mut d = start("PrimitiveSphere")?;
|
||||
d.kind = Kind::Primitive { cmd: "PrimitiveSphere", fields: vec![("radius", radius.clone())], operation: op_index(operation) };
|
||||
let mut d = start("solid.sphere")?;
|
||||
d.kind = Kind::Primitive { cmd: "solid.sphere", fields: vec![("radius", radius.clone())], operation: op_index(operation) };
|
||||
d.extra.insert("center".into(), pt3(*center));
|
||||
d
|
||||
}
|
||||
FeatureKind::Torus { center, major, minor, operation } => {
|
||||
let mut d = start("PrimitiveTorus")?;
|
||||
let mut d = start("solid.torus")?;
|
||||
d.kind = Kind::Primitive {
|
||||
cmd: "PrimitiveTorus",
|
||||
cmd: "solid.torus",
|
||||
fields: vec![("major", major.clone()), ("minor", minor.clone())],
|
||||
operation: op_index(operation),
|
||||
};
|
||||
@@ -2564,7 +2557,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Combine { target, tools, operation, keep_tools } => {
|
||||
let mut d = start("FusionCombineCommand")?;
|
||||
let mut d = start("solid.combine")?;
|
||||
d.kind = Kind::Combine { operation: op_index(operation).max(1), keep_tools: *keep_tools };
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
inp.items = vec![Sel::Body { name: target.clone() }];
|
||||
@@ -2575,7 +2568,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Mirror { features, plane, .. } => {
|
||||
let mut d = start("MirrorCommand")?;
|
||||
let mut d = start("solid.mirror")?;
|
||||
let ids: Vec<u64> = features.iter().filter_map(|n| s.doc.find_feature(n).map(|f| f.id)).collect();
|
||||
// Features that made no body of their own (a hole) were picked as features.
|
||||
let as_features = ids.iter().any(|id| !st.bodies.iter().any(|b| b.feature == *id));
|
||||
@@ -2598,7 +2591,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
let bodies: Vec<Sel> = st.bodies.iter().filter(|b| ids.contains(&b.feature)).map(|b| Sel::Body { name: b.name.clone() }).collect();
|
||||
let mut d = match pattern {
|
||||
PatternKind::Rectangular { dir1, count1, spacing1, dir2, count2, spacing2 } => {
|
||||
let mut d = start("PatternRectangular")?;
|
||||
let mut d = start("solid.pattern.rectangular")?;
|
||||
d.kind = Kind::PatternRect {
|
||||
count: count1.clone(),
|
||||
spacing: spacing1.clone(),
|
||||
@@ -2622,7 +2615,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
PatternKind::Circular { origin, axis, count, angle } => {
|
||||
let mut d = start("PatternCircular")?;
|
||||
let mut d = start("solid.pattern.circular")?;
|
||||
d.kind = Kind::PatternCirc { count: count.clone(), angle: angle.clone() };
|
||||
match world_axis(*axis).filter(|_| origin.len() < 1e-9) {
|
||||
Some(name) => {
|
||||
@@ -2653,7 +2646,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Loft { sections, operation, targets } => {
|
||||
let mut d = start("SolidLoft")?;
|
||||
let mut d = start("solid.loft")?;
|
||||
d.kind = Kind::Loft { operation: op_index(operation) };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = sections
|
||||
@@ -2670,7 +2663,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Sweep { sketch, profiles, path, operation, targets, .. } => {
|
||||
let mut d = start("Sweep")?;
|
||||
let mut d = start("solid.sweep")?;
|
||||
d.kind = Kind::Sweep { operation: op_index(operation) };
|
||||
let items = st.sketch(*sketch).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
@@ -2685,7 +2678,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::ConstructionPlane { plane: PlaneRef::Offset { base, distance } } => {
|
||||
let mut d = start("ConstructionPlaneOffsetFromPlaneCommand")?;
|
||||
let mut d = start("construct.plane.offset")?;
|
||||
d.kind = Kind::OffsetPlane { offset: distance.clone() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = plane_sel(s, base).into_iter().collect();
|
||||
@@ -2693,7 +2686,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::ConstructionPlane { plane: PlaneRef::AtAngle { base, axis_origin, axis_dir, angle } } => {
|
||||
let mut d = start("ConstructionPlaneAtAngleCommand")?;
|
||||
let mut d = start("construct.plane.at_angle")?;
|
||||
d.kind = Kind::AnglePlane { angle: angle.clone() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = plane_sel(s, base).into_iter().collect();
|
||||
@@ -2711,7 +2704,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Split { body, plane, .. } => {
|
||||
let mut d = start("FusionSplitBodyCommand")?;
|
||||
let mut d = start("solid.split_body")?;
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = vec![Sel::Body { name: body.clone() }];
|
||||
}
|
||||
@@ -2721,7 +2714,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Scale { bodies, origin, factor, factors } => {
|
||||
let mut d = start("ModifyScale")?;
|
||||
let mut d = start("solid.scale")?;
|
||||
d.kind = Kind::Scale { factor: factor.clone() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect();
|
||||
@@ -2733,7 +2726,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::OffsetFace { faces, distance, body } => {
|
||||
let mut d = start("FusionOffsetFacesCommand")?;
|
||||
let mut d = start("solid.offset_face")?;
|
||||
d.kind = Kind::OffsetFaces { distance: distance.clone() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, *p)).collect();
|
||||
@@ -2744,7 +2737,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Thread { face, designation, length } => {
|
||||
let mut d = start("FusionThreadCommand")?;
|
||||
let mut d = start("solid.thread")?;
|
||||
d.kind = Kind::Thread { designation: designation.clone().unwrap_or_default(), length: length.clone().unwrap_or_default() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = face_sel(s, *face).into_iter().collect();
|
||||
@@ -2752,7 +2745,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::BoundingSolid { bodies, margin } => {
|
||||
let mut d = start("StockModelCommand")?;
|
||||
let mut d = start("solid.bounding_solid")?;
|
||||
d.kind = Kind::Stock { margin: margin.clone() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect();
|
||||
@@ -2760,7 +2753,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Pipe { path, diameter, wall, operation, targets, .. } => {
|
||||
let mut d = start("PrimitivePipe")?;
|
||||
let mut d = start("solid.pipe")?;
|
||||
d.kind = Kind::Pipe { diameter: diameter.clone(), wall: wall.clone().unwrap_or_default() };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = path.iter().map(|id| Sel::SketchCurve { id: id.clone() }).collect();
|
||||
@@ -2772,7 +2765,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Emboss { sketch, profiles, depth, deboss, targets } => {
|
||||
let mut d = start("EmbossCmd")?;
|
||||
let mut d = start("solid.emboss")?;
|
||||
d.kind = Kind::Emboss { depth: depth.clone(), deboss: *deboss };
|
||||
let items = st.sketch(*sketch).map(|ss| profile_indices(ss, profiles)).unwrap_or_default();
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
@@ -2784,7 +2777,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Rib { curves, thickness, depth, flip, web, .. } => {
|
||||
let mut d = start(if *web { "FusionWebCommand" } else { "FusionRibCommand" })?;
|
||||
let mut d = start(if *web { "solid.web" } else { "solid.rib" })?;
|
||||
d.kind = Kind::Rib { web: *web, thickness: thickness.clone(), depth: depth.clone().unwrap_or_default(), flip: *flip };
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = curves.iter().map(|id| Sel::SketchCurve { id: id.clone() }).collect();
|
||||
@@ -2792,7 +2785,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::ReplaceFace { faces, target, body } => {
|
||||
let mut d = start("FusionReplaceFaceCommand")?;
|
||||
let mut d = start("solid.replace_face")?;
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = faces.iter().filter_map(|p| face_sel(s, *p)).collect();
|
||||
}
|
||||
@@ -2805,14 +2798,14 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
d
|
||||
}
|
||||
FeatureKind::Remove { bodies } => {
|
||||
let mut d = start("SoftDeleteCommand")?;
|
||||
let mut d = start("solid.remove")?;
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
inp.items = bodies.iter().map(|n| Sel::Body { name: n.clone() }).collect();
|
||||
}
|
||||
d
|
||||
}
|
||||
FeatureKind::Move { bodies, translate, rotate_axis, angle } => {
|
||||
let mut d = start("FusionMoveCommand")?;
|
||||
let mut d = start("solid.move")?;
|
||||
crate::dialogs_move::for_feature(&mut d, bodies, translate, *rotate_axis, angle.as_ref());
|
||||
d
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ pub struct Ap {
|
||||
|
||||
/// The dialog for the Appearance command.
|
||||
pub fn start(_app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
|
||||
(id == "AppearanceCommand").then(|| {
|
||||
(id == "appearance.assign").then(|| {
|
||||
(
|
||||
Kind::Appearance(Ap { faces: false, color: [196, 199, 204], opacity: 1.0, look: None, category: 0, note: None }),
|
||||
vec![SelInput::new("Objects", BODIES, true)],
|
||||
@@ -151,7 +151,7 @@ fn apply_params(targets: &[Sel], color: [u8; 3], opacity: f32, look: Option<&str
|
||||
/// The commands OK runs: the current look on the picked objects (nothing when none are picked).
|
||||
pub fn commands(k: &Ap, inputs: &[SelInput]) -> Vec<(String, Value)> {
|
||||
let items = inputs.first().map(|i| i.items.as_slice()).unwrap_or(&[]);
|
||||
apply_params(items, k.color, k.opacity, k.look.as_deref()).map(|p| vec![("AppearanceCommand".to_string(), p)]).unwrap_or_default()
|
||||
apply_params(items, k.color, k.opacity, k.look.as_deref()).map(|p| vec![("appearance.assign".to_string(), p)]).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// What a swatch dropped now would land on: the face or body under the cursor in the viewport,
|
||||
@@ -267,7 +267,7 @@ pub fn rows(app: &mut SolveApp, ui: &mut egui::Ui, k: &mut Ap, inputs: &mut [Sel
|
||||
if ui.add_enabled(!picked.is_empty(), egui::Button::new("Apply to selection")).clicked()
|
||||
&& let Some(p) = apply_params(&picked, k.color, k.opacity, k.look.as_deref())
|
||||
{
|
||||
k.note = Some(match app.run("AppearanceCommand", p) {
|
||||
k.note = Some(match app.run("appearance.assign", p) {
|
||||
Ok(_) => format!("applied to {} item(s)", picked.len()),
|
||||
Err(e) => e,
|
||||
});
|
||||
@@ -321,7 +321,7 @@ pub fn rows(app: &mut SolveApp, ui: &mut egui::Ui, k: &mut Ap, inputs: &mut [Sel
|
||||
k.opacity = look.opacity;
|
||||
k.look = (look.category != "In This Design").then(|| look.name.clone());
|
||||
if let Some(p) = apply_params(&targets, look.color, look.opacity, k.look.as_deref()) {
|
||||
k.note = Some(match app.run("AppearanceCommand", p) {
|
||||
k.note = Some(match app.run("appearance.assign", p) {
|
||||
Ok(_) => format!("{} on {}", look.name, target_names(&targets)),
|
||||
Err(e) => e,
|
||||
});
|
||||
@@ -358,8 +358,8 @@ mod tests {
|
||||
|
||||
fn app() -> SolveApp {
|
||||
let mut s = Session::default();
|
||||
s.execute("PrimitiveBox", &json!({"length": 10, "width": 10, "height": 10})).unwrap();
|
||||
s.execute("PrimitiveBox", &json!({"length": 5, "width": 5, "height": 5, "corner": [20, 0, 0]})).unwrap();
|
||||
s.execute("solid.box", &json!({"length": 10, "width": 10, "height": 10})).unwrap();
|
||||
s.execute("solid.box", &json!({"length": 5, "width": 5, "height": 5, "corner": [20, 0, 0]})).unwrap();
|
||||
SolveApp::new(s, Services::default())
|
||||
}
|
||||
|
||||
@@ -377,7 +377,7 @@ mod tests {
|
||||
fn ok_applies_the_look_to_the_objects() {
|
||||
let mut a = app();
|
||||
a.session.selection = vec![Sel::Body { name: "Body1".into() }, Sel::Body { name: "Body2".into() }];
|
||||
a.start("AppearanceCommand");
|
||||
a.start("appearance.assign");
|
||||
let mut d = a.dialog.clone().unwrap();
|
||||
assert_eq!(d.inputs[0].items.len(), 2);
|
||||
if let Kind::Appearance(k) = &mut d.kind {
|
||||
@@ -385,7 +385,7 @@ mod tests {
|
||||
k.look = Some("Paint - Red".into());
|
||||
}
|
||||
let c = apply_commands(&a, &d).unwrap();
|
||||
assert_eq!(c[0].0, "AppearanceCommand");
|
||||
assert_eq!(c[0].0, "appearance.assign");
|
||||
assert_eq!(c[0].1["color"], "#c42824");
|
||||
assert_eq!(c[0].1["bodies"], json!(["Body1", "Body2"]));
|
||||
for (id, p) in c {
|
||||
|
||||
@@ -26,16 +26,8 @@ const TYPE_LABELS: [&str; 7] = ["Rigid", "Revolute", "Slider", "Cylindrical", "P
|
||||
/// What a joint origin input accepts (snaps on faces, edges and vertices).
|
||||
const SNAPS: u16 = FACES | EDGES | VERTICES;
|
||||
/// Commands whose effect is where occurrences sit (previewed on the placed model).
|
||||
const MOVES: [&str; 8] = [
|
||||
"JointAssembleCmdNew",
|
||||
"JointAsBuiltCmd",
|
||||
"FusionMoveJointsCommand",
|
||||
"joint.edit",
|
||||
"joint.limits",
|
||||
"RigidGroupCmd",
|
||||
"parts.insert",
|
||||
"FusionFastenersCommand",
|
||||
];
|
||||
const MOVES: [&str; 8] =
|
||||
["joint.create", "joint.as_built", "joint.drive", "joint.edit", "joint.limits", "joint.rigid_group", "parts.insert", "parts.fastener"];
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Asm {
|
||||
@@ -148,27 +140,25 @@ impl Asm {
|
||||
pub fn start(app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
|
||||
let s = &app.session;
|
||||
let (asm, inputs) = match id {
|
||||
"FusionCreateNewComponentCommand" => {
|
||||
(Asm::NewComponent { name: String::new(), activate: true }, vec![SelInput::new("Bodies (optional)", BODIES, true)])
|
||||
}
|
||||
"JointAssembleCmdNew" => (
|
||||
"component.create" => (Asm::NewComponent { name: String::new(), activate: true }, vec![SelInput::new("Bodies (optional)", BODIES, true)]),
|
||||
"joint.create" => (
|
||||
Asm::Joint(Box::new(JointForm::new(false))),
|
||||
vec![SelInput::new("Component 1", SNAPS, false), SelInput::new("Component 2", SNAPS, false)],
|
||||
),
|
||||
"JointAsBuiltCmd" => {
|
||||
"joint.as_built" => {
|
||||
(Asm::Joint(Box::new(JointForm::new(true))), vec![SelInput::new("Components", BODIES, true), SelInput::new("Position", SNAPS, false)])
|
||||
}
|
||||
"JointOrigin" => (Asm::JointOrigin { name: String::new(), hover: None }, vec![SelInput::new("Snap", SNAPS, false)]),
|
||||
"RigidGroupCmd" => (Asm::RigidGroup, vec![SelInput::new("Components", BODIES, true)]),
|
||||
"FusionMoveJointsCommand" => {
|
||||
"joint.origin" => (Asm::JointOrigin { name: String::new(), hover: None }, vec![SelInput::new("Snap", SNAPS, false)]),
|
||||
"joint.rigid_group" => (Asm::RigidGroup, vec![SelInput::new("Components", BODIES, true)]),
|
||||
"joint.drive" => {
|
||||
let joint = movable(s).first().copied().unwrap_or(0);
|
||||
(Asm::Drive { joint, values: current_values(s, joint), focus: true }, vec![])
|
||||
}
|
||||
"FusionMotionRelationshipCommand" => {
|
||||
"joint.motion_link" => {
|
||||
let b = usize::from(movable(s).len() > 1);
|
||||
(Asm::MotionLink { a: 0, b, ia: 0, ib: 0, ratio: "1".into(), offset: "0".into() }, vec![])
|
||||
}
|
||||
"InterferenceCheckCommand" => {
|
||||
"inspect.interference" => {
|
||||
(Asm::Interference { result: None, overlaps: Vec::new(), of: Vec::new() }, vec![SelInput::new("Bodies (all if none)", BODIES, true)])
|
||||
}
|
||||
_ => return None,
|
||||
@@ -571,7 +561,7 @@ fn capital(s: &str) -> String {
|
||||
/// Run the check and find the overlapping volumes (world placement) to show in red.
|
||||
fn interference(app: &mut SolveApp, picked: &[Sel]) -> (Value, Vec<Mesh>) {
|
||||
let names: Vec<String> = picked.iter().filter_map(|x| if let Sel::Body { name } = x { Some(name.clone()) } else { None }).collect();
|
||||
let r = match app.run("InterferenceCheckCommand", json!({ "bodies": names })) {
|
||||
let r = match app.run("inspect.interference", json!({ "bodies": names })) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return (json!({ "error": e }), Vec::new()),
|
||||
};
|
||||
@@ -655,9 +645,9 @@ pub fn commands(app: &SolveApp, k: &Asm, inputs: &[SelInput]) -> Result<Vec<(Str
|
||||
if !name.trim().is_empty() {
|
||||
p["name"] = json!(name.trim());
|
||||
}
|
||||
cmd("FusionCreateNewComponentCommand", p)
|
||||
cmd("component.create", p)
|
||||
} else {
|
||||
cmd("FusionCreateComponentsFromBodiesCommand", json!({ "bodies": bodies }))
|
||||
cmd("component.from_bodies", json!({ "bodies": bodies }))
|
||||
}
|
||||
}
|
||||
Asm::Joint(f) => {
|
||||
@@ -685,7 +675,7 @@ pub fn commands(app: &SolveApp, k: &Asm, inputs: &[SelInput]) -> Result<Vec<(Str
|
||||
if let Some(x) = sel_items(inputs, 1).first().and_then(|x| snap_of(s, x)) {
|
||||
p["at"] = x.param;
|
||||
}
|
||||
return Ok(cmd("JointAsBuiltCmd", p));
|
||||
return Ok(cmd("joint.as_built", p));
|
||||
}
|
||||
let (a, b) = (snap(0, "component 1's joint origin")?, snap(1, "component 2's joint origin")?);
|
||||
if a.param.get("occurrence").is_some() && a.param.get("occurrence") == b.param.get("occurrence") {
|
||||
@@ -697,31 +687,31 @@ pub fn commands(app: &SolveApp, k: &Asm, inputs: &[SelInput]) -> Result<Vec<(Str
|
||||
// meet face to face, a pin goes into its hole); the dialog's Flip is the command's
|
||||
// `flip`, which turns B the other way.
|
||||
let p = json!({"type": kind, "a": a_param, "b": b.param, "flip": f.flip, "offset": f.offset[2], "angle": f.angle, "limits": limits});
|
||||
cmd("JointAssembleCmdNew", p)
|
||||
cmd("joint.create", p)
|
||||
}
|
||||
Asm::JointOrigin { name, .. } => {
|
||||
let mut p = snap(0, "a snap")?.param;
|
||||
if !name.trim().is_empty() {
|
||||
p["name"] = json!(name.trim());
|
||||
}
|
||||
cmd("JointOrigin", p)
|
||||
cmd("joint.origin", p)
|
||||
}
|
||||
Asm::RigidGroup => {
|
||||
let occs = occurrences(s, sel_items(inputs, 0));
|
||||
if occs.len() < 2 {
|
||||
return Err("select two or more components".into());
|
||||
}
|
||||
cmd("RigidGroupCmd", json!({ "occurrences": occs }))
|
||||
cmd("joint.rigid_group", json!({ "occurrences": occs }))
|
||||
}
|
||||
Asm::Drive { joint, values, .. } => {
|
||||
let j = s.doc.assembly.joints.get(*joint).ok_or("no joint that moves yet")?;
|
||||
cmd("FusionMoveJointsCommand", json!({"joint": j.id, "values": values}))
|
||||
cmd("joint.drive", json!({"joint": j.id, "values": values}))
|
||||
}
|
||||
Asm::MotionLink { a, b, ia, ib, ratio, offset } => {
|
||||
let js = &s.doc.assembly.joints;
|
||||
let (Some(ja), Some(jb)) = (js.get(*a), js.get(*b)) else { return Err("needs two joints".into()) };
|
||||
let num = |e: &str| eval(e, ValueKind::Unitless);
|
||||
cmd("FusionMotionRelationshipCommand", json!({"a": ja.id, "b": jb.id, "ia": ia, "ib": ib, "ratio": num(ratio)?, "offset": num(offset)?}))
|
||||
cmd("joint.motion_link", json!({"a": ja.id, "b": jb.id, "ia": ia, "ib": ib, "ratio": num(ratio)?, "offset": num(offset)?}))
|
||||
}
|
||||
// The check runs from its Compute button; OK only closes.
|
||||
Asm::Interference { .. } => Vec::new(),
|
||||
@@ -756,7 +746,7 @@ pub fn animate(app: &SolveApp, k: &Asm, cmds: &mut Vec<(String, Value)>) {
|
||||
.collect();
|
||||
let drive = match f.editing {
|
||||
Some(id) => Some(id),
|
||||
None => match cmds.iter_mut().find(|(c, _)| c == "JointAssembleCmdNew") {
|
||||
None => match cmds.iter_mut().find(|(c, _)| c == "joint.create") {
|
||||
Some((_, p)) => {
|
||||
p["values"] = json!(values);
|
||||
None
|
||||
@@ -766,7 +756,7 @@ pub fn animate(app: &SolveApp, k: &Asm, cmds: &mut Vec<(String, Value)>) {
|
||||
},
|
||||
};
|
||||
if let Some(id) = drive {
|
||||
cmds.push(("FusionMoveJointsCommand".into(), json!({"joint": id, "values": values})));
|
||||
cmds.push(("joint.drive".into(), json!({"joint": id, "values": values})));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ fn two_boxes() -> SolveApp {
|
||||
let mut s = Session::default();
|
||||
for (name, x) in [("A", 0), ("B", 30)] {
|
||||
s.execute("component.activate", &json!({"component": "root"})).unwrap();
|
||||
s.execute("FusionCreateNewComponentCommand", &json!({ "name": name })).unwrap();
|
||||
s.execute("PrimitiveBox", &json!({"length": 20, "width": 20, "height": 10, "corner": [x, 0, 0]})).unwrap();
|
||||
s.execute("component.create", &json!({ "name": name })).unwrap();
|
||||
s.execute("solid.box", &json!({"length": 20, "width": 20, "height": 10, "corner": [x, 0, 0]})).unwrap();
|
||||
}
|
||||
s.execute("component.activate", &json!({"component": "root"})).unwrap();
|
||||
s.execute("occurrence.ground", &json!({"occurrence": "A:1", "grounded": true})).unwrap();
|
||||
@@ -28,7 +28,7 @@ fn occ(app: &SolveApp, name: &str) -> u64 {
|
||||
#[test]
|
||||
fn joint_from_two_snaps() {
|
||||
let mut app = two_boxes();
|
||||
app.start("JointAssembleCmdNew");
|
||||
app.start("joint.create");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
let (fa, fb) = (face_sel(&app.session, Vec3::new(10.0, 10.0, 10.0)).unwrap(), face_sel(&app.session, Vec3::new(40.0, 10.0, 0.0)).unwrap());
|
||||
assert!(apply_commands(&app, &d).is_err(), "no snaps yet");
|
||||
@@ -42,7 +42,7 @@ fn joint_from_two_snaps() {
|
||||
}
|
||||
let c = apply_commands(&app, &d).unwrap();
|
||||
let (id, p) = &c[0];
|
||||
assert_eq!(id, "JointAssembleCmdNew");
|
||||
assert_eq!(id, "joint.create");
|
||||
assert_eq!(p["type"], "revolute");
|
||||
assert_eq!(p["a"]["occurrence"], occ(&app, "A"));
|
||||
assert_eq!(p["b"]["occurrence"], occ(&app, "B"));
|
||||
@@ -70,7 +70,7 @@ fn joint_from_two_snaps() {
|
||||
#[test]
|
||||
fn joint_and_drive_preview_on_the_placed_model() {
|
||||
let mut app = two_boxes();
|
||||
app.start("JointAssembleCmdNew");
|
||||
app.start("joint.create");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
d.pick(&app.session, face_sel(&app.session, Vec3::new(10.0, 10.0, 10.0)).unwrap());
|
||||
d.pick(&app.session, face_sel(&app.session, Vec3::new(40.0, 10.0, 0.0)).unwrap());
|
||||
@@ -91,20 +91,20 @@ fn joint_and_drive_preview_on_the_placed_model() {
|
||||
for (id, p) in apply_commands(&app, &d).unwrap() {
|
||||
app.run(&id, p).unwrap();
|
||||
}
|
||||
app.start("FusionMoveJointsCommand");
|
||||
app.start("joint.drive");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
if let Kind::Assembly(Asm::Drive { values, .. }) = &mut d.kind {
|
||||
assert_eq!(values, &vec!["0 deg".to_string()]);
|
||||
values[0] = "30 deg".into();
|
||||
}
|
||||
let cmds = apply_commands(&app, &d).unwrap();
|
||||
assert_eq!(cmds[0].0, "FusionMoveJointsCommand");
|
||||
assert_eq!(cmds[0].0, "joint.drive");
|
||||
let b = world_preview(&app.session, &cmds, colors).unwrap().unwrap();
|
||||
assert_eq!(b.replaced, vec!["Body2".to_string()]);
|
||||
// Features preview as before while nothing is placed away from its frame, and on the
|
||||
// placed model once something is (B moved by its joint).
|
||||
assert!(world_preview(&two_boxes().session, &[("Extrude".into(), json!({}))], colors).is_none());
|
||||
assert!(world_preview(&app.session, &[("Extrude".into(), json!({}))], colors).is_some());
|
||||
assert!(world_preview(&two_boxes().session, &[("solid.extrude".into(), json!({}))], colors).is_none());
|
||||
assert!(world_preview(&app.session, &[("solid.extrude".into(), json!({}))], colors).is_some());
|
||||
}
|
||||
|
||||
/// Edit Joint changes type, alignment and limits of the joint in place.
|
||||
@@ -112,11 +112,8 @@ fn joint_and_drive_preview_on_the_placed_model() {
|
||||
fn edit_joint_runs_edit_and_limits() {
|
||||
let mut app = two_boxes();
|
||||
let (a, b) = (occ(&app, "A"), occ(&app, "B"));
|
||||
app.run(
|
||||
"JointAssembleCmdNew",
|
||||
json!({"type": "slider", "a": {"occurrence": a, "face": [10, 10, 10]}, "b": {"occurrence": b, "face": [40, 10, 0]}}),
|
||||
)
|
||||
.unwrap();
|
||||
app.run("joint.create", json!({"type": "slider", "a": {"occurrence": a, "face": [10, 10, 10]}, "b": {"occurrence": b, "face": [40, 10, 0]}}))
|
||||
.unwrap();
|
||||
let id = app.session.doc.assembly.joints[0].id;
|
||||
let mut d = edit_joint(&app, id).unwrap();
|
||||
assert!(d.inputs.is_empty());
|
||||
@@ -141,19 +138,19 @@ fn edit_joint_runs_edit_and_limits() {
|
||||
fn component_picks_become_occurrences() {
|
||||
let mut app = two_boxes();
|
||||
app.session.selection = vec![Sel::Body { name: "Body1".into() }, Sel::Body { name: "Body2".into() }];
|
||||
app.start("JointAsBuiltCmd");
|
||||
app.start("joint.as_built");
|
||||
let d = app.dialog.clone().unwrap();
|
||||
let c = apply_commands(&app, &d).unwrap();
|
||||
assert_eq!(c[0].1["a"], occ(&app, "A"));
|
||||
assert_eq!(c[0].1["b"], occ(&app, "B"));
|
||||
app.session.selection = vec![Sel::Body { name: "Body1".into() }, Sel::Body { name: "Body2".into() }];
|
||||
app.start("RigidGroupCmd");
|
||||
app.start("joint.rigid_group");
|
||||
let c = apply_commands(&app, app.dialog.as_ref().unwrap()).unwrap();
|
||||
assert_eq!(c[0].1["occurrences"], json!([occ(&app, "A"), occ(&app, "B")]));
|
||||
app.dialog = None;
|
||||
app.start("FusionCreateNewComponentCommand");
|
||||
app.start("component.create");
|
||||
let c = apply_commands(&app, app.dialog.as_ref().unwrap()).unwrap();
|
||||
assert_eq!(c[0].0, "FusionCreateNewComponentCommand");
|
||||
assert_eq!(c[0].0, "component.create");
|
||||
}
|
||||
|
||||
/// Round edges snap to their centre; straight ones are not circles.
|
||||
@@ -180,7 +177,7 @@ fn text_at(out: &egui::FullOutput, text: &str) -> Option<egui::Pos2> {
|
||||
#[test]
|
||||
fn ok_click_survives_a_busy_preview() {
|
||||
let mut app = two_boxes();
|
||||
app.start("JointAssembleCmdNew");
|
||||
app.start("joint.create");
|
||||
let mut d = app.dialog.take().unwrap();
|
||||
d.pick(&app.session, face_sel(&app.session, Vec3::new(10.0, 10.0, 10.0)).unwrap());
|
||||
d.pick(&app.session, face_sel(&app.session, Vec3::new(40.0, 10.0, 0.0)).unwrap());
|
||||
@@ -215,7 +212,7 @@ fn ok_click_survives_a_busy_preview() {
|
||||
#[test]
|
||||
fn face_snaps_mate_face_to_face() {
|
||||
let mut app = two_boxes();
|
||||
app.start("JointAssembleCmdNew");
|
||||
app.start("joint.create");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
d.pick(&app.session, face_sel(&app.session, Vec3::new(10.0, 10.0, 10.0)).unwrap());
|
||||
d.pick(&app.session, face_sel(&app.session, Vec3::new(40.0, 10.0, 10.0)).unwrap());
|
||||
@@ -246,12 +243,9 @@ fn face_snaps_mate_face_to_face() {
|
||||
fn drive_value_takes_the_keyboard() {
|
||||
let mut app = two_boxes();
|
||||
let (a, b) = (occ(&app, "A"), occ(&app, "B"));
|
||||
app.run(
|
||||
"JointAssembleCmdNew",
|
||||
json!({"type": "revolute", "a": {"occurrence": a, "face": [10, 10, 10]}, "b": {"occurrence": b, "face": [40, 10, 0]}}),
|
||||
)
|
||||
.unwrap();
|
||||
app.start("FusionMoveJointsCommand");
|
||||
app.run("joint.create", json!({"type": "revolute", "a": {"occurrence": a, "face": [10, 10, 10]}, "b": {"occurrence": b, "face": [40, 10, 0]}}))
|
||||
.unwrap();
|
||||
app.start("joint.drive");
|
||||
let ctx = egui::Context::default();
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1600.0, 1000.0));
|
||||
let frame = |app: &mut SolveApp, events: Vec<egui::Event>| {
|
||||
|
||||
@@ -92,8 +92,8 @@ pub fn start(app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
|
||||
(1..).map(|k| format!("{prefix}{k}")).find(|c| !taken.contains(c)).unwrap_or_default()
|
||||
};
|
||||
let (mo, inputs) = match id {
|
||||
"ContactSetCmd" => (Mo::ContactSet { name: n("Contact Set", "contact.list"), check: None }, vec![SelInput::new("Components", BODIES, true)]),
|
||||
"FusionMotionStudyCommand" => (
|
||||
"contact.create" => (Mo::ContactSet { name: n("Contact Set", "contact.list"), check: None }, vec![SelInput::new("Components", BODIES, true)]),
|
||||
"motion.study" => (
|
||||
Mo::MotionStudy {
|
||||
name: n("Motion Study", "motion.list"),
|
||||
study: None,
|
||||
@@ -108,7 +108,7 @@ pub fn start(app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
|
||||
vec![],
|
||||
),
|
||||
"explode.create" => (Mo::Explode { name: n("Exploded View", "explode.list"), scale: 1.0, shown: None, saved: None, error: None }, vec![]),
|
||||
"FusionShowDesignConfigPanelCmd" | "FusionStartDesignConfigModeCmd" => (Mo::Configurations { new_row: String::new(), error: None }, vec![]),
|
||||
"config.table" | "config.configure" => (Mo::Configurations { new_row: String::new(), error: None }, vec![]),
|
||||
_ => return None,
|
||||
};
|
||||
Some((Kind::Motion(mo), inputs))
|
||||
@@ -240,7 +240,7 @@ fn moved(before: &Session, after: &Session) -> BTreeMap<u64, Mat> {
|
||||
fn study_pose(app: &SolveApp, params: &Value, step: u32) -> Result<BTreeMap<u64, Mat>, String> {
|
||||
let mut sc = app.session.scratch();
|
||||
let name = params["study"].as_str().or(params["name"].as_str()).unwrap_or_default().to_string();
|
||||
sc.execute("FusionMotionStudyCommand", params).map_err(|e| e.to_string())?;
|
||||
sc.execute("motion.study", params).map_err(|e| e.to_string())?;
|
||||
sc.execute("motion.play", &json!({"study": name, "step": step})).map_err(|e| e.to_string())?;
|
||||
Ok(moved(&app.session, &sc))
|
||||
}
|
||||
@@ -272,7 +272,7 @@ pub fn rows(app: &mut SolveApp, ui: &mut egui::Ui, k: &mut Mo, inputs: &mut [Sel
|
||||
row_label(ui, "Contact");
|
||||
let mut on = list.get("enabled").and_then(Value::as_bool).unwrap_or(false);
|
||||
if ui.checkbox(&mut on, "enabled").changed() {
|
||||
let _ = app.run("EnableContactSetsCmd", json!({ "enabled": on }));
|
||||
let _ = app.run("contact.enable_sets", json!({ "enabled": on }));
|
||||
}
|
||||
ui.end_row();
|
||||
for s in list.get("sets").and_then(Value::as_array).cloned().unwrap_or_default() {
|
||||
@@ -471,7 +471,7 @@ pub fn rows(app: &mut SolveApp, ui: &mut egui::Ui, k: &mut Mo, inputs: &mut [Sel
|
||||
}
|
||||
}
|
||||
Mo::Configurations { new_row, error } => {
|
||||
let v = app.session.execute("FusionShowDesignConfigPanelCmd", &json!({})).unwrap_or(Value::Null);
|
||||
let v = app.session.execute("config.table", &json!({})).unwrap_or(Value::Null);
|
||||
let mut tb = table(&v);
|
||||
// Bare numbers show with their parameter's unit ("40" is 40 mm), as Fusion shows them.
|
||||
let kinds = app.session.doc.all_param_exprs();
|
||||
@@ -579,7 +579,7 @@ pub fn rows(app: &mut SolveApp, ui: &mut egui::Ui, k: &mut Mo, inputs: &mut [Sel
|
||||
// The first column starts the table (with a Default row of the current values).
|
||||
let (id, p) = if tb.columns.is_empty() {
|
||||
(
|
||||
"FusionStartDesignConfigModeCmd",
|
||||
"config.configure",
|
||||
match p.get("param") {
|
||||
Some(n) => json!({ "params": [n] }),
|
||||
None => json!({ "features": [p.get("feature")] }),
|
||||
@@ -651,10 +651,10 @@ pub fn commands(app: &SolveApp, k: &Mo, inputs: &[SelInput]) -> Result<Vec<(Stri
|
||||
// Only managing the sets: nothing to add.
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
vec![("ContactSetCmd".into(), json!({"name": name, "occurrences": occs}))]
|
||||
vec![("contact.create".into(), json!({"name": name, "occurrences": occs}))]
|
||||
}
|
||||
Mo::MotionStudy { name, study, steps, tracks, .. } => {
|
||||
vec![("FusionMotionStudyCommand".into(), study_params(&app.session, name, study.as_deref(), steps, tracks)?)]
|
||||
vec![("motion.study".into(), study_params(&app.session, name, study.as_deref(), steps, tracks)?)]
|
||||
}
|
||||
Mo::Explode { name, scale, .. } => {
|
||||
vec![("explode.create".into(), json!({"name": name, "scale": scale})), ("explode.show".into(), json!({ "view": name }))]
|
||||
@@ -723,11 +723,11 @@ mod tests {
|
||||
let mut s = Session::default();
|
||||
for (name, x) in [("A", 0), ("B", 30)] {
|
||||
s.execute("component.activate", &json!({"component": "root"})).unwrap();
|
||||
s.execute("FusionCreateNewComponentCommand", &json!({ "name": name })).unwrap();
|
||||
s.execute("PrimitiveBox", &json!({"length": 20, "width": 20, "height": 10, "corner": [x, 0, 0]})).unwrap();
|
||||
s.execute("component.create", &json!({ "name": name })).unwrap();
|
||||
s.execute("solid.box", &json!({"length": 20, "width": 20, "height": 10, "corner": [x, 0, 0]})).unwrap();
|
||||
}
|
||||
s.execute(
|
||||
"JointAssembleCmdNew",
|
||||
"joint.create",
|
||||
&json!({"type": "revolute", "a": {"occurrence": "A:1", "face": [10, 10, 10]}, "b": {"occurrence": "B:1", "face": [40, 10, 0]}}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Move/Copy: the move types (Free Move, Translate, Rotate, Point to Point, Point to Position),
|
||||
//! their rows and inputs, the triad on the canvas and the FusionMoveCommand they make. Nothing
|
||||
//! their rows and inputs, the triad on the canvas and the solid.move they make. Nothing
|
||||
//! moves until a value is typed or the triad is dragged.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
@@ -165,7 +165,7 @@ fn deg(v: f64) -> String {
|
||||
format!("{} deg", if s == "-0" || s.is_empty() { "0" } else { s })
|
||||
}
|
||||
|
||||
/// The FusionMoveCommand parameters (bodies, translate, axis, angle).
|
||||
/// The solid.move parameters (bodies, translate, axis, angle).
|
||||
pub fn params(app: &SolveApp, d: &Dialog) -> Result<Value, String> {
|
||||
let Kind::Move { mode, x, y, z, rx, ry, rz, angle } = &d.kind else { return Err("not a move".into()) };
|
||||
let bodies = body_names(d);
|
||||
|
||||
@@ -64,7 +64,7 @@ pub struct Pt {
|
||||
|
||||
/// The dialog for Insert Part.
|
||||
pub fn start(_app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
|
||||
matches!(id, "FusionFastenersCommand" | "parts.insert").then(|| {
|
||||
matches!(id, "parts.fastener" | "parts.insert").then(|| {
|
||||
(
|
||||
Kind::Part(Pt { family: 0, size: 0, length: None, name: String::new(), picture: None, lib: None }),
|
||||
vec![SelInput::new("Hole (optional)", FACES | EDGES, false)],
|
||||
@@ -207,7 +207,7 @@ pub fn commands(app: &SolveApp, k: &Pt, inputs: &[SelInput]) -> Result<Vec<(Stri
|
||||
};
|
||||
let p = insert_params(&lib, k, inputs.first().and_then(|i| i.items.first()))?;
|
||||
// The toolbar's command when the engine has it (same parameters).
|
||||
let id = if solvecraft_engine::find_command("FusionFastenersCommand").is_some() { "FusionFastenersCommand" } else { "parts.insert" };
|
||||
let id = if solvecraft_engine::find_command("parts.fastener").is_some() { "parts.fastener" } else { "parts.insert" };
|
||||
Ok(vec![(id.into(), p)])
|
||||
}
|
||||
|
||||
|
||||
@@ -125,16 +125,16 @@ fn lip(groove: bool) -> Pl {
|
||||
/// The dialog for a plastic command.
|
||||
pub fn start(app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
|
||||
let (pl, inputs) = match id {
|
||||
"FusionBossCommand" => (boss(), vec![SelInput::new("Position", PLANAR_FACES, false)]),
|
||||
"FusionLipCommand" => (lip(false), vec![SelInput::new("Rim face", FACES, false)]),
|
||||
"FusionSnapFitCommand" => (snap_fit(), vec![SelInput::new("Position", PLANAR_FACES, false)]),
|
||||
"FusionRestCommand" => (rest(), vec![SelInput::new("Position", PLANAR_FACES, false)]),
|
||||
"FusionManagePlasticRuleCommand" => {
|
||||
"plastic.boss" => (boss(), vec![SelInput::new("Position", PLANAR_FACES, false)]),
|
||||
"plastic.lip" => (lip(false), vec![SelInput::new("Rim face", FACES, false)]),
|
||||
"plastic.snap_fit" => (snap_fit(), vec![SelInput::new("Position", PLANAR_FACES, false)]),
|
||||
"plastic.rest" => (rest(), vec![SelInput::new("Position", PLANAR_FACES, false)]),
|
||||
"plastic.manage_rules" => {
|
||||
let rules = app.session.doc.plastic_rules();
|
||||
let pick = app.session.doc.plastic.active.as_ref().and_then(|a| rules.iter().position(|r| &r.name == a)).unwrap_or(0);
|
||||
(Pl::Rules { pick, name: String::new(), values: Vec::new(), active: false, loaded: None }, vec![])
|
||||
}
|
||||
"FusionAssignPlasticRuleCommand" => (Pl::Assign { rule: 1 }, vec![SelInput::new("Bodies", BODIES, true)]),
|
||||
"plastic.assign_rule" => (Pl::Assign { rule: 1 }, vec![SelInput::new("Bodies", BODIES, true)]),
|
||||
_ => return None,
|
||||
};
|
||||
Some((Kind::Plastic(pl), inputs))
|
||||
@@ -332,7 +332,7 @@ pub fn commands(app: &SolveApp, k: &Pl, inputs: &[SelInput], extra: &Map<String,
|
||||
if app.session.doc.eval(ribs, ValueKind::Unitless).is_ok_and(|n| n >= 1.0) {
|
||||
put(&mut p, &[("ribs", ribs), ("rib_thickness", rib_thickness), ("rib_length", rib_length), ("rib_offset", rib_offset)]);
|
||||
}
|
||||
("FusionBossCommand", p)
|
||||
("plastic.boss", p)
|
||||
}
|
||||
Pl::Lip { groove, width, height, gap, outside } => {
|
||||
let (body, at) = face.clone().ok_or("select the rim face first")?;
|
||||
@@ -341,7 +341,7 @@ pub fn commands(app: &SolveApp, k: &Pl, inputs: &[SelInput], extra: &Map<String,
|
||||
if *groove {
|
||||
put(&mut p, &[("gap", gap)]);
|
||||
}
|
||||
("FusionLipCommand", p)
|
||||
("plastic.lip", p)
|
||||
}
|
||||
Pl::SnapFit { hook, length, thickness, width, catch_depth, catch_length } => {
|
||||
let mut p = placed("a point on a face")?;
|
||||
@@ -356,7 +356,7 @@ pub fn commands(app: &SolveApp, k: &Pl, inputs: &[SelInput], extra: &Map<String,
|
||||
{
|
||||
p[key] = json!(v);
|
||||
}
|
||||
("FusionSnapFitCommand", p)
|
||||
("plastic.snap_fit", p)
|
||||
}
|
||||
Pl::Rest { round, width, length, height, draft, thickness } => {
|
||||
let mut p = placed("a point on a face")?;
|
||||
@@ -366,7 +366,7 @@ pub fn commands(app: &SolveApp, k: &Pl, inputs: &[SelInput], extra: &Map<String,
|
||||
p["length"] = json!(length);
|
||||
}
|
||||
put(&mut p, &[("draft", draft), ("thickness", thickness)]);
|
||||
("FusionRestCommand", p)
|
||||
("plastic.rest", p)
|
||||
}
|
||||
Pl::Rules { pick, name, values, active, .. } => {
|
||||
let rules = app.session.doc.plastic_rules();
|
||||
@@ -386,7 +386,7 @@ pub fn commands(app: &SolveApp, k: &Pl, inputs: &[SelInput], extra: &Map<String,
|
||||
p[*key] = json!(v);
|
||||
}
|
||||
}
|
||||
("FusionManagePlasticRuleCommand", p)
|
||||
("plastic.manage_rules", p)
|
||||
}
|
||||
Pl::Assign { rule } => {
|
||||
let bodies: Vec<String> = inputs
|
||||
@@ -399,7 +399,7 @@ pub fn commands(app: &SolveApp, k: &Pl, inputs: &[SelInput], extra: &Map<String,
|
||||
return Err("select bodies first".into());
|
||||
}
|
||||
let name = rule.checked_sub(1).and_then(|i| app.session.doc.plastic_rules().get(i).map(|r| r.name.clone())).unwrap_or_default();
|
||||
("FusionAssignPlasticRuleCommand", json!({"bodies": bodies, "rule": name}))
|
||||
("plastic.assign_rule", json!({"bodies": bodies, "rule": name}))
|
||||
}
|
||||
};
|
||||
if let Value::Object(m) = &mut p {
|
||||
@@ -465,13 +465,13 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
}
|
||||
}
|
||||
}
|
||||
(b, "FusionBossCommand", *position)
|
||||
(b, "plastic.boss", *position)
|
||||
}
|
||||
FeatureKind::Lip { face, width, height, groove, gap, outside, body } => {
|
||||
if let Some(b) = body {
|
||||
extra.insert("body".into(), json!(b));
|
||||
}
|
||||
(Pl::Lip { groove: *groove, width: width.clone(), height: height.clone(), gap: s(gap), outside: *outside }, "FusionLipCommand", *face)
|
||||
(Pl::Lip { groove: *groove, width: width.clone(), height: height.clone(), gap: s(gap), outside: *outside }, "plastic.lip", *face)
|
||||
}
|
||||
FeatureKind::SnapFit { position, direction, hook, length, thickness, width, catch_depth, catch_length, body } => {
|
||||
extra.insert("direction".into(), pt(*direction));
|
||||
@@ -490,7 +490,7 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
catch_depth: catch_depth.clone(),
|
||||
catch_length: catch_length.clone(),
|
||||
};
|
||||
(f, "FusionSnapFitCommand", *position)
|
||||
(f, "plastic.snap_fit", *position)
|
||||
}
|
||||
FeatureKind::Rest { position, direction, along, width, length, height, draft, thickness, body } => {
|
||||
extra.insert("direction".into(), pt(*direction));
|
||||
@@ -508,7 +508,7 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
draft: s(draft),
|
||||
thickness: s(thickness),
|
||||
};
|
||||
(r, "FusionRestCommand", *position)
|
||||
(r, "plastic.rest", *position)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
@@ -23,9 +23,9 @@ fn run_all(app: &mut SolveApp, c: Vec<(String, Value)>) {
|
||||
/// Edit Feature keeps the direction and refills the values.
|
||||
#[test]
|
||||
fn boss_from_a_face_pick() {
|
||||
let mut app = app_with(&[("PrimitiveBox", json!({"length": 60, "width": 60, "height": 3}))]);
|
||||
let mut app = app_with(&[("solid.box", json!({"length": 60, "width": 60, "height": 3}))]);
|
||||
app.session.selection = face_sel(&app.session, Vec3::new(30.0, 30.0, 3.0)).into_iter().collect();
|
||||
app.start("FusionBossCommand");
|
||||
app.start("plastic.boss");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
assert_eq!(d.primary().map(|p| p.0), Some("Height"));
|
||||
let c = apply_commands(&app, &d).unwrap();
|
||||
@@ -57,11 +57,11 @@ fn snap_lip_rest() {
|
||||
assert!(dirs.iter().all(|(_, d)| d.dot(n).abs() < 1e-12 && (d.len() - 1.0).abs() < 1e-12));
|
||||
}
|
||||
let mut app = app_with(&[
|
||||
("PrimitiveBox", json!({"length": 80, "width": 50, "height": 30})),
|
||||
("FusionShellBodyCommand", json!({"faces": [[40, 25, 30]], "thickness": 2})),
|
||||
("solid.box", json!({"length": 80, "width": 50, "height": 30})),
|
||||
("solid.shell", json!({"faces": [[40, 25, 30]], "thickness": 2})),
|
||||
]);
|
||||
app.session.selection = face_sel(&app.session, Vec3::new(1.0, 25.0, 30.0)).into_iter().collect();
|
||||
app.start("FusionLipCommand");
|
||||
app.start("plastic.lip");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
if let Kind::Plastic(Pl::Lip { groove, gap, .. }) = &mut d.kind {
|
||||
*groove = true;
|
||||
@@ -71,15 +71,15 @@ fn snap_lip_rest() {
|
||||
assert_eq!(c[0].1["type"], "groove");
|
||||
assert_eq!(c[0].1["gap"], "0.2 mm");
|
||||
run_all(&mut app, c);
|
||||
let mut app = app_with(&[("PrimitiveBox", json!({"length": 40, "width": 40, "height": 4}))]);
|
||||
let mut app = app_with(&[("solid.box", json!({"length": 40, "width": 40, "height": 4}))]);
|
||||
app.session.selection = face_sel(&app.session, Vec3::new(20.0, 20.0, 4.0)).into_iter().collect();
|
||||
app.start("FusionSnapFitCommand");
|
||||
app.start("plastic.snap_fit");
|
||||
let c = apply_commands(&app, app.dialog.as_ref().unwrap()).unwrap();
|
||||
assert_eq!(c[0].1["hook"], json!([1.0, 0.0, 0.0]));
|
||||
run_all(&mut app, c);
|
||||
app.dialog = None;
|
||||
app.session.selection = face_sel(&app.session, Vec3::new(5.0, 5.0, 4.0)).into_iter().collect();
|
||||
app.start("FusionRestCommand");
|
||||
app.start("plastic.rest");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
assert!(apply_commands(&app, &d).unwrap()[0].1.get("length").is_none(), "round");
|
||||
if let Kind::Plastic(Pl::Rest { round, .. }) = &mut d.kind {
|
||||
@@ -91,12 +91,12 @@ fn snap_lip_rest() {
|
||||
/// The rule manager edits a library rule into the design; Assign gives bodies a rule.
|
||||
#[test]
|
||||
fn plastic_rules_manage_and_assign() {
|
||||
let mut app = app_with(&[("PrimitiveBox", json!({"length": 10, "width": 10, "height": 10}))]);
|
||||
app.start("FusionManagePlasticRuleCommand");
|
||||
let mut app = app_with(&[("solid.box", json!({"length": 10, "width": 10, "height": 10}))]);
|
||||
app.start("plastic.manage_rules");
|
||||
let d = app.dialog.clone().unwrap();
|
||||
assert!(apply_commands(&app, &d).is_ok());
|
||||
app.session.selection = vec![Sel::Body { name: "Body1".into() }];
|
||||
app.start("FusionAssignPlasticRuleCommand");
|
||||
app.start("plastic.assign_rule");
|
||||
let c = apply_commands(&app, app.dialog.as_ref().unwrap()).unwrap();
|
||||
assert_eq!(c[0].1["bodies"], json!(["Body1"]));
|
||||
assert_eq!(c[0].1["rule"], "ABS (1.5mm)");
|
||||
|
||||
@@ -131,26 +131,22 @@ fn new_flange(ty: usize) -> Sm {
|
||||
/// The dialog for a sheet metal command.
|
||||
pub fn start(app: &SolveApp, id: &str) -> Option<(Kind, Vec<SelInput>)> {
|
||||
let (sm, inputs) = match id {
|
||||
"FusionSheetMetalFlangeCommand" => (new_flange(0), vec![SelInput::new("Profile / Edges", PROFILES | EDGES | CURVES, true)]),
|
||||
"FusionSheetMetalHemFlangeCommand" => {
|
||||
(Sm::Hem { length: "5 mm".into(), gap: String::new(), flip: false }, vec![SelInput::new("Edges", EDGES, true)])
|
||||
}
|
||||
"SheetMetalFoldCmd" => (
|
||||
"sheet.flange" => (new_flange(0), vec![SelInput::new("Profile / Edges", PROFILES | EDGES | CURVES, true)]),
|
||||
"sheet.hem" => (Sm::Hem { length: "5 mm".into(), gap: String::new(), flip: false }, vec![SelInput::new("Edges", EDGES, true)]),
|
||||
"sheet.fold" => (
|
||||
Sm::Fold { angle: "90 deg".into(), radius: String::new(), position: 0, flip: false },
|
||||
vec![SelInput::new("Bend Line", AXES, false), SelInput::new("Stationary Side", PLANAR_FACES, false)],
|
||||
),
|
||||
"FusionSheetmetalUnfoldCommand" => (Sm::Unfold { refold: false }, vec![SelInput::new("Body (last if none)", BODIES, false)]),
|
||||
"sheet.unfold" => (Sm::Unfold { refold: false }, vec![SelInput::new("Body (last if none)", BODIES, false)]),
|
||||
"sheet.refold" => (Sm::Unfold { refold: true }, vec![SelInput::new("Body (last if none)", BODIES, false)]),
|
||||
"ConvertToSheetMetalCmd" => (Sm::Convert { rule: 0 }, vec![SelInput::new("Face", PLANAR_FACES, false)]),
|
||||
"FusionSheetMetalRulesCommand" => {
|
||||
"sheet.convert" => (Sm::Convert { rule: 0 }, vec![SelInput::new("Face", PLANAR_FACES, false)]),
|
||||
"sheet.manage_rules" => {
|
||||
let active = app.session.doc.sheet.active.clone();
|
||||
let rules = rule_list(app);
|
||||
let pick = active.and_then(|a| rules.iter().position(|r| r.name == a)).unwrap_or(0);
|
||||
(Sm::Rules { pick, name: String::new(), values: Vec::new(), active: true, loaded: None }, vec![])
|
||||
}
|
||||
"FusionSheetMetalFlatPatternCmd" => {
|
||||
(Sm::FlatPattern { data: None, key: None, status: None }, vec![SelInput::new("Body (last if none)", BODIES, false)])
|
||||
}
|
||||
"sheet.flat_pattern" => (Sm::FlatPattern { data: None, key: None, status: None }, vec![SelInput::new("Body (last if none)", BODIES, false)]),
|
||||
_ => return None,
|
||||
};
|
||||
Some((Kind::Sheet(sm), inputs))
|
||||
@@ -307,7 +303,7 @@ pub fn rows(app: &mut SolveApp, ui: &mut egui::Ui, k: &mut Sm, inputs: &mut [Sel
|
||||
row_label(ui, "");
|
||||
if ui.button("Delete rule").clicked() {
|
||||
let n = rules.get(*pick).map(|r| r.name.clone()).unwrap_or_default();
|
||||
match app.run("FusionSheetMetalRulesCommand", json!({"name": n, "delete": true})) {
|
||||
match app.run("sheet.manage_rules", json!({"name": n, "delete": true})) {
|
||||
Ok(_) => {
|
||||
*pick = 0;
|
||||
*loaded = None;
|
||||
@@ -326,7 +322,7 @@ pub fn rows(app: &mut SolveApp, ui: &mut egui::Ui, k: &mut Sm, inputs: &mut [Sel
|
||||
if let Some(b) = &body {
|
||||
p["body"] = json!(b);
|
||||
}
|
||||
*data = Some(app.session.execute("FusionSheetMetalFlatPatternCmd", &p).map_err(|e| e.to_string()));
|
||||
*data = Some(app.session.execute("sheet.flat_pattern", &p).map_err(|e| e.to_string()));
|
||||
*key = Some(want);
|
||||
}
|
||||
match data {
|
||||
@@ -458,7 +454,7 @@ pub fn commands(app: &SolveApp, k: &Sm, inputs: &[SelInput], extra: &Map<String,
|
||||
if let Some(r) = rule_value(app, *rule).filter(|_| p["type"] != "edge") {
|
||||
p["rule"] = json!(r);
|
||||
}
|
||||
("FusionSheetMetalFlangeCommand", p)
|
||||
("sheet.flange", p)
|
||||
}
|
||||
Sm::Hem { length, gap, flip } => {
|
||||
let edges = edge_points(inputs);
|
||||
@@ -469,7 +465,7 @@ pub fn commands(app: &SolveApp, k: &Sm, inputs: &[SelInput], extra: &Map<String,
|
||||
if !gap.trim().is_empty() {
|
||||
p["gap"] = json!(gap);
|
||||
}
|
||||
("FusionSheetMetalHemFlangeCommand", p)
|
||||
("sheet.hem", p)
|
||||
}
|
||||
Sm::Fold { angle, radius, position, flip } => {
|
||||
let mut p = json!({"angle": angle, "position": FOLD_POSITIONS.get(*position).copied().unwrap_or("centerline"), "flip": flip});
|
||||
@@ -496,14 +492,14 @@ pub fn commands(app: &SolveApp, k: &Sm, inputs: &[SelInput], extra: &Map<String,
|
||||
if let Some(Sel::Face { point, .. }) = items(inputs, 1).first() {
|
||||
p["fixed"] = pt(*point);
|
||||
}
|
||||
("SheetMetalFoldCmd", p)
|
||||
("sheet.fold", p)
|
||||
}
|
||||
Sm::Unfold { refold } => {
|
||||
let mut p = json!({});
|
||||
if let Some(Sel::Body { name }) = items(inputs, 0).first() {
|
||||
p["body"] = json!(name);
|
||||
}
|
||||
(if *refold { "sheet.refold" } else { "FusionSheetmetalUnfoldCommand" }, p)
|
||||
(if *refold { "sheet.refold" } else { "sheet.unfold" }, p)
|
||||
}
|
||||
Sm::Convert { rule } => {
|
||||
let Some(Sel::Face { body, point, .. }) = items(inputs, 0).first() else { return Err("select the plate's large face first".into()) };
|
||||
@@ -511,7 +507,7 @@ pub fn commands(app: &SolveApp, k: &Sm, inputs: &[SelInput], extra: &Map<String,
|
||||
if let Some(r) = rule_value(app, *rule) {
|
||||
p["rule"] = json!(r);
|
||||
}
|
||||
("ConvertToSheetMetalCmd", p)
|
||||
("sheet.convert", p)
|
||||
}
|
||||
Sm::Rules { pick, name, values, active, .. } => {
|
||||
let rules = rule_list(app);
|
||||
@@ -528,7 +524,7 @@ pub fn commands(app: &SolveApp, k: &Sm, inputs: &[SelInput], extra: &Map<String,
|
||||
p[*k] = json!(v);
|
||||
}
|
||||
}
|
||||
("FusionSheetMetalRulesCommand", p)
|
||||
("sheet.manage_rules", p)
|
||||
}
|
||||
Sm::FlatPattern { .. } => return Ok(Vec::new()),
|
||||
};
|
||||
@@ -602,7 +598,7 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
position: FOLD_POSITIONS.iter().position(|x| x == position || (*x == "mould" && position == "mold")).unwrap_or(0),
|
||||
flip: *flip,
|
||||
};
|
||||
let (_, mut ins) = start(app, "SheetMetalFoldCmd")?;
|
||||
let (_, mut ins) = start(app, "sheet.fold")?;
|
||||
match curve {
|
||||
Some(c) => {
|
||||
if let Some(i) = ins.first_mut() {
|
||||
@@ -624,11 +620,11 @@ pub fn for_feature(app: &SolveApp, kind: &FeatureKind) -> Option<(Kind, Vec<SelI
|
||||
_ => return None,
|
||||
};
|
||||
let id = match &k {
|
||||
Sm::Flange { .. } => "FusionSheetMetalFlangeCommand",
|
||||
Sm::Hem { .. } => "FusionSheetMetalHemFlangeCommand",
|
||||
Sm::Unfold { refold: false } => "FusionSheetmetalUnfoldCommand",
|
||||
Sm::Flange { .. } => "sheet.flange",
|
||||
Sm::Hem { .. } => "sheet.hem",
|
||||
Sm::Unfold { refold: false } => "sheet.unfold",
|
||||
Sm::Unfold { .. } => "sheet.refold",
|
||||
_ => "ConvertToSheetMetalCmd",
|
||||
_ => "sheet.convert",
|
||||
};
|
||||
let (_, mut inputs) = start(app, id)?;
|
||||
if let Some(i) = inputs.first_mut() {
|
||||
|
||||
@@ -8,9 +8,9 @@ use crate::dialogs::{apply_commands, for_feature as edit_dialog};
|
||||
/// A 100 x 60 base flange on XY (2.5 mm steel).
|
||||
fn plate() -> SolveApp {
|
||||
let mut s = Session::default();
|
||||
s.execute("SketchCreate", &json!({"plane": "XY", "name": "Base"})).unwrap();
|
||||
s.execute("ShapeRectangleTwoPoint", &json!({"p0": [0, 0], "p1": [100, 60]})).unwrap();
|
||||
s.execute("SketchStop", &json!({})).unwrap();
|
||||
s.execute("sketch.create", &json!({"plane": "XY", "name": "Base"})).unwrap();
|
||||
s.execute("sketch.rectangle.two_point", &json!({"p0": [0, 0], "p1": [100, 60]})).unwrap();
|
||||
s.execute("sketch.finish", &json!({})).unwrap();
|
||||
SolveApp::new(s, Services::default())
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ fn flange_follows_the_picks() {
|
||||
let mut app = plate();
|
||||
let sk = app.session.doc.features[0].id;
|
||||
app.session.selection = vec![Sel::Profile { sketch: sk, index: 0 }];
|
||||
app.start("FusionSheetMetalFlangeCommand");
|
||||
app.start("sheet.flange");
|
||||
let d = app.dialog.clone().unwrap();
|
||||
let c = apply_commands(&app, &d).unwrap();
|
||||
assert_eq!(c[0].1["type"], "base");
|
||||
@@ -41,7 +41,7 @@ fn flange_follows_the_picks() {
|
||||
.collect();
|
||||
assert_eq!(edges.len(), 4);
|
||||
app.session.selection = edges;
|
||||
app.start("FusionSheetMetalFlangeCommand");
|
||||
app.start("sheet.flange");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
if let Kind::Sheet(Sm::Flange { height, position, .. }) = &mut d.kind {
|
||||
*height = "20 mm".into();
|
||||
@@ -63,27 +63,27 @@ fn flange_follows_the_picks() {
|
||||
assert_eq!(c[0].0, "timeline.redefine");
|
||||
assert_eq!(c[0].1["params"]["height"], "20 mm");
|
||||
// Flat pattern: four bends; Unfold previews.
|
||||
let flat = app.session.execute("FusionSheetMetalFlatPatternCmd", &json!({})).unwrap();
|
||||
let flat = app.session.execute("sheet.flat_pattern", &json!({})).unwrap();
|
||||
assert_eq!(flat["bends"].as_array().map(Vec::len), Some(4), "{flat}");
|
||||
app.dialog = None;
|
||||
app.start("FusionSheetmetalUnfoldCommand");
|
||||
assert_eq!(apply_commands(&app, app.dialog.as_ref().unwrap()).unwrap()[0].0, "FusionSheetmetalUnfoldCommand");
|
||||
app.start("sheet.unfold");
|
||||
assert_eq!(apply_commands(&app, app.dialog.as_ref().unwrap()).unwrap()[0].0, "sheet.unfold");
|
||||
}
|
||||
|
||||
/// Hem takes edges and an optional gap; rules make and pick a rule with its expressions.
|
||||
#[test]
|
||||
fn hem_and_rules() {
|
||||
let mut app = plate();
|
||||
app.run("FusionSheetMetalFlangeCommand", json!({"sketch": "Base"})).unwrap();
|
||||
app.run("sheet.flange", json!({"sketch": "Base"})).unwrap();
|
||||
app.session.selection = edge_sel(&app.session, Vec3::new(50.0, 0.0, 2.5)).into_iter().collect();
|
||||
app.start("FusionSheetMetalHemFlangeCommand");
|
||||
app.start("sheet.hem");
|
||||
let d = app.dialog.clone().unwrap();
|
||||
let c = apply_commands(&app, &d).unwrap();
|
||||
assert_eq!(c[0].1["length"], "5 mm");
|
||||
assert!(c[0].1.get("gap").is_none(), "the rule's gap");
|
||||
run_all(&mut app, c);
|
||||
app.dialog = None;
|
||||
app.start("FusionSheetMetalRulesCommand");
|
||||
app.start("sheet.manage_rules");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
if let Kind::Sheet(Sm::Rules { pick, name, values, loaded, .. }) = &mut d.kind {
|
||||
*pick = 1;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//! A click selects a dimension (Delete removes it), a drag moves its text (and so the offset of
|
||||
//! its dimension line), a double-click edits its value in place with parameter completion.
|
||||
//! Everything acts through commands: `sketch.dimension_text`, `sketch.delete`,
|
||||
//! `ChangeParameterCommand`.
|
||||
//! `parameters.change`.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
@@ -321,7 +321,7 @@ fn edit_box(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
e.fresh = e.fresh.saturating_sub(1);
|
||||
match done {
|
||||
Some(true) => {
|
||||
if let Err(err) = app.run("ChangeParameterCommand", json!({"name": e.param, "expression": e.expr})) {
|
||||
if let Err(err) = app.run("parameters.change", json!({"name": e.param, "expression": e.expr})) {
|
||||
app.set_status(err, true);
|
||||
EDIT.with(|x| *x.borrow_mut() = Some(Edit { fresh: FRESH, ..e }));
|
||||
}
|
||||
|
||||
@@ -445,20 +445,20 @@ mod tests {
|
||||
#[test]
|
||||
fn tabs_keep_their_own_design() {
|
||||
let mut a = app();
|
||||
a.run("PrimitiveBox", json!({"length": 10, "width": 10, "height": 10})).unwrap();
|
||||
a.run("solid.box", json!({"length": 10, "width": 10, "height": 10})).unwrap();
|
||||
a.session.selection = vec![solvecraft_engine::Sel::Body { name: "Body1".into() }];
|
||||
a.cam.distance = 123.0;
|
||||
new_design(&mut a);
|
||||
assert_eq!(a.docs.count(), 2);
|
||||
assert_eq!(a.docs.active, 1);
|
||||
assert!(a.session.doc.features.is_empty() && a.session.selection.is_empty());
|
||||
a.run("PrimitiveSphere", json!({"diameter": 8})).unwrap();
|
||||
a.run("PrimitiveSphere", json!({"diameter": 4})).unwrap();
|
||||
a.run("solid.sphere", json!({"diameter": 8})).unwrap();
|
||||
a.run("solid.sphere", json!({"diameter": 4})).unwrap();
|
||||
switch(&mut a, 0);
|
||||
assert_eq!(a.session.doc.features.len(), 1);
|
||||
assert_eq!(a.session.selection.len(), 1);
|
||||
assert!((a.cam.distance - 123.0).abs() < 1e-9);
|
||||
a.run("UndoCommand", json!({})).unwrap();
|
||||
a.run("edit.undo", json!({})).unwrap();
|
||||
assert!(a.session.doc.features.is_empty(), "undo is this design's");
|
||||
switch(&mut a, 1);
|
||||
assert_eq!(a.session.doc.features.len(), 2, "the other design kept its features");
|
||||
|
||||
@@ -200,9 +200,9 @@ pub fn release(app: &mut SolveApp) {
|
||||
let Some(s) = current() else { return };
|
||||
clear();
|
||||
let (cmd, params) = match &s.target {
|
||||
Target::Point(t) => ("ConstraintCoincident", json!({"a": s.point, "b": t})),
|
||||
Target::Mid(l) => ("ConstraintMidPoint", json!({"point": s.point, "line": l})),
|
||||
Target::On(c) => ("ConstraintCoincident", json!({"a": s.point, "b": c})),
|
||||
Target::Point(t) => ("sketch.constraint.coincident", json!({"a": s.point, "b": t})),
|
||||
Target::Mid(l) => ("sketch.constraint.midpoint", json!({"point": s.point, "line": l})),
|
||||
Target::On(c) => ("sketch.constraint.coincident", json!({"a": s.point, "b": c})),
|
||||
Target::Align => return,
|
||||
};
|
||||
// A snap that would over-constrain the sketch is simply not kept.
|
||||
|
||||
@@ -157,10 +157,10 @@ pub fn commit(app: &mut SolveApp, line: &str, end: Vec2) {
|
||||
let Some(i) = current().filter(|i| i.at.dist(end) < 1e-9) else { return };
|
||||
clear();
|
||||
let (cmd, params) = match i.kind {
|
||||
Kind::Parallel => ("ConstraintParallel", json!({"a": i.curve, "b": line})),
|
||||
Kind::Perpendicular => ("ConstraintPerpendicular", json!({"a": i.curve, "b": line})),
|
||||
Kind::Tangent => ("ConstraintTangent", json!({"a": i.curve, "b": line})),
|
||||
Kind::Extension => ("ConstraintCoincident", json!({"a": format!("{line}.end"), "b": i.curve})),
|
||||
Kind::Parallel => ("sketch.constraint.parallel", json!({"a": i.curve, "b": line})),
|
||||
Kind::Perpendicular => ("sketch.constraint.perpendicular", json!({"a": i.curve, "b": line})),
|
||||
Kind::Tangent => ("sketch.constraint.tangent", json!({"a": i.curve, "b": line})),
|
||||
Kind::Extension => ("sketch.constraint.coincident", json!({"a": format!("{line}.end"), "b": i.curve})),
|
||||
};
|
||||
// An inference that would over-constrain the sketch is simply not kept.
|
||||
let _ = app.session.execute(cmd, ¶ms);
|
||||
@@ -168,7 +168,7 @@ pub fn commit(app: &mut SolveApp, line: &str, end: Vec2) {
|
||||
|
||||
/// The dashed guide of the current inference (its label is the snap hint) (while the Line tool is active).
|
||||
pub fn show(app: &SolveApp, painter: &egui::Painter, proj: &Proj) {
|
||||
if app.tool.as_ref().is_none_or(|t| t.cmd != "DrawPolyline") {
|
||||
if app.tool.as_ref().is_none_or(|t| t.cmd != "sketch.line") {
|
||||
return;
|
||||
}
|
||||
let Some(i) = current() else { return };
|
||||
|
||||
@@ -37,10 +37,15 @@ impl Keymap {
|
||||
}
|
||||
pub fn load(&mut self, v: &Value) {
|
||||
self.custom =
|
||||
v.as_object().map(|o| o.iter().take(2000).filter_map(|(k, v)| Some((k.clone(), v.as_str()?.to_string()))).collect()).unwrap_or_default();
|
||||
v.as_object().map(|o| o.iter().take(2000).filter_map(|(k, v)| Some((current(k), v.as_str()?.to_string()))).collect()).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
|
||||
/// Saved before the command ids were renamed: the current id.
|
||||
fn current(id: &str) -> String {
|
||||
solvecraft_engine::legacy_ids::current_id(id).to_string()
|
||||
}
|
||||
|
||||
/// A key press as a shortcut name: `E`, `Shift+E`, `Ctrl+Alt+K`, `F5`, `Delete`.
|
||||
pub fn key_name(key: egui::Key, mods: egui::Modifiers) -> String {
|
||||
let mut s = String::new();
|
||||
@@ -250,24 +255,24 @@ mod tests {
|
||||
#[test]
|
||||
fn rebinding_detects_conflicts_and_resets() {
|
||||
let mut a = app();
|
||||
assert_eq!(effective(&a, "Extrude").as_deref(), Some("E"));
|
||||
assert_eq!(effective(&a, "solid.extrude").as_deref(), Some("E"));
|
||||
// Shift+E is free.
|
||||
bind(&mut a, "Extrude", "Shift+E", false).unwrap();
|
||||
assert_eq!(commands_for(&a, "Shift+E"), vec!["Extrude"]);
|
||||
bind(&mut a, "solid.extrude", "Shift+E", false).unwrap();
|
||||
assert_eq!(commands_for(&a, "Shift+E"), vec!["solid.extrude"]);
|
||||
assert!(commands_for(&a, "E").is_empty());
|
||||
// M belongs to Move/Copy: refused, then taken over on request.
|
||||
let e = bind(&mut a, "Revolve", "M", false).unwrap_err();
|
||||
let e = bind(&mut a, "solid.revolve", "M", false).unwrap_err();
|
||||
assert!(e.contains("already used by Move/Copy"), "{e}");
|
||||
bind(&mut a, "Revolve", "M", true).unwrap();
|
||||
assert_eq!(commands_for(&a, "M"), vec!["Revolve"]);
|
||||
assert_eq!(effective(&a, "FusionMoveCommand"), None);
|
||||
assert!(bind(&mut a, "Revolve", "Ctrl+Z", false).is_err(), "reserved");
|
||||
bind(&mut a, "solid.revolve", "M", true).unwrap();
|
||||
assert_eq!(commands_for(&a, "M"), vec!["solid.revolve"]);
|
||||
assert_eq!(effective(&a, "solid.move"), None);
|
||||
assert!(bind(&mut a, "solid.revolve", "Ctrl+Z", false).is_err(), "reserved");
|
||||
// Kept between runs.
|
||||
let mut b = app();
|
||||
b.keymap.load(&a.keymap.prefs());
|
||||
assert_eq!(effective(&b, "Revolve").as_deref(), Some("M"));
|
||||
assert_eq!(effective(&b, "solid.revolve").as_deref(), Some("M"));
|
||||
reset(&mut b, None);
|
||||
assert_eq!(effective(&b, "FusionMoveCommand").as_deref(), Some("M"));
|
||||
assert_eq!(effective(&b, "solid.move").as_deref(), Some("M"));
|
||||
assert_eq!(key_name(egui::Key::K, egui::Modifiers { ctrl: true, shift: true, ..Default::default() }), "Ctrl+Shift+K");
|
||||
}
|
||||
}
|
||||
+16
-16
@@ -353,11 +353,11 @@ impl SolveApp {
|
||||
match &r {
|
||||
Ok(_) => {
|
||||
self.session.echo(format!("{id}: done"));
|
||||
if matches!(id, "NewDocumentCommand" | "doc.open") {
|
||||
if matches!(id, "file.new" | "doc.open") {
|
||||
self.cam = Camera::default();
|
||||
self.fit_view();
|
||||
}
|
||||
if matches!(id, "doc.open" | "SaveDocumentCommand" | "SaveDocumentAsCommand")
|
||||
if matches!(id, "doc.open" | "file.save" | "file.save_as")
|
||||
&& let Some(p) = self.session.path.clone()
|
||||
{
|
||||
self.home.add_recent(&p);
|
||||
@@ -387,7 +387,7 @@ impl SolveApp {
|
||||
}
|
||||
self.tool = None;
|
||||
self.home.open = false;
|
||||
if matches!(id, "FusionImportCommandFromToolbar" | "ParaMeshInsertAlignCommand") {
|
||||
if matches!(id, "file.insert_step" | "file.insert_mesh") {
|
||||
if let Some(p) = self.services.pick_open.as_ref().and_then(|f| f()) {
|
||||
self.insert_path(&p);
|
||||
}
|
||||
@@ -396,21 +396,21 @@ impl SolveApp {
|
||||
if !sketch_tools::start_hook(self, id) {
|
||||
return;
|
||||
}
|
||||
if id == "ChangeParameterCommand" {
|
||||
if id == "parameters.change" {
|
||||
params_dialog::open(self);
|
||||
return;
|
||||
}
|
||||
if id == "SketchStop" {
|
||||
if id == "sketch.finish" {
|
||||
self.finish_sketch();
|
||||
return;
|
||||
}
|
||||
// Press Pull: edges get a fillet, faces and profiles an extrude.
|
||||
if id == "FusionPressPullCommand" {
|
||||
if id == "solid.press_pull" {
|
||||
let edges = self.session.selection.iter().any(|s| matches!(s, solvecraft_engine::Sel::Edge { .. }));
|
||||
return self.start(if edges { "FusionFilletEdgesCommand" } else { "Extrude" });
|
||||
return self.start(if edges { "solid.fillet" } else { "solid.extrude" });
|
||||
}
|
||||
// Fix/Unfix: the selected sketch points and curves change at once, otherwise pick them.
|
||||
if id == "ConstraintFix" {
|
||||
if id == "sketch.constraint.fix" {
|
||||
let ents: Vec<String> = self
|
||||
.session
|
||||
.selection
|
||||
@@ -433,7 +433,7 @@ impl SolveApp {
|
||||
}
|
||||
if let Some(spec) = solvecraft_engine::find_command(id)
|
||||
&& (tools::Tool::for_command(id).is_some() || dialogs::Dialog::for_command(self, id).is_some())
|
||||
&& id != "SketchCreate"
|
||||
&& id != "sketch.create"
|
||||
{
|
||||
self.last_command = Some((spec.id.to_string(), spec.label.to_string()));
|
||||
}
|
||||
@@ -468,13 +468,13 @@ impl SolveApp {
|
||||
let Some(idx) = self.session.doc.feature_index(id) else { return };
|
||||
let marker = self.session.doc.marker;
|
||||
self.tool = None;
|
||||
if self.run("timeline.rollTo", json!({ "position": idx })).is_err() {
|
||||
if self.run("timeline.roll_to", json!({ "position": idx })).is_err() {
|
||||
return;
|
||||
}
|
||||
match dialogs::for_feature(self, id, marker) {
|
||||
Some(d) => self.dialog = Some(d),
|
||||
None => {
|
||||
let _ = self.run("timeline.rollTo", marker.map(|m| json!({ "position": m })).unwrap_or_else(|| json!({})));
|
||||
let _ = self.run("timeline.roll_to", marker.map(|m| json!({ "position": m })).unwrap_or_else(|| json!({})));
|
||||
self.palette.text = format!("timeline.edit {{\"feature\": {id}, \"set\": {{}}}}");
|
||||
self.ui.palette_open = true;
|
||||
}
|
||||
@@ -496,9 +496,9 @@ impl SolveApp {
|
||||
self.dialog = None;
|
||||
let Some(idx) = self.session.doc.feature_index(id) else { return };
|
||||
let marker = self.session.doc.marker;
|
||||
let rolled = marker.is_none_or(|m| m > idx + 1) && self.run("timeline.rollTo", json!({ "position": idx + 1 })).is_ok();
|
||||
let rolled = marker.is_none_or(|m| m > idx + 1) && self.run("timeline.roll_to", json!({ "position": idx + 1 })).is_ok();
|
||||
let before = self.cam;
|
||||
if self.run("SketchActivate", json!({ "sketch": id })).is_err() {
|
||||
if self.run("sketch.edit", json!({ "sketch": id })).is_err() {
|
||||
if rolled {
|
||||
self.restore_marker(marker);
|
||||
}
|
||||
@@ -513,7 +513,7 @@ impl SolveApp {
|
||||
/// return to the view from before the sketch.
|
||||
pub fn finish_sketch(&mut self) {
|
||||
self.tool = None;
|
||||
let _ = self.run("SketchStop", json!({}));
|
||||
let _ = self.run("sketch.finish", json!({}));
|
||||
if let Some(marker) = self.pre_sketch_marker.take() {
|
||||
self.restore_marker(marker);
|
||||
}
|
||||
@@ -523,7 +523,7 @@ impl SolveApp {
|
||||
}
|
||||
|
||||
fn restore_marker(&mut self, marker: Option<usize>) {
|
||||
let _ = self.run("timeline.rollTo", marker.map(|m| json!({ "position": m })).unwrap_or_else(|| json!({})));
|
||||
let _ = self.run("timeline.roll_to", marker.map(|m| json!({ "position": m })).unwrap_or_else(|| json!({})));
|
||||
}
|
||||
|
||||
/// Everything shown as selected: the selection plus the open dialog's inputs.
|
||||
@@ -555,7 +555,7 @@ impl SolveApp {
|
||||
/// Insert a STEP file's bodies (an Import base feature) or a 3MF/STL file's meshes into the
|
||||
/// current design.
|
||||
pub fn insert_path(&mut self, path: &str) {
|
||||
let cmd = if solvecraft_engine::io::is_mesh_path(path) { "ParaMeshInsertAlignCommand" } else { "FusionImportCommandFromToolbar" };
|
||||
let cmd = if solvecraft_engine::io::is_mesh_path(path) { "file.insert_mesh" } else { "file.insert_step" };
|
||||
if let Ok(r) = self.run(cmd, json!({ "path": path })) {
|
||||
self.fit_view();
|
||||
if let Some(w) = r["warnings"].as_array().filter(|w| !w.is_empty()) {
|
||||
|
||||
@@ -64,7 +64,7 @@ fn face_menu_hides_and_finds_the_body() {
|
||||
#[test]
|
||||
fn body_menu_isolates_renames_locks_and_deletes() {
|
||||
let mut app = sample_app();
|
||||
app.run("PrimitiveBox", json!({"length": 5, "width": 5, "height": 5, "corner": [200, 0, 0]})).unwrap();
|
||||
app.run("solid.box", json!({"length": 5, "width": 5, "height": 5, "corner": [200, 0, 0]})).unwrap();
|
||||
let b = body(&app);
|
||||
let t = Target::Body { name: b.clone() };
|
||||
let items = context_menu::items(&app, &t);
|
||||
@@ -119,9 +119,9 @@ fn sketch_menu_hides_and_moves_to_another_plane() {
|
||||
#[test]
|
||||
fn sketch_entities_offer_the_constraints_that_fit() {
|
||||
let mut app = sample_app();
|
||||
app.run("SketchCreate", json!({"plane": "XY"})).unwrap();
|
||||
app.run("DrawPolyline", json!({"points": [[0, 0], [10, 1]]})).unwrap();
|
||||
app.run("DrawPolyline", json!({"points": [[0, 5], [10, 7]]})).unwrap();
|
||||
app.run("sketch.create", json!({"plane": "XY"})).unwrap();
|
||||
app.run("sketch.line", json!({"points": [[0, 0], [10, 1]]})).unwrap();
|
||||
app.run("sketch.line", json!({"points": [[0, 5], [10, 7]]})).unwrap();
|
||||
let ids: Vec<String> = {
|
||||
let st = app.session.model.state();
|
||||
let ss = st.sketch(app.session.active_sketch.unwrap()).unwrap();
|
||||
@@ -141,7 +141,7 @@ fn sketch_entities_offer_the_constraints_that_fit() {
|
||||
fn component_menu_activates_grounds_and_pastes() {
|
||||
let mut app = sample_app();
|
||||
let b = body(&app);
|
||||
app.run("FusionCreateComponentsFromBodiesCommand", json!({"bodies": [b]})).unwrap();
|
||||
app.run("component.from_bodies", json!({"bodies": [b]})).unwrap();
|
||||
let c = app.session.doc.components[0].id;
|
||||
let t = Target::Component { id: c };
|
||||
act(&mut app, &t, "Activate");
|
||||
@@ -169,7 +169,7 @@ fn pick_runs_an_item_of_the_open_menu() {
|
||||
#[test]
|
||||
fn folder_and_group_menus_make_rename_and_drop_groups() {
|
||||
let mut app = sample_app();
|
||||
app.run("PrimitiveBox", json!({"length": 5, "width": 5, "height": 5, "corner": [200, 0, 0]})).unwrap();
|
||||
app.run("solid.box", json!({"length": 5, "width": 5, "height": 5, "corner": [200, 0, 0]})).unwrap();
|
||||
let names: Vec<String> = app.session.model.state().bodies.iter().map(|b| b.name.clone()).collect();
|
||||
let items: Vec<_> = names.iter().map(|n| json!({"type": "body", "name": n})).collect();
|
||||
app.run("select.set", json!({ "items": items })).unwrap();
|
||||
@@ -206,8 +206,8 @@ fn browser_folds_are_kept_in_the_preferences() {
|
||||
fn delete_key_handles_every_kind_and_asks_about_dependents() {
|
||||
use solvecraft_engine::Sel;
|
||||
let mut app = sample_app();
|
||||
app.run("ConstructionPlaneOffsetFromPlaneCommand", json!({"base": "XY", "offset": 10})).unwrap();
|
||||
app.run("PrimitiveBox", json!({"length": 5, "width": 5, "height": 5, "corner": [200, 0, 0]})).unwrap();
|
||||
app.run("construct.plane.offset", json!({"base": "XY", "offset": 10})).unwrap();
|
||||
app.run("solid.box", json!({"length": 5, "width": 5, "height": 5, "corner": [200, 0, 0]})).unwrap();
|
||||
let n = app.session.doc.features.len();
|
||||
// A body and a construction plane together: one undo step, no question.
|
||||
let b = app.session.model.state().bodies.last().unwrap().name.clone();
|
||||
@@ -216,7 +216,7 @@ fn delete_key_handles_every_kind_and_asks_about_dependents() {
|
||||
assert!(app.menu.confirm.is_none());
|
||||
assert!(app.session.model.state().body(&b).is_none());
|
||||
assert!(app.session.doc.find_feature("Plane1").is_none());
|
||||
app.run("UndoCommand", json!({})).unwrap();
|
||||
app.run("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(app.session.doc.features.len(), n);
|
||||
// A sketch with features built on it: the confirmation lists them first.
|
||||
let sk = app.session.doc.features.iter().find(|f| f.name == "Base").unwrap().id;
|
||||
@@ -229,7 +229,7 @@ fn delete_key_handles_every_kind_and_asks_about_dependents() {
|
||||
crate::delete::delete_selection(&mut app);
|
||||
crate::delete::confirm(&mut app, true);
|
||||
assert!(app.session.doc.features.len() < n);
|
||||
app.run("UndoCommand", json!({})).unwrap();
|
||||
app.run("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(app.session.doc.features.len(), n);
|
||||
// A face alone: not available yet, nothing changes.
|
||||
let b0 = app.session.model.state().bodies[0].name.clone();
|
||||
@@ -243,7 +243,7 @@ fn delete_key_handles_every_kind_and_asks_about_dependents() {
|
||||
fn menu_delete_items_use_the_same_path() {
|
||||
let mut app = sample_app();
|
||||
let b = body(&app);
|
||||
app.run("FusionCreateComponentsFromBodiesCommand", json!({"bodies": [b]})).unwrap();
|
||||
app.run("component.from_bodies", json!({"bodies": [b]})).unwrap();
|
||||
let c = app.session.doc.components[0].id;
|
||||
act(&mut app, &Target::Component { id: c }, "Delete");
|
||||
assert!(app.session.doc.components.is_empty());
|
||||
@@ -261,7 +261,7 @@ fn canvas_menu_hides_renames_calibrates_and_deletes() {
|
||||
png.extend(200u32.to_be_bytes());
|
||||
png.extend([8, 6, 0, 0, 0, 0, 0, 0, 0]);
|
||||
let data = solvecraft_engine::doc::canvas::base64_encode(&png);
|
||||
let id = app.run("FusionAddCanvasCommand", json!({"data": data, "plane": "XY", "width": 80})).unwrap()["canvas"].as_u64().unwrap();
|
||||
let id = app.run("canvas.insert", json!({"data": data, "plane": "XY", "width": 80})).unwrap()["canvas"].as_u64().unwrap();
|
||||
let t = Target::Canvas { id };
|
||||
act(&mut app, &t, "Show/Hide");
|
||||
assert!(!app.session.doc.canvases[0].visible);
|
||||
@@ -273,7 +273,7 @@ fn canvas_menu_hides_renames_calibrates_and_deletes() {
|
||||
assert!(p.calibrate && (p.distance - 80.0).abs() < 1e-9, "starts from the image's width");
|
||||
act(&mut app, &t, "Delete");
|
||||
assert!(app.session.doc.canvases.is_empty());
|
||||
app.run("UndoCommand", json!({})).unwrap();
|
||||
app.run("edit.undo", json!({})).unwrap();
|
||||
assert_eq!(app.session.doc.canvases.len(), 1);
|
||||
// A canvas picked in the browser goes with the Delete key.
|
||||
app.tree.picked_canvases = vec![id];
|
||||
@@ -373,7 +373,7 @@ fn v_toggles_what_is_selected() {
|
||||
assert!(app.ui.hidden_bodies.is_empty() && app.ui.hidden_origin.is_empty());
|
||||
assert!(crate::browser::sketch_visible(&app, sk));
|
||||
// Components picked in the browser hide their bodies.
|
||||
app.run("FusionCreateComponentsFromBodiesCommand", json!({"bodies": [b.clone()]})).unwrap();
|
||||
app.run("component.from_bodies", json!({"bodies": [b.clone()]})).unwrap();
|
||||
app.run("select.clear", json!({})).unwrap();
|
||||
app.tree.picked_components = vec![app.session.doc.components[0].id];
|
||||
context_menu::toggle_visibility(&mut app);
|
||||
@@ -421,16 +421,16 @@ fn units_menu_changes_the_design_units_and_new_designs_take_the_preference() {
|
||||
fn an_uncaptured_move_asks_before_the_next_command() {
|
||||
let mut app = sample_app();
|
||||
let b = body(&app);
|
||||
app.run("FusionCreateComponentsFromBodiesCommand", json!({"bodies": [b]})).unwrap();
|
||||
app.run("component.from_bodies", json!({"bodies": [b]})).unwrap();
|
||||
let occ = app.session.doc.occurrences[0].id;
|
||||
app.run("occurrence.move", json!({"occurrence": occ, "translate": [10, 0, 0], "capture": false})).unwrap();
|
||||
app.start("SketchCreate");
|
||||
assert_eq!(app.tree.capture_prompt.as_deref(), Some("SketchCreate"), "asks first");
|
||||
app.start("sketch.create");
|
||||
assert_eq!(app.tree.capture_prompt.as_deref(), Some("sketch.create"), "asks first");
|
||||
assert!(app.dialog.is_none());
|
||||
crate::browser::answer_capture(&mut app, "cancel").unwrap();
|
||||
assert!(app.tree.capture_prompt.is_none() && !app.session.pending_moves.is_empty());
|
||||
// Revert, then the command goes ahead.
|
||||
app.start("SketchCreate");
|
||||
app.start("sketch.create");
|
||||
crate::browser::answer_capture(&mut app, "revert").unwrap();
|
||||
assert!(app.session.pending_moves.is_empty());
|
||||
assert!(app.dialog.is_some(), "Create Sketch started");
|
||||
|
||||
@@ -46,7 +46,7 @@ impl ParamsDialog {
|
||||
/// The command the live preview should run (a valid expression being edited).
|
||||
pub fn preview_commands(&self) -> Option<Vec<(String, Value)>> {
|
||||
let (n, e) = self.preview.as_ref()?;
|
||||
Some(vec![("ChangeParameterCommand".to_string(), json!({"name": n, "expression": e}))])
|
||||
Some(vec![("parameters.change".to_string(), json!({"name": n, "expression": e}))])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ fn table(app: &mut SolveApp, ui: &mut egui::Ui, d: &mut ParamsDialog, rows: &[&R
|
||||
if sel != before
|
||||
&& let Some(u) = UNITS.get(sel)
|
||||
{
|
||||
d.error = app.run("ChangeParameterCommand", json!({"name": r.name, "expression": r.expression, "unit": u})).err();
|
||||
d.error = app.run("parameters.change", json!({"name": r.name, "expression": r.expression, "unit": u})).err();
|
||||
}
|
||||
} else {
|
||||
ui.label(RichText::new(unit_label(&r.unit)).color(t.text_dim));
|
||||
@@ -281,7 +281,7 @@ fn table(app: &mut SolveApp, ui: &mut egui::Ui, d: &mut ParamsDialog, rows: &[&R
|
||||
if resp.lost_focus() {
|
||||
d.preview = None;
|
||||
if e != r.expression {
|
||||
let res = app.run("ChangeParameterCommand", json!({"name": r.name, "expression": e}));
|
||||
let res = app.run("parameters.change", json!({"name": r.name, "expression": e}));
|
||||
d.error = res.err();
|
||||
if d.error.is_none() {
|
||||
d.editing.remove(&r.name);
|
||||
|
||||
@@ -393,7 +393,7 @@ mod tests {
|
||||
#[test]
|
||||
fn orbiting_around_the_cursor_keeps_that_point_still() {
|
||||
let mut app = SolveApp::new(solvecraft_engine::Session::default(), Default::default());
|
||||
app.run("PrimitiveBox", json!({"length": 40, "width": 40, "height": 40})).unwrap();
|
||||
app.run("solid.box", json!({"length": 40, "width": 40, "height": 40})).unwrap();
|
||||
app.preferences.orbit_cursor = true;
|
||||
let rect = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(800.0, 600.0));
|
||||
app.fit_view();
|
||||
|
||||
@@ -15,7 +15,7 @@ fn app_with(script: serde_json::Value) -> SolveApp {
|
||||
}
|
||||
|
||||
fn boxed() -> SolveApp {
|
||||
app_with(json!([{"command": "PrimitiveBox", "params": {"length": 40, "width": 30, "height": 20}}]))
|
||||
app_with(json!([{"command": "solid.box", "params": {"length": 40, "width": 30, "height": 20}}]))
|
||||
}
|
||||
|
||||
/// The face of Body1 whose centre is at `c`.
|
||||
@@ -42,13 +42,13 @@ fn start(app: &mut SolveApp, sels: Vec<Sel>, id: &str) -> Dialog {
|
||||
#[test]
|
||||
fn profiles_then_extrude_takes_them_all() {
|
||||
let mut app = app_with(json!([
|
||||
{"command": "SketchCreate", "params": {"plane": "XY"}},
|
||||
{"command": "CircleCenterRadius", "params": {"center": [0, 0], "radius": 5}},
|
||||
{"command": "CircleCenterRadius", "params": {"center": [20, 0], "radius": 5}},
|
||||
{"command": "SketchStop", "params": {}},
|
||||
{"command": "sketch.create", "params": {"plane": "XY"}},
|
||||
{"command": "sketch.circle.center", "params": {"center": [0, 0], "radius": 5}},
|
||||
{"command": "sketch.circle.center", "params": {"center": [20, 0], "radius": 5}},
|
||||
{"command": "sketch.finish", "params": {}},
|
||||
]));
|
||||
let sk = app.session.doc.features.first().unwrap().id;
|
||||
let d = start(&mut app, vec![Sel::Profile { sketch: sk, index: 0 }, Sel::Profile { sketch: sk, index: 1 }], "Extrude");
|
||||
let d = start(&mut app, vec![Sel::Profile { sketch: sk, index: 0 }, Sel::Profile { sketch: sk, index: 1 }], "solid.extrude");
|
||||
assert_eq!(d.inputs[0].items.len(), 2);
|
||||
assert!(d.focus, "the value box takes the keyboard");
|
||||
assert!(apply_commands(&app, &d).is_ok());
|
||||
@@ -58,7 +58,7 @@ fn profiles_then_extrude_takes_them_all() {
|
||||
fn edges_then_fillet_takes_them_with_chains() {
|
||||
let mut app = boxed();
|
||||
let (a, b) = (edge(&app, Vec3::new(0.0, 0.0, 10.0)), edge(&app, Vec3::new(20.0, 0.0, 20.0)));
|
||||
let d = start(&mut app, vec![a, b], "FusionFilletEdgesCommand");
|
||||
let d = start(&mut app, vec![a, b], "solid.fillet");
|
||||
assert_eq!(d.inputs[0].items.len(), 2, "box edges are not tangent to others");
|
||||
let cmds = apply_commands(&app, &d).unwrap();
|
||||
assert_eq!(cmds[0].1["edges"].as_array().unwrap().len(), 2);
|
||||
@@ -68,7 +68,7 @@ fn edges_then_fillet_takes_them_with_chains() {
|
||||
fn face_then_extrude_press_pulls_it() {
|
||||
let mut app = boxed();
|
||||
let top = face(&app, Vec3::new(20.0, 15.0, 20.0));
|
||||
let d = start(&mut app, vec![top], "Extrude");
|
||||
let d = start(&mut app, vec![top], "solid.extrude");
|
||||
let cmds = apply_commands(&app, &d).unwrap();
|
||||
assert!(cmds[0].1.get("face").is_some());
|
||||
}
|
||||
@@ -78,9 +78,9 @@ fn faces_then_shell_and_face_then_hole() {
|
||||
let mut app = boxed();
|
||||
let top = face(&app, Vec3::new(20.0, 15.0, 20.0));
|
||||
let front = face(&app, Vec3::new(20.0, 0.0, 10.0));
|
||||
let d = start(&mut app, vec![top.clone(), front], "FusionShellBodyCommand");
|
||||
let d = start(&mut app, vec![top.clone(), front], "solid.shell");
|
||||
assert_eq!(d.inputs[0].items.len(), 2);
|
||||
let d = start(&mut app, vec![top], "FusionHoleCommand");
|
||||
let d = start(&mut app, vec![top], "solid.hole");
|
||||
assert_eq!(d.inputs[0].items.len(), 1);
|
||||
let cmds = apply_commands(&app, &d).unwrap();
|
||||
assert!(cmds[0].1.get("position").is_some());
|
||||
@@ -89,12 +89,12 @@ fn faces_then_shell_and_face_then_hole() {
|
||||
#[test]
|
||||
fn line_and_profile_then_revolve_routes_each() {
|
||||
let mut app = app_with(json!([
|
||||
{"command": "SketchCreate", "params": {"plane": "XZ"}},
|
||||
{"command": "ShapeRectangleTwoPoint", "params": {"p0": [10, 0], "p1": [20, 10]}},
|
||||
{"command": "DrawPolyline", "params": {"points": [[0, 0], [0, 30]], "ids": ["axis"]}},
|
||||
{"command": "sketch.create", "params": {"plane": "XZ"}},
|
||||
{"command": "sketch.rectangle.two_point", "params": {"p0": [10, 0], "p1": [20, 10]}},
|
||||
{"command": "sketch.line", "params": {"points": [[0, 0], [0, 30]], "ids": ["axis"]}},
|
||||
]));
|
||||
let sk = app.session.active_sketch.unwrap();
|
||||
let d = start(&mut app, vec![Sel::SketchCurve { id: "axis".into() }, Sel::Profile { sketch: sk, index: 0 }], "Revolve");
|
||||
let d = start(&mut app, vec![Sel::SketchCurve { id: "axis".into() }, Sel::Profile { sketch: sk, index: 0 }], "solid.revolve");
|
||||
assert_eq!(d.inputs[0].items, vec![Sel::Profile { sketch: sk, index: 0 }]);
|
||||
assert_eq!(d.inputs[1].items, vec![Sel::SketchCurve { id: "axis".into() }]);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ fn line_and_profile_then_revolve_routes_each() {
|
||||
fn face_then_move_takes_the_body_and_odd_items_are_ignored() {
|
||||
let mut app = boxed();
|
||||
let top = face(&app, Vec3::new(20.0, 15.0, 20.0));
|
||||
let d = start(&mut app, vec![top, Sel::Axis { name: "X".into() }], "FusionMoveCommand");
|
||||
let d = start(&mut app, vec![top, Sel::Axis { name: "X".into() }], "solid.move");
|
||||
assert_eq!(d.inputs[0].items, vec![Sel::Body { name: "Body1".into() }]);
|
||||
assert!(matches!(d.kind, Kind::Move { .. }));
|
||||
}
|
||||
@@ -111,14 +111,14 @@ fn face_then_move_takes_the_body_and_odd_items_are_ignored() {
|
||||
#[test]
|
||||
fn hole_at_sketch_points() {
|
||||
let mut app = app_with(json!([
|
||||
{"command": "PrimitiveBox", "params": {"length": 40, "width": 30, "height": 20}},
|
||||
{"command": "SketchCreate", "params": {"plane": {"face": [20, 15, 20]}}},
|
||||
{"command": "DrawPoint", "params": {"point": [10, 10], "id": "h1"}},
|
||||
{"command": "DrawPoint", "params": {"point": [30, 20], "id": "h2"}},
|
||||
{"command": "SketchStop", "params": {}},
|
||||
{"command": "solid.box", "params": {"length": 40, "width": 30, "height": 20}},
|
||||
{"command": "sketch.create", "params": {"plane": {"face": [20, 15, 20]}}},
|
||||
{"command": "sketch.point", "params": {"point": [10, 10], "id": "h1"}},
|
||||
{"command": "sketch.point", "params": {"point": [30, 20], "id": "h2"}},
|
||||
{"command": "sketch.finish", "params": {}},
|
||||
]));
|
||||
let sk = app.session.doc.features.iter().find(|f| matches!(f.kind, solvecraft_engine::doc::FeatureKind::Sketch { .. })).unwrap().id;
|
||||
app.start("FusionHoleCommand");
|
||||
app.start("solid.hole");
|
||||
let mut d = app.dialog.clone().unwrap();
|
||||
if let Kind::Hole { opts, .. } = &mut d.kind {
|
||||
opts.multiple = true;
|
||||
@@ -135,12 +135,12 @@ fn hole_at_sketch_points() {
|
||||
#[test]
|
||||
fn a_hole_is_patterned_as_a_feature() {
|
||||
let mut app = app_with(json!([
|
||||
{"command": "PrimitiveBox", "params": {"length": 60, "width": 40, "height": 10}},
|
||||
{"command": "FusionHoleCommand", "params": {"position": [10, 10, 10], "diameter": 5}}
|
||||
{"command": "solid.box", "params": {"length": 60, "width": 40, "height": 10}},
|
||||
{"command": "solid.hole", "params": {"position": [10, 10, 10], "diameter": 5}}
|
||||
]));
|
||||
let hole = app.session.doc.features.last().unwrap().id;
|
||||
// Picked in the timeline before the command: the objects become features.
|
||||
let mut d = start(&mut app, vec![Sel::Feature { id: hole }], "PatternRectangular");
|
||||
let mut d = start(&mut app, vec![Sel::Feature { id: hole }], "solid.pattern.rectangular");
|
||||
assert_eq!(d.inputs[0].accept, crate::selection::FEATURES);
|
||||
assert_eq!(d.inputs[0].items, vec![Sel::Feature { id: hole }]);
|
||||
d.inputs[1].items = vec![Sel::Axis { name: "X".into() }];
|
||||
@@ -164,7 +164,7 @@ fn a_hole_is_patterned_as_a_feature() {
|
||||
let [a, b, c] = m.tri(&m.triangles[t]).unwrap();
|
||||
crate::viewport::Hit::Face { body: "Body1".into(), index: m.tri_face[t] as usize, point: (a + b + c) * (1.0 / 3.0) }
|
||||
};
|
||||
let mut d = start(&mut app, vec![], "PatternCircular");
|
||||
let mut d = start(&mut app, vec![], "solid.pattern.circular");
|
||||
d.inputs[0].accept = crate::selection::FEATURES;
|
||||
let box_id = app.session.doc.features[0].id;
|
||||
assert_eq!(d.candidate(&app.session, &face_at(Vec3::new(12.5, 10.0, 5.0))), Some(Sel::Feature { id: hole }));
|
||||
@@ -180,11 +180,11 @@ fn a_hole_is_patterned_as_a_feature() {
|
||||
#[test]
|
||||
fn a_hole_is_mirrored_as_a_feature() {
|
||||
let mut app = app_with(json!([
|
||||
{"command": "PrimitiveBox", "params": {"length": 60, "width": 40, "height": 10}},
|
||||
{"command": "FusionHoleCommand", "params": {"position": [10, 10, 10], "diameter": 5}}
|
||||
{"command": "solid.box", "params": {"length": 60, "width": 40, "height": 10}},
|
||||
{"command": "solid.hole", "params": {"position": [10, 10, 10], "diameter": 5}}
|
||||
]));
|
||||
let hole = app.session.doc.features.last().unwrap().id;
|
||||
let mut d = start(&mut app, vec![Sel::Feature { id: hole }], "MirrorCommand");
|
||||
let mut d = start(&mut app, vec![Sel::Feature { id: hole }], "solid.mirror");
|
||||
assert_eq!(d.inputs[0].accept, crate::selection::FEATURES);
|
||||
d.inputs[1].items = vec![Sel::Plane { name: "YZ".into() }];
|
||||
let cmds = apply_commands(&app, &d).unwrap();
|
||||
@@ -195,7 +195,7 @@ fn a_hole_is_mirrored_as_a_feature() {
|
||||
fn chamfer_types_send_their_values() {
|
||||
let mut app = boxed();
|
||||
let e = edge(&app, Vec3::new(20.0, 0.0, 20.0));
|
||||
let mut d = start(&mut app, vec![e], "FusionChamferCommand");
|
||||
let mut d = start(&mut app, vec![e], "solid.chamfer");
|
||||
let mut with = |t: usize| {
|
||||
if let Kind::Fillet { ctype, distance2, angle, flip, .. } = &mut d.kind {
|
||||
*ctype = t;
|
||||
|
||||
@@ -367,10 +367,10 @@ mod tests {
|
||||
fn plate() -> Session {
|
||||
let mut s = Session::default();
|
||||
for (id, p) in [
|
||||
("SketchCreate", json!({"plane": "XY"})),
|
||||
("ShapeRectangleTwoPoint", json!({"p0": [0, 0], "p1": [40, 30]})),
|
||||
("SketchStop", json!({})),
|
||||
("Extrude", json!({"distance": 20})),
|
||||
("sketch.create", json!({"plane": "XY"})),
|
||||
("sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [40, 30]})),
|
||||
("sketch.finish", json!({})),
|
||||
("solid.extrude", json!({"distance": 20})),
|
||||
] {
|
||||
s.execute(id, &p).unwrap();
|
||||
}
|
||||
@@ -383,7 +383,7 @@ mod tests {
|
||||
fn fillet_preview_scene() {
|
||||
let s = plate();
|
||||
let (rev, undo) = (s.revision, s.undo.len());
|
||||
let b = compute(&s, &[("FusionFilletEdgesCommand".into(), json!({"edges": [[0, 0, 10]], "radius": 3}))], colors()).unwrap();
|
||||
let b = compute(&s, &[("solid.fillet".into(), json!({"edges": [[0, 0, 10]], "radius": 3}))], colors()).unwrap();
|
||||
assert_eq!(b.replaced, vec!["Body1".to_string()]);
|
||||
let added = count(&b.scene.tris, crate::gpu::TRI_SIZE, colors().added);
|
||||
let kept = count(&b.scene.tris, crate::gpu::TRI_SIZE, colors().body);
|
||||
@@ -397,10 +397,10 @@ mod tests {
|
||||
#[test]
|
||||
fn cut_preview_scene() {
|
||||
let mut s = plate();
|
||||
s.execute("SketchCreate", &json!({"plane": "XY"})).unwrap();
|
||||
s.execute("CircleCenterRadius", &json!({"center": [20, 15], "radius": 5})).unwrap();
|
||||
s.execute("SketchStop", &json!({})).unwrap();
|
||||
let b = compute(&s, &[("Extrude".into(), json!({"distance": 20, "operation": "cut"}))], colors()).unwrap();
|
||||
s.execute("sketch.create", &json!({"plane": "XY"})).unwrap();
|
||||
s.execute("sketch.circle.center", &json!({"center": [20, 15], "radius": 5})).unwrap();
|
||||
s.execute("sketch.finish", &json!({})).unwrap();
|
||||
let b = compute(&s, &[("solid.extrude".into(), json!({"distance": 20, "operation": "cut"}))], colors()).unwrap();
|
||||
assert!(b.replaced.is_empty());
|
||||
assert!(b.scene.tris.is_empty() && !b.scene.xray.is_empty());
|
||||
assert!(b.error.is_none());
|
||||
@@ -410,14 +410,14 @@ mod tests {
|
||||
#[test]
|
||||
fn extrude_preview_scene() {
|
||||
let mut s = Session::default();
|
||||
s.execute("SketchCreate", &json!({"plane": "XY"})).unwrap();
|
||||
s.execute("CircleCenterRadius", &json!({"center": [0, 0], "radius": 5})).unwrap();
|
||||
s.execute("SketchStop", &json!({})).unwrap();
|
||||
let b = compute(&s, &[("Extrude".into(), json!({"distance": "15 mm"}))], colors()).unwrap();
|
||||
s.execute("sketch.create", &json!({"plane": "XY"})).unwrap();
|
||||
s.execute("sketch.circle.center", &json!({"center": [0, 0], "radius": 5})).unwrap();
|
||||
s.execute("sketch.finish", &json!({})).unwrap();
|
||||
let b = compute(&s, &[("solid.extrude".into(), json!({"distance": "15 mm"}))], colors()).unwrap();
|
||||
assert!(b.replaced.is_empty());
|
||||
assert_eq!(count(&b.scene.tris, crate::gpu::TRI_SIZE, colors().body), 0);
|
||||
assert!(b.scene.ghost.is_empty());
|
||||
assert!(compute(&s, &[("Extrude".into(), json!({"distance": "1 +"}))], colors()).is_err());
|
||||
assert!(compute(&s, &[("solid.extrude".into(), json!({"distance": "1 +"}))], colors()).is_err());
|
||||
assert!(s.model.state().bodies.is_empty());
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
//! drags, toolbar starts) the way a person would use it, with checks on the result.
|
||||
//!
|
||||
//! A scenario is a JSON list of steps:
|
||||
//! - `{"start": "Extrude"}`: a toolbar click; `{"key": "E", "shift"?, "cmd"?}`; `{"text": "20"}`
|
||||
//! - `{"start": "solid.extrude"}`: a toolbar click; `{"key": "E", "shift"?, "cmd"?}`; `{"text": "20"}`
|
||||
//! - `{"widget": "Revolute", "near"?: "Type"}`: click a dialog field, choice or button by its
|
||||
//! text (with `near`: the one in that label's row)
|
||||
//! - `{"click": AT, "double"?, "shift"?, "ctrl"?, "button"?}`, `{"move": AT}`,
|
||||
@@ -650,7 +650,7 @@ pub fn check(h: &mut Harness, e: &Value) -> Result<(), String> {
|
||||
// {body, min?: [x|null, y|null, z|null], max?: […]}: where a body is (±1e-3 mm).
|
||||
let name = v["body"].as_str().unwrap_or_default();
|
||||
// Where it is in the assembly (measured as placed).
|
||||
let m = h.call("engine.execute", json!({"command": "MeasureCommand", "params": {"bodies": [name]}}))["result"].clone();
|
||||
let m = h.call("engine.execute", json!({"command": "inspect.measure", "params": {"bodies": [name]}}))["result"].clone();
|
||||
let b = m["bodies"].as_array().and_then(|a| a.first()).cloned().ok_or_else(|| format!("no body {name}: {m}"))?;
|
||||
for side in ["min", "max"] {
|
||||
for (i, want) in v[side].as_array().into_iter().flatten().enumerate() {
|
||||
@@ -682,7 +682,7 @@ pub fn check(h: &mut Harness, e: &Value) -> Result<(), String> {
|
||||
"body_volume" => {
|
||||
// {body, volume}: one body's volume.
|
||||
let name = v["body"].as_str().unwrap_or_default();
|
||||
let m = h.call("engine.execute", json!({"command": "MeasureCommand", "params": {"bodies": [name]}}))["result"].clone();
|
||||
let m = h.call("engine.execute", json!({"command": "inspect.measure", "params": {"bodies": [name]}}))["result"].clone();
|
||||
let got = m["bodies"][0]["volume_mm3"].as_f64().unwrap_or(f64::NAN);
|
||||
approx(got, &v["volume"], &format!("{name} volume"))?;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,10 @@ impl ShortcutBox {
|
||||
}
|
||||
pub fn load(&mut self, v: &Value) {
|
||||
let list = |k: &str| -> Vec<String> {
|
||||
v.get(k).and_then(Value::as_array).map(|a| a.iter().take(64).filter_map(|x| x.as_str().map(str::to_string)).collect()).unwrap_or_default()
|
||||
v.get(k)
|
||||
.and_then(Value::as_array)
|
||||
.map(|a| a.iter().take(64).filter_map(|x| x.as_str().map(|id| solvecraft_engine::legacy_ids::current_id(id).to_string())).collect())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
self.recent = list("recent");
|
||||
self.pinned = list("pinned");
|
||||
@@ -272,16 +275,16 @@ mod tests {
|
||||
#[test]
|
||||
fn search_finds_by_name_id_and_panel_and_puts_recent_first() {
|
||||
let mut app = SolveApp::new(solvecraft_engine::Session::default(), Default::default());
|
||||
assert_eq!(search(&app, "extr").first().map(|x| x.0), Some("Extrude"));
|
||||
assert_eq!(search(&app, "FusionFilletEdges").first().map(|x| x.0), Some("FusionFilletEdgesCommand"));
|
||||
assert!(search(&app, "constraints").iter().any(|x| x.0 == "ConstraintParallel"), "by panel");
|
||||
app.sbox.used("FusionShellBodyCommand");
|
||||
app.sbox.used("Revolve");
|
||||
assert_eq!(search(&app, "extr").first().map(|x| x.0), Some("solid.extrude"));
|
||||
assert_eq!(search(&app, "solid.fil").first().map(|x| x.0), Some("solid.fillet"));
|
||||
assert!(search(&app, "constraints").iter().any(|x| x.0 == "sketch.constraint.parallel"), "by panel");
|
||||
app.sbox.used("solid.shell");
|
||||
app.sbox.used("solid.revolve");
|
||||
let all = search(&app, "");
|
||||
assert_eq!((all[0].0, all[1].0), ("Revolve", "FusionShellBodyCommand"));
|
||||
app.sbox.toggle_pin("Extrude");
|
||||
assert_eq!((all[0].0, all[1].0), ("solid.revolve", "solid.shell"));
|
||||
app.sbox.toggle_pin("solid.extrude");
|
||||
let mut other = SolveApp::new(solvecraft_engine::Session::default(), Default::default());
|
||||
other.sbox.load(&app.sbox.prefs());
|
||||
assert_eq!((other.sbox.pinned.clone(), other.sbox.recent.len()), (vec!["Extrude".to_string()], 2));
|
||||
assert_eq!((other.sbox.pinned.clone(), other.sbox.recent.len()), (vec!["solid.extrude".to_string()], 2));
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
//! and to points already drawn in 3D; segments that stay on the plane are ordinary sketch lines.
|
||||
//! A drawn 3D point (or curve end) clicked without a tool gets a move triad: drag an arrow to
|
||||
//! move along X, Y or Z, or the square to move parallel to the sketch plane. Everything runs
|
||||
//! `sketch.line3d`, `sketch.spline3d`, `sketch.point3d`, `sketch.move3d` and `DrawPolyline`.
|
||||
//! `sketch.line3d`, `sketch.spline3d`, `sketch.point3d`, `sketch.move3d` and `sketch.line`.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn active(app: &SolveApp) -> bool {
|
||||
}
|
||||
|
||||
fn takes(cmd: &str) -> bool {
|
||||
matches!(cmd, "DrawPolyline" | "DrawSpline" | "DrawPoint")
|
||||
matches!(cmd, "sketch.line" | "sketch.spline.fit_point" | "sketch.point")
|
||||
}
|
||||
|
||||
/// Points drawn in 3D: (wire id, fit index, point).
|
||||
@@ -122,17 +122,17 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) ->
|
||||
set_chain(Some(tool.cmd), Vec::new());
|
||||
}
|
||||
match tool.cmd {
|
||||
"DrawPoint" => {
|
||||
"sketch.point" => {
|
||||
let r = match on_plane(app, p) {
|
||||
Some(q) => app.run("DrawPoint", json!({"point": [q.x, q.y]})),
|
||||
Some(q) => app.run("sketch.point", json!({"point": [q.x, q.y]})),
|
||||
None => app.run("sketch.point3d", json!({"point": [p.x, p.y, p.z]})),
|
||||
};
|
||||
report(app, r);
|
||||
}
|
||||
"DrawPolyline" => {
|
||||
"sketch.line" => {
|
||||
if let Some(a) = chain().last().copied() {
|
||||
let r = match (on_plane(app, a), on_plane(app, p)) {
|
||||
(Some(qa), Some(qb)) => app.run("DrawPolyline", json!({"points": [[qa.x, qa.y], [qb.x, qb.y]], "infer": true})),
|
||||
(Some(qa), Some(qb)) => app.run("sketch.line", json!({"points": [[qa.x, qa.y], [qb.x, qb.y]], "infer": true})),
|
||||
_ => app.run("sketch.line3d", json!({"points": [[a.x, a.y, a.z], [p.x, p.y, p.z]]})),
|
||||
};
|
||||
report(app, r);
|
||||
@@ -168,10 +168,10 @@ pub fn finish(app: &mut SolveApp) -> bool {
|
||||
set_chain(None, Vec::new());
|
||||
return false;
|
||||
}
|
||||
if cmd == "DrawSpline" && pts.len() >= 2 {
|
||||
if cmd == "sketch.spline.fit_point" && pts.len() >= 2 {
|
||||
let r = if pts.iter().all(|p| on_plane(app, *p).is_some()) {
|
||||
let q: Vec<Value> = pts.iter().filter_map(|p| on_plane(app, *p)).map(|q| json!([q.x, q.y])).collect();
|
||||
app.run("DrawSpline", json!({ "points": q }))
|
||||
app.run("sketch.spline.fit_point", json!({ "points": q }))
|
||||
} else {
|
||||
let q: Vec<Value> = pts.iter().map(|p| json!([p.x, p.y, p.z])).collect();
|
||||
app.run("sketch.spline3d", json!({ "points": q }))
|
||||
@@ -195,7 +195,7 @@ pub fn preview(app: &SolveApp, t: &Tool, painter: &egui::Painter, proj: &Proj) -
|
||||
if let Some((h, label)) = hover
|
||||
&& let Some(s) = proj.to_screen(h)
|
||||
{
|
||||
if t.cmd != "DrawPoint" && !pts.is_empty() {
|
||||
if t.cmd != "sketch.point" && !pts.is_empty() {
|
||||
all.push(s);
|
||||
}
|
||||
painter.circle_stroke(s, if label == "Plane" { 3.0 } else { 6.0 }, stroke);
|
||||
@@ -210,7 +210,7 @@ pub fn preview(app: &SolveApp, t: &Tool, painter: &egui::Painter, proj: &Proj) -
|
||||
tk.text_dim,
|
||||
);
|
||||
}
|
||||
if t.cmd == "DrawSpline" && all.len() >= 2 {
|
||||
if t.cmd == "sketch.spline.fit_point" && all.len() >= 2 {
|
||||
let mut w: Vec<Vec3> = pts.clone();
|
||||
if let Some((h, _)) = hover {
|
||||
w.push(h);
|
||||
|
||||
@@ -36,14 +36,14 @@ pub fn boxes_for(cmd: &str, n: usize) -> Vec<DimBox> {
|
||||
use ValueKind::*;
|
||||
let b = DimBox::new;
|
||||
match (cmd, n) {
|
||||
("DrawPolyline", n) if n >= 1 => vec![b("Length", Length), b("Angle", Angle)],
|
||||
("ShapeRectangleTwoPoint" | "ShapeRectangleCenter", 1) => vec![b("Width", Length), b("Height", Length)],
|
||||
("CircleCenterRadius", 1) => vec![b("Diameter", Length)],
|
||||
("ArcCenterTwoPoint", 1) => vec![b("Radius", Length)],
|
||||
("ArcCenterTwoPoint", 2) => vec![b("Sweep", Angle)],
|
||||
("ShapePolygonInscribed" | "ShapePolygonCircumscribed", 1) => vec![b("Radius", Length), b("Sides", Unitless)],
|
||||
("ShapeSlotCenterToCenter" | "ShapeSlotOverall", 1) => vec![b("Length", Length)],
|
||||
("ShapeSlotCenterToCenter" | "ShapeSlotOverall", 2) => vec![b("Width", Length)],
|
||||
("sketch.line", n) if n >= 1 => vec![b("Length", Length), b("Angle", Angle)],
|
||||
("sketch.rectangle.two_point" | "sketch.rectangle.center", 1) => vec![b("Width", Length), b("Height", Length)],
|
||||
("sketch.circle.center", 1) => vec![b("Diameter", Length)],
|
||||
("sketch.arc.center_point", 1) => vec![b("Radius", Length)],
|
||||
("sketch.arc.center_point", 2) => vec![b("Sweep", Angle)],
|
||||
("sketch.polygon.inscribed" | "sketch.polygon.circumscribed", 1) => vec![b("Radius", Length), b("Sides", Unitless)],
|
||||
("sketch.slot.center_to_center" | "sketch.slot.overall", 1) => vec![b("Length", Length)],
|
||||
("sketch.slot.center_to_center" | "sketch.slot.overall", 2) => vec![b("Width", Length)],
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -67,31 +67,31 @@ pub fn effective(app: &SolveApp, t: &Tool, h: Vec2) -> Vec2 {
|
||||
let Some(a) = t.pts.last().map(|p| p.0) else { return h };
|
||||
let lk = |l: &str| locked(app, t, l);
|
||||
match (t.cmd, t.pts.len()) {
|
||||
("DrawPolyline", _) | ("ShapeSlotCenterToCenter" | "ShapeSlotOverall", 1) => {
|
||||
("sketch.line", _) | ("sketch.slot.center_to_center" | "sketch.slot.overall", 1) => {
|
||||
let v = h - a;
|
||||
let len = lk("Length").unwrap_or(v.len());
|
||||
let ang = lk("Angle").unwrap_or_else(|| snap_axis(v.angle()));
|
||||
a + Vec2::from_angle(ang) * len
|
||||
}
|
||||
("ShapeRectangleTwoPoint", 1) => {
|
||||
("sketch.rectangle.two_point", 1) => {
|
||||
let d = h - a;
|
||||
Vec2::new(a.x + lk("Width").map_or(d.x, |w| w * sign(d.x)), a.y + lk("Height").map_or(d.y, |hh| hh * sign(d.y)))
|
||||
}
|
||||
("ShapeRectangleCenter", 1) => {
|
||||
("sketch.rectangle.center", 1) => {
|
||||
let d = h - a;
|
||||
Vec2::new(a.x + lk("Width").map_or(d.x, |w| w * 0.5 * sign(d.x)), a.y + lk("Height").map_or(d.y, |hh| hh * 0.5 * sign(d.y)))
|
||||
}
|
||||
("CircleCenterRadius", 1) => a + dir_or_x(h - a) * lk("Diameter").map_or((h - a).len(), |d| d * 0.5),
|
||||
("ArcCenterTwoPoint", 1) => a + dir_or_x(h - a) * lk("Radius").unwrap_or((h - a).len()),
|
||||
("ArcCenterTwoPoint", 2) => {
|
||||
("sketch.circle.center", 1) => a + dir_or_x(h - a) * lk("Diameter").map_or((h - a).len(), |d| d * 0.5),
|
||||
("sketch.arc.center_point", 1) => a + dir_or_x(h - a) * lk("Radius").unwrap_or((h - a).len()),
|
||||
("sketch.arc.center_point", 2) => {
|
||||
let c = t.pts.first().map(|p| p.0).unwrap_or(a);
|
||||
let r = (a - c).len();
|
||||
let a0 = (a - c).angle();
|
||||
let sweep = lk("Sweep").unwrap_or_else(|| ((h - c).angle() - a0).rem_euclid(std::f64::consts::TAU));
|
||||
c + Vec2::from_angle(a0 + sweep) * r
|
||||
}
|
||||
("ShapePolygonInscribed" | "ShapePolygonCircumscribed", 1) => a + dir_or_x(h - a) * lk("Radius").unwrap_or((h - a).len()),
|
||||
("ShapeSlotCenterToCenter" | "ShapeSlotOverall", 2) => match lk("Width") {
|
||||
("sketch.polygon.inscribed" | "sketch.polygon.circumscribed", 1) => a + dir_or_x(h - a) * lk("Radius").unwrap_or((h - a).len()),
|
||||
("sketch.slot.center_to_center" | "sketch.slot.overall", 2) => match lk("Width") {
|
||||
Some(w) => {
|
||||
let c0 = t.pts.first().map(|p| p.0).unwrap_or(a);
|
||||
let dir = dir_or_x(a - c0);
|
||||
@@ -136,33 +136,33 @@ fn live(t: &Tool, e: Vec2, px: f64) -> Vec<(f64, Vec2)> {
|
||||
let first = t.pts.first().map(|p| p.0).unwrap_or(a);
|
||||
let off = 20.0 * px;
|
||||
match (t.cmd, t.pts.len()) {
|
||||
("DrawPolyline", _) | ("ShapeSlotCenterToCenter" | "ShapeSlotOverall", 1) => {
|
||||
("sketch.line", _) | ("sketch.slot.center_to_center" | "sketch.slot.overall", 1) => {
|
||||
let v = e - a;
|
||||
let d = dir_or_x(v);
|
||||
let n = d.perp();
|
||||
vec![(v.len(), (a + e) * 0.5 + n * off), (v.angle(), a + d * (v.len() * 0.3).min(60.0 * px) - n * off)]
|
||||
}
|
||||
("ShapeRectangleTwoPoint", 1) => {
|
||||
("sketch.rectangle.two_point", 1) => {
|
||||
let (sx, sy) = (sign(e.x - a.x), sign(e.y - a.y));
|
||||
vec![
|
||||
((e.x - a.x).abs(), Vec2::new((a.x + e.x) * 0.5, a.y - sy * off)),
|
||||
((e.y - a.y).abs(), Vec2::new(e.x + sx * 2.0 * off, (a.y + e.y) * 0.5)),
|
||||
]
|
||||
}
|
||||
("ShapeRectangleCenter", 1) => {
|
||||
("sketch.rectangle.center", 1) => {
|
||||
let d = e - a;
|
||||
let (sx, sy) = (sign(d.x), sign(d.y));
|
||||
vec![(2.0 * d.x.abs(), Vec2::new(a.x, a.y - d.y - sy * off)), (2.0 * d.y.abs(), Vec2::new(a.x + d.x + sx * 2.0 * off, a.y))]
|
||||
}
|
||||
("CircleCenterRadius", 1) => vec![(2.0 * (e - a).len(), (a + e) * 0.5 + dir_or_x(e - a).perp() * off)],
|
||||
("ArcCenterTwoPoint", 1) | ("ShapePolygonInscribed" | "ShapePolygonCircumscribed", 1) => {
|
||||
("sketch.circle.center", 1) => vec![(2.0 * (e - a).len(), (a + e) * 0.5 + dir_or_x(e - a).perp() * off)],
|
||||
("sketch.arc.center_point", 1) | ("sketch.polygon.inscribed" | "sketch.polygon.circumscribed", 1) => {
|
||||
vec![((e - a).len(), (a + e) * 0.5), (6.0, a + (e - a) * 0.5 + (e - a).perp() * 0.3)]
|
||||
}
|
||||
("ArcCenterTwoPoint", 2) => {
|
||||
("sketch.arc.center_point", 2) => {
|
||||
let a0 = (a - first).angle();
|
||||
vec![(((e - first).angle() - a0).rem_euclid(std::f64::consts::TAU), e)]
|
||||
}
|
||||
("ShapeSlotCenterToCenter" | "ShapeSlotOverall", 2) => {
|
||||
("sketch.slot.center_to_center" | "sketch.slot.overall", 2) => {
|
||||
let dir = dir_or_x(a - first);
|
||||
vec![(2.0 * dir.cross(e - first).abs(), e)]
|
||||
}
|
||||
@@ -193,10 +193,10 @@ pub fn constraints(t: &Tool, curves: &[String]) -> Vec<(Vec<String>, &'static st
|
||||
let mut out = Vec::new();
|
||||
for b in t.dims.iter().filter(|b| b.locked) {
|
||||
let target = match (t.cmd, b.label) {
|
||||
("DrawPolyline", "Length") | ("ShapeRectangleTwoPoint" | "ShapeRectangleCenter", "Width") => curves.first().map(|c| (c, "length")),
|
||||
("ShapeRectangleTwoPoint" | "ShapeRectangleCenter", "Height") => curves.get(1).map(|c| (c, "length")),
|
||||
("CircleCenterRadius", "Diameter") => curves.first().map(|c| (c, "diameter")),
|
||||
("ArcCenterTwoPoint", "Radius") => curves.first().map(|c| (c, "radius")),
|
||||
("sketch.line", "Length") | ("sketch.rectangle.two_point" | "sketch.rectangle.center", "Width") => curves.first().map(|c| (c, "length")),
|
||||
("sketch.rectangle.two_point" | "sketch.rectangle.center", "Height") => curves.get(1).map(|c| (c, "length")),
|
||||
("sketch.circle.center", "Diameter") => curves.first().map(|c| (c, "diameter")),
|
||||
("sketch.arc.center_point", "Radius") => curves.first().map(|c| (c, "radius")),
|
||||
_ => None,
|
||||
};
|
||||
if let Some((c, ty)) = target {
|
||||
|
||||
@@ -60,7 +60,7 @@ fn toolbar_finish_and_other_feature_edit_leave_the_sketch() {
|
||||
let marker = app.session.doc.marker;
|
||||
let id = first_sketch(&app);
|
||||
app.edit_sketch(id);
|
||||
app.start("SketchStop");
|
||||
app.start("sketch.finish");
|
||||
assert_eq!(app.session.active_sketch, None);
|
||||
assert_eq!(app.session.doc.marker, marker);
|
||||
// Double-clicking a solid feature while sketching finishes the sketch first.
|
||||
|
||||
+124
-129
@@ -39,16 +39,16 @@ thread_local! {
|
||||
/// Commands registered on one panel that Fusion also lists on another: the analyses (sketch
|
||||
/// INSPECT) on SOLID INSPECT, and Interference (SOLID INSPECT) on sketch INSPECT.
|
||||
const ALSO_IN: &[(&str, &str, &str)] = &[
|
||||
("SOLID", "INSPECT", "FusionZebraAnalysisCommand"),
|
||||
("SOLID", "INSPECT", "FusionDraftAnalysisCommand"),
|
||||
("SOLID", "INSPECT", "FusionCurvatureMapAnalysisCommand"),
|
||||
("SOLID", "INSPECT", "FusionEnvironmentMapAnalysisCommand"),
|
||||
("SOLID", "INSPECT", "FusionAccessibilityAnalysisCommand"),
|
||||
("SOLID", "INSPECT", "FusionMinimumRadiusAnalysisCommand"),
|
||||
("SOLID", "INSPECT", "FusionIsoCurveAnalysisCommand"),
|
||||
("SOLID", "INSPECT", "FusionCurvatureCombAnalysisCommand"),
|
||||
("SOLID", "INSPECT", "FusionCenterOfMassCommand"),
|
||||
("SKETCH", "INSPECT", "InterferenceCheckCommand"),
|
||||
("SOLID", "INSPECT", "inspect.zebra"),
|
||||
("SOLID", "INSPECT", "inspect.draft"),
|
||||
("SOLID", "INSPECT", "inspect.curvature_map"),
|
||||
("SOLID", "INSPECT", "inspect.environment_map"),
|
||||
("SOLID", "INSPECT", "inspect.accessibility"),
|
||||
("SOLID", "INSPECT", "inspect.minimum_radius"),
|
||||
("SOLID", "INSPECT", "inspect.isocurve"),
|
||||
("SOLID", "INSPECT", "inspect.curvature_comb"),
|
||||
("SOLID", "INSPECT", "inspect.center_of_mass"),
|
||||
("SKETCH", "INSPECT", "inspect.interference"),
|
||||
];
|
||||
|
||||
/// A panel's commands plus the ones it shares with another panel (see [`ALSO_IN`]).
|
||||
@@ -74,19 +74,19 @@ pub fn also_in<'a>(
|
||||
fn analysis(app: &mut SolveApp, cmd: &str, p: Value) {
|
||||
let Ok(v) = app.run(cmd, p) else { return };
|
||||
let mark = match cmd {
|
||||
"FusionCenterOfMassCommand" => v3(&v["center"]).map(|c| (c, "Center of mass".to_string())),
|
||||
"inspect.center_of_mass" => v3(&v["center"]).map(|c| (c, "Center of mass".to_string())),
|
||||
_ => v3(&v["at"]).zip(v["min_radius"].as_f64()).map(|(c, r)| (c, format!("R min {r:.3} mm"))),
|
||||
};
|
||||
if let Some(m) = mark {
|
||||
MARKS.with(|k| k.borrow_mut().push(m));
|
||||
} else if cmd == "FusionMinimumRadiusAnalysisCommand" {
|
||||
} else if cmd == "inspect.minimum_radius" {
|
||||
app.set_status("Minimum radius: it is straight (no curvature)", false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Isocurve analysis lines.
|
||||
fn iso_lines(app: &SolveApp, painter: &egui::Painter, proj: &Proj) {
|
||||
if app.tool.as_ref().map(|t| t.cmd) != Some("FusionIsoCurveAnalysisCommand") {
|
||||
if app.tool.as_ref().map(|t| t.cmd) != Some("inspect.isocurve") {
|
||||
ISO.with(|i| i.borrow_mut().clear());
|
||||
return;
|
||||
}
|
||||
@@ -104,7 +104,7 @@ fn iso_lines(app: &SolveApp, painter: &egui::Painter, proj: &Proj) {
|
||||
|
||||
/// Analysis markers: a target with its label.
|
||||
fn marks(app: &SolveApp, painter: &egui::Painter, proj: &Proj) {
|
||||
if !app.tool.as_ref().is_some_and(|t| matches!(t.cmd, "FusionMinimumRadiusAnalysisCommand" | "FusionCenterOfMassCommand")) {
|
||||
if !app.tool.as_ref().is_some_and(|t| matches!(t.cmd, "inspect.minimum_radius" | "inspect.center_of_mass")) {
|
||||
MARKS.with(|m| m.borrow_mut().clear());
|
||||
return;
|
||||
}
|
||||
@@ -128,19 +128,12 @@ pub fn start_hook(app: &mut SolveApp, id: &str) -> bool {
|
||||
// Surface analyses toggle: a second click turns the same one off.
|
||||
use solvecraft_engine::SurfaceAnalysis as A;
|
||||
let same = match (id, app.session.analysis) {
|
||||
("FusionZebraAnalysisCommand", Some(A::Zebra { .. })) => Some(true),
|
||||
("FusionDraftAnalysisCommand", Some(A::Draft { .. })) => Some(true),
|
||||
("FusionCurvatureMapAnalysisCommand", Some(A::Curvature { .. })) => Some(true),
|
||||
("FusionEnvironmentMapAnalysisCommand", Some(A::Environment)) => Some(true),
|
||||
("FusionAccessibilityAnalysisCommand", Some(A::Access { .. })) => Some(true),
|
||||
(
|
||||
"FusionZebraAnalysisCommand"
|
||||
| "FusionDraftAnalysisCommand"
|
||||
| "FusionCurvatureMapAnalysisCommand"
|
||||
| "FusionEnvironmentMapAnalysisCommand"
|
||||
| "FusionAccessibilityAnalysisCommand",
|
||||
_,
|
||||
) => Some(false),
|
||||
("inspect.zebra", Some(A::Zebra { .. })) => Some(true),
|
||||
("inspect.draft", Some(A::Draft { .. })) => Some(true),
|
||||
("inspect.curvature_map", Some(A::Curvature { .. })) => Some(true),
|
||||
("inspect.environment_map", Some(A::Environment)) => Some(true),
|
||||
("inspect.accessibility", Some(A::Access { .. })) => Some(true),
|
||||
("inspect.zebra" | "inspect.draft" | "inspect.curvature_map" | "inspect.environment_map" | "inspect.accessibility", _) => Some(false),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(on) = same {
|
||||
@@ -150,7 +143,7 @@ pub fn start_hook(app: &mut SolveApp, id: &str) -> bool {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if id == "FusionCenterOfMassCommand" {
|
||||
if id == "inspect.center_of_mass" {
|
||||
// The whole model at once; the tool stays for picking single bodies.
|
||||
MARKS.with(|m| m.borrow_mut().clear());
|
||||
DEFERRED.with(|d| d.set(true));
|
||||
@@ -163,7 +156,7 @@ pub fn start_hook(app: &mut SolveApp, id: &str) -> bool {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if !matches!(id, "FusionAddCanvasCommand" | "FusionAddEditDecalCommand") {
|
||||
if !matches!(id, "canvas.insert" | "canvas.decal") {
|
||||
return true;
|
||||
}
|
||||
let path = app.services.pick_open.as_ref().and_then(|f| f());
|
||||
@@ -178,7 +171,7 @@ fn v3(v: &Value) -> Option<Vec3> {
|
||||
|
||||
/// Curvature combs: teeth and the envelope through their tips.
|
||||
fn combs(app: &SolveApp, painter: &egui::Painter, proj: &Proj) {
|
||||
if app.tool.as_ref().map(|t| t.cmd) != Some("FusionCurvatureCombAnalysisCommand") {
|
||||
if app.tool.as_ref().map(|t| t.cmd) != Some("inspect.curvature_comb") {
|
||||
COMBS.with(|c| c.borrow_mut().clear());
|
||||
return;
|
||||
}
|
||||
@@ -243,7 +236,7 @@ pub fn refine_snap(
|
||||
None => PREV.with(Cell::get),
|
||||
};
|
||||
// Line tool: the tangent point of a circle or arc (before plain "on the curve").
|
||||
let line_tool = app.tool.as_ref().is_none_or(|t| t.cmd == "DrawPolyline");
|
||||
let line_tool = app.tool.as_ref().is_none_or(|t| t.cmd == "sketch.line");
|
||||
if best.is_none()
|
||||
&& line_tool
|
||||
&& let Some(prev) = prev
|
||||
@@ -346,25 +339,25 @@ enum Mode {
|
||||
|
||||
fn mode(id: &str) -> Option<Mode> {
|
||||
Some(match id {
|
||||
"ProjectNewCmd" | "IntersectCmd" | "Include3DGeometry" | "FitCurvesToSectionCommand" | "SketchIsoparametricCurve" => Mode::ModelRef,
|
||||
"FusionCurvatureCombAnalysisCommand" | "FusionAddCanvasCommand" | "FusionAddEditDecalCommand" => Mode::ModelRef,
|
||||
"FusionMinimumRadiusAnalysisCommand" | "FusionCenterOfMassCommand" | "FusionIsoCurveAnalysisCommand" => Mode::ModelRef,
|
||||
"TrimSketchCmd" | "ExtendSketchCmd" | "BreakSketchCmd" => Mode::CurveAt,
|
||||
"SketchMidpointLine" => Mode::Points(2),
|
||||
"ArcTangent" => Mode::Points(2),
|
||||
"CircleElipse" | "ConicCurveCmd" | "ShapeSlotCenterPoint" => Mode::Points(3),
|
||||
"ShapeArcSlotThreePoint" | "ShapeArcSlotCenterTwoPoint" => Mode::Points(4),
|
||||
"DrawSpline" | "DrawCVMSpline3D" | "DrawCVMSpline5D" => Mode::Points(0),
|
||||
"MTextCmd" => Mode::Points(1),
|
||||
"BlendG1CurveSketchCmd" => Mode::Points(2),
|
||||
"Offset" => Mode::Entities(1),
|
||||
"FilletSketchCmd" | "ChamferSketchEqualDistance" | "ChamferSketchDistanceAngle" | "ChamferSketchDistanceDistance" => Mode::Entities(1),
|
||||
"ConstraintSmooth" | "CircleTanTanRadius" => Mode::Entities(2),
|
||||
"CircleThreeTangent" => Mode::Entities(3),
|
||||
"SketchConstrainer" | "SketchPolygonConstraintCmd" => Mode::Entities(0),
|
||||
"sketch.project" | "sketch.intersect" | "sketch.include_3d" | "sketch.fit_curves_to_section" | "sketch.isoparametric_curve" => Mode::ModelRef,
|
||||
"inspect.curvature_comb" | "canvas.insert" | "canvas.decal" => Mode::ModelRef,
|
||||
"inspect.minimum_radius" | "inspect.center_of_mass" | "inspect.isocurve" => Mode::ModelRef,
|
||||
"sketch.trim" | "sketch.extend" | "sketch.break" => Mode::CurveAt,
|
||||
"sketch.line.midpoint" => Mode::Points(2),
|
||||
"sketch.arc.tangent" => Mode::Points(2),
|
||||
"sketch.ellipse" | "sketch.conic" | "sketch.slot.center_point" => Mode::Points(3),
|
||||
"sketch.slot.arc_three_point" | "sketch.slot.arc_center" => Mode::Points(4),
|
||||
"sketch.spline.fit_point" | "sketch.spline.control_point" | "sketch.spline.control_point_5" => Mode::Points(0),
|
||||
"sketch.text" => Mode::Points(1),
|
||||
"sketch.blend_curve" => Mode::Points(2),
|
||||
"sketch.offset" => Mode::Entities(1),
|
||||
"sketch.fillet" | "sketch.chamfer.equal_distance" | "sketch.chamfer.distance_angle" | "sketch.chamfer.two_distance" => Mode::Entities(1),
|
||||
"sketch.constraint.curvature" | "sketch.circle.two_tangent" => Mode::Entities(2),
|
||||
"sketch.circle.three_tangent" => Mode::Entities(3),
|
||||
"sketch.constrainer" | "sketch.constraint.polygon" => Mode::Entities(0),
|
||||
"sketch.centerline" => Mode::Entities(1),
|
||||
"MirrorSketchCommand" | "CircularSketchPatternCommand" | "RectangularSketchPatternCommand" => Mode::EntitiesThen,
|
||||
"ProjectToSurface" | "IntersectionCurve" | "SpunProfileCmd" => Mode::Steps,
|
||||
"sketch.mirror" | "sketch.pattern.circular" | "sketch.pattern.rectangular" => Mode::EntitiesThen,
|
||||
"sketch.project_to_surface" | "sketch.intersection_curve" | "sketch.spun_profile" => Mode::Steps,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -388,46 +381,48 @@ pub fn tool_for(id: &str) -> Option<Tool> {
|
||||
pub fn hint(id: &str) -> Option<String> {
|
||||
Some(
|
||||
match id {
|
||||
"ProjectNewCmd" => "Project: click edges, faces, vertices, other sketches' curves, axes or planes",
|
||||
"IntersectCmd" => "Intersect: click faces or edges to cut with the sketch plane",
|
||||
"Include3DGeometry" => "Include 3D Geometry: click edges or vertices",
|
||||
"FitCurvesToSectionCommand" => "Fit Curves to Mesh Section: click a body",
|
||||
"SketchIsoparametricCurve" => "Isoparametric Curve: click a point on a face (Shift: along)",
|
||||
"FusionCurvatureCombAnalysisCommand" => "Curvature comb: click sketch curves or model edges",
|
||||
"FusionMinimumRadiusAnalysisCommand" => "Minimum radius: click sketch curves, edges or faces",
|
||||
"FusionCenterOfMassCommand" => "Center of mass of all bodies; click a body for its own",
|
||||
"FusionIsoCurveAnalysisCommand" => "Isocurves: click faces",
|
||||
"FusionAddCanvasCommand" => "Canvas: click a plane or a planar face where the image's centre goes",
|
||||
"FusionAddEditDecalCommand" => "Decal: click a planar face where the image's centre goes",
|
||||
"TrimSketchCmd" => "Trim: click the piece of a curve to remove",
|
||||
"ExtendSketchCmd" => "Extend: click a curve near the end to extend",
|
||||
"BreakSketchCmd" => "Break: click a curve where it should split",
|
||||
"SketchMidpointLine" => "Midpoint line: click the middle, then an end",
|
||||
"ArcTangent" => "Tangent arc: click the end of a line or arc, then the arc's end",
|
||||
"CircleElipse" => "Ellipse: click the centre, the end of the major axis, then a point on it",
|
||||
"ConicCurveCmd" => "Conic: click the start, the end, then the apex",
|
||||
"ShapeSlotCenterPoint" => "Slot: click the centre, an arc centre, then the width",
|
||||
"ShapeArcSlotThreePoint" => "Arc slot: click start, a point on the arc, the end, then the width",
|
||||
"ShapeArcSlotCenterTwoPoint" => "Arc slot: click the centre, the start, the end, then the width",
|
||||
"DrawSpline" => "Spline: click fit points; right-click to finish",
|
||||
"DrawCVMSpline3D" | "DrawCVMSpline5D" => "Control point spline: click control points; right-click to finish",
|
||||
"MTextCmd" => "Text: click where the text starts",
|
||||
"BlendG1CurveSketchCmd" => "Blend curve: click the ends of two curves",
|
||||
"Offset" => "Offset: click a curve (its chain is offset), then the side and distance",
|
||||
"FilletSketchCmd" => "Fillet: click the corner of two lines",
|
||||
"ChamferSketchEqualDistance" | "ChamferSketchDistanceAngle" | "ChamferSketchDistanceDistance" => "Chamfer: click the corner of two lines",
|
||||
"ConstraintSmooth" => "Curvature: click two curves that share an end",
|
||||
"CircleTanTanRadius" => "2-tangent circle: click two curves (the circle goes near the second click)",
|
||||
"CircleThreeTangent" => "3-tangent circle: click three curves",
|
||||
"SketchConstrainer" => "Constrain: click one or two entities; right-click to apply",
|
||||
"SketchPolygonConstraintCmd" => "Polygon: click the lines of a closed chain; right-click to apply",
|
||||
"sketch.project" => "Project: click edges, faces, vertices, other sketches' curves, axes or planes",
|
||||
"sketch.intersect" => "Intersect: click faces or edges to cut with the sketch plane",
|
||||
"sketch.include_3d" => "Include 3D Geometry: click edges or vertices",
|
||||
"sketch.fit_curves_to_section" => "Fit Curves to Mesh Section: click a body",
|
||||
"sketch.isoparametric_curve" => "Isoparametric Curve: click a point on a face (Shift: along)",
|
||||
"inspect.curvature_comb" => "Curvature comb: click sketch curves or model edges",
|
||||
"inspect.minimum_radius" => "Minimum radius: click sketch curves, edges or faces",
|
||||
"inspect.center_of_mass" => "Center of mass of all bodies; click a body for its own",
|
||||
"inspect.isocurve" => "Isocurves: click faces",
|
||||
"canvas.insert" => "Canvas: click a plane or a planar face where the image's centre goes",
|
||||
"canvas.decal" => "Decal: click a planar face where the image's centre goes",
|
||||
"sketch.trim" => "Trim: click the piece of a curve to remove",
|
||||
"sketch.extend" => "Extend: click a curve near the end to extend",
|
||||
"sketch.break" => "Break: click a curve where it should split",
|
||||
"sketch.line.midpoint" => "Midpoint line: click the middle, then an end",
|
||||
"sketch.arc.tangent" => "Tangent arc: click the end of a line or arc, then the arc's end",
|
||||
"sketch.ellipse" => "Ellipse: click the centre, the end of the major axis, then a point on it",
|
||||
"sketch.conic" => "Conic: click the start, the end, then the apex",
|
||||
"sketch.slot.center_point" => "Slot: click the centre, an arc centre, then the width",
|
||||
"sketch.slot.arc_three_point" => "Arc slot: click start, a point on the arc, the end, then the width",
|
||||
"sketch.slot.arc_center" => "Arc slot: click the centre, the start, the end, then the width",
|
||||
"sketch.spline.fit_point" => "Spline: click fit points; right-click to finish",
|
||||
"sketch.spline.control_point" | "sketch.spline.control_point_5" => "Control point spline: click control points; right-click to finish",
|
||||
"sketch.text" => "Text: click where the text starts",
|
||||
"sketch.blend_curve" => "Blend curve: click the ends of two curves",
|
||||
"sketch.offset" => "Offset: click a curve (its chain is offset), then the side and distance",
|
||||
"sketch.fillet" => "Fillet: click the corner of two lines",
|
||||
"sketch.chamfer.equal_distance" | "sketch.chamfer.distance_angle" | "sketch.chamfer.two_distance" => {
|
||||
"Chamfer: click the corner of two lines"
|
||||
}
|
||||
"sketch.constraint.curvature" => "Curvature: click two curves that share an end",
|
||||
"sketch.circle.two_tangent" => "2-tangent circle: click two curves (the circle goes near the second click)",
|
||||
"sketch.circle.three_tangent" => "3-tangent circle: click three curves",
|
||||
"sketch.constrainer" => "Constrain: click one or two entities; right-click to apply",
|
||||
"sketch.constraint.polygon" => "Polygon: click the lines of a closed chain; right-click to apply",
|
||||
"sketch.centerline" => "Centerline: click lines",
|
||||
"MirrorSketchCommand" => "Mirror: click entities, right-click, then click the mirror line",
|
||||
"CircularSketchPatternCommand" => "Circular pattern: click entities, right-click, then click the centre",
|
||||
"RectangularSketchPatternCommand" => "Rectangular pattern: click entities, right-click, then click two points (direction and spacing)",
|
||||
"ProjectToSurface" => "Project to surface: click curves of other sketches, right-click, then click the face",
|
||||
"IntersectionCurve" => "Intersection curve: click two faces",
|
||||
"SpunProfileCmd" => "Spun profile: click the body, then the axis line",
|
||||
"sketch.mirror" => "Mirror: click entities, right-click, then click the mirror line",
|
||||
"sketch.pattern.circular" => "Circular pattern: click entities, right-click, then click the centre",
|
||||
"sketch.pattern.rectangular" => "Rectangular pattern: click entities, right-click, then click two points (direction and spacing)",
|
||||
"sketch.project_to_surface" => "Project to surface: click curves of other sketches, right-click, then click the face",
|
||||
"sketch.intersection_curve" => "Intersection curve: click two faces",
|
||||
"sketch.spun_profile" => "Spun profile: click the body, then the axis line",
|
||||
_ => return None,
|
||||
}
|
||||
.to_string(),
|
||||
@@ -521,7 +516,7 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
let at = sketch_point_at(app, proj, pos);
|
||||
let cmd = tool.cmd;
|
||||
match m {
|
||||
Mode::ModelRef if cmd == "FusionCurvatureCombAnalysisCommand" => {
|
||||
Mode::ModelRef if cmd == "inspect.curvature_comb" => {
|
||||
let params = hits.iter().find_map(|h| match h {
|
||||
Hit::SketchCurve { sketch, id, .. } => Some(json!({"sketch": sketch, "curves": [id]})),
|
||||
Hit::Edge { mid, .. } => Some(json!({"edges": [[mid.x, mid.y, mid.z]]})),
|
||||
@@ -537,7 +532,7 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::ModelRef if cmd == "FusionIsoCurveAnalysisCommand" => {
|
||||
Mode::ModelRef if cmd == "inspect.isocurve" => {
|
||||
let face = hits.iter().find_map(|h| if let Hit::Face { point, .. } = h { Some(*point) } else { None });
|
||||
if let Some(at) = face
|
||||
&& let Ok(v) = app.run(cmd, json!({"faces": [[at.x, at.y, at.z]]}))
|
||||
@@ -549,21 +544,21 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Mode::ModelRef if matches!(cmd, "FusionMinimumRadiusAnalysisCommand" | "FusionCenterOfMassCommand") => {
|
||||
Mode::ModelRef if matches!(cmd, "inspect.minimum_radius" | "inspect.center_of_mass") => {
|
||||
let params = hits.iter().find_map(|h| match (cmd, h) {
|
||||
("FusionMinimumRadiusAnalysisCommand", Hit::SketchCurve { sketch, id, .. }) => Some(json!({"sketch": sketch, "curves": [id]})),
|
||||
("FusionMinimumRadiusAnalysisCommand", Hit::Edge { mid, .. }) => Some(json!({"edges": [[mid.x, mid.y, mid.z]]})),
|
||||
("FusionMinimumRadiusAnalysisCommand", Hit::Face { point, .. }) => Some(json!({"faces": [[point.x, point.y, point.z]]})),
|
||||
("FusionCenterOfMassCommand", Hit::Face { body, .. }) => Some(json!({"bodies": [body]})),
|
||||
("inspect.minimum_radius", Hit::SketchCurve { sketch, id, .. }) => Some(json!({"sketch": sketch, "curves": [id]})),
|
||||
("inspect.minimum_radius", Hit::Edge { mid, .. }) => Some(json!({"edges": [[mid.x, mid.y, mid.z]]})),
|
||||
("inspect.minimum_radius", Hit::Face { point, .. }) => Some(json!({"faces": [[point.x, point.y, point.z]]})),
|
||||
("inspect.center_of_mass", Hit::Face { body, .. }) => Some(json!({"bodies": [body]})),
|
||||
_ => None,
|
||||
});
|
||||
if let Some(p) = params {
|
||||
analysis(app, cmd, p);
|
||||
}
|
||||
}
|
||||
Mode::ModelRef if matches!(cmd, "FusionAddCanvasCommand" | "FusionAddEditDecalCommand") => {
|
||||
Mode::ModelRef if matches!(cmd, "canvas.insert" | "canvas.decal") => {
|
||||
let Some(path) = IMAGE.with(|i| i.borrow().clone()) else { return };
|
||||
let decal = cmd == "FusionAddEditDecalCommand";
|
||||
let decal = cmd == "canvas.decal";
|
||||
let target = hits.iter().find_map(|h| match h {
|
||||
Hit::Face { point, .. } => Some((json!({"face": [point.x, point.y, point.z]}), *point)),
|
||||
Hit::Plane { name, point } if !decal => Some((json!(name), *point)),
|
||||
@@ -599,12 +594,12 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
let r = hits.iter().find_map(|h| model_ref(app, h));
|
||||
let Some(r) = r else { return };
|
||||
match cmd {
|
||||
"FitCurvesToSectionCommand" => {
|
||||
"sketch.fit_curves_to_section" => {
|
||||
if let Some(b) = r.get("body").and_then(Value::as_str) {
|
||||
let _ = app.run(cmd, json!({"body": b}));
|
||||
}
|
||||
}
|
||||
"SketchIsoparametricCurve" => {
|
||||
"sketch.isoparametric_curve" => {
|
||||
if r.get("face").is_some() {
|
||||
let along = SHIFT.with(Cell::get);
|
||||
let _ = app.run(cmd, json!({"face": r["face"], "body": r["body"], "direction": if along { "v" } else { "u" }}));
|
||||
@@ -629,14 +624,14 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
}
|
||||
}
|
||||
Mode::Entities(n) => {
|
||||
if cmd == "Offset" && !tool.picks.is_empty() {
|
||||
if cmd == "sketch.offset" && !tool.picks.is_empty() {
|
||||
if let Some((p, _)) = at {
|
||||
offset_side(app, tool, p);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let Some((id, is_point)) = sketch_entity(app, &hits) else { return };
|
||||
if cmd == "Offset" {
|
||||
if cmd == "sketch.offset" {
|
||||
// First the curve, then the side point.
|
||||
if tool.picks.is_empty() && !is_point {
|
||||
tool.picks.push(id);
|
||||
@@ -645,7 +640,7 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if cmd.starts_with("Fillet") || cmd.starts_with("Chamfer") {
|
||||
if cmd == "sketch.fillet" || cmd.starts_with("sketch.chamfer.") {
|
||||
if is_point {
|
||||
run_corner(app, cmd, &id);
|
||||
}
|
||||
@@ -672,13 +667,13 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
if tool.picks.last().is_some_and(|x| x == "|") {
|
||||
let ents: Vec<String> = tool.picks.iter().filter(|x| *x != "|").cloned().collect();
|
||||
match cmd {
|
||||
"MirrorSketchCommand" => {
|
||||
"sketch.mirror" => {
|
||||
if let Some((line, false)) = sketch_entity(app, &hits) {
|
||||
let _ = app.run(cmd, json!({"entities": ents, "line": line}));
|
||||
tool.picks.clear();
|
||||
}
|
||||
}
|
||||
"CircularSketchPatternCommand" => {
|
||||
"sketch.pattern.circular" => {
|
||||
if let Some(p) = at {
|
||||
let _ = app.run(cmd, json!({"entities": ents, "center": arg(&p), "count": 6}));
|
||||
tool.picks.clear();
|
||||
@@ -707,7 +702,7 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
}
|
||||
}
|
||||
Mode::Steps => match cmd {
|
||||
"IntersectionCurve" => {
|
||||
"sketch.intersection_curve" => {
|
||||
if let Some(r) = hits.iter().find_map(|h| model_ref(app, h)).filter(|r| r.get("face").is_some()) {
|
||||
tool.picks.push(r.to_string());
|
||||
if tool.picks.len() >= 2 {
|
||||
@@ -718,7 +713,7 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
"SpunProfileCmd" => {
|
||||
"sketch.spun_profile" => {
|
||||
if tool.picks.is_empty() {
|
||||
if let Some(b) =
|
||||
hits.iter().find_map(|h| model_ref(app, h)).and_then(|r| r.get("body").and_then(Value::as_str).map(str::to_string))
|
||||
@@ -731,7 +726,7 @@ pub fn on_click(app: &mut SolveApp, tool: &mut Tool, proj: &Proj, pos: Pos2) {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// ProjectToSurface: curves of other sketches, then (after a right-click) a face.
|
||||
// sketch.project_to_surface: curves of other sketches, then (after a right-click) a face.
|
||||
let staged = tool.picks.last().is_some_and(|x| x == "|");
|
||||
if staged {
|
||||
if let Some(r) = hits.iter().find_map(|h| model_ref(app, h)).filter(|r| r.get("face").is_some()) {
|
||||
@@ -751,26 +746,26 @@ fn run_points(app: &mut SolveApp, t: &Tool) {
|
||||
let p = &t.pts;
|
||||
let width = |a: Vec2, b: Vec2, c: Vec2| -> f64 { 2.0 * (b - a).normalized().map(|d| d.cross(c - a).abs()).unwrap_or(1.0) };
|
||||
let params = match (t.cmd, p.as_slice()) {
|
||||
("SketchMidpointLine", [m, e]) => json!({"mid": arg(m), "end": xy(e.0)}),
|
||||
("ArcTangent", [s, e]) => json!({"start": arg(s), "end": xy(e.0)}),
|
||||
("CircleElipse", [c, m, q]) => json!({"center": xy(c.0), "major": xy(m.0), "minor": xy(q.0)}),
|
||||
("ConicCurveCmd", [a, b, x]) => json!({"start": arg(a), "end": arg(b), "apex": arg(x)}),
|
||||
("ShapeSlotCenterPoint", [c, e, w]) => json!({"center": xy(c.0), "end": xy(e.0), "width": width(c.0, e.0, w.0).max(1e-3)}),
|
||||
("ShapeArcSlotThreePoint", [a, th, b, w]) => {
|
||||
("sketch.line.midpoint", [m, e]) => json!({"mid": arg(m), "end": xy(e.0)}),
|
||||
("sketch.arc.tangent", [s, e]) => json!({"start": arg(s), "end": xy(e.0)}),
|
||||
("sketch.ellipse", [c, m, q]) => json!({"center": xy(c.0), "major": xy(m.0), "minor": xy(q.0)}),
|
||||
("sketch.conic", [a, b, x]) => json!({"start": arg(a), "end": arg(b), "apex": arg(x)}),
|
||||
("sketch.slot.center_point", [c, e, w]) => json!({"center": xy(c.0), "end": xy(e.0), "width": width(c.0, e.0, w.0).max(1e-3)}),
|
||||
("sketch.slot.arc_three_point", [a, th, b, w]) => {
|
||||
json!({"start": xy(a.0), "through": xy(th.0), "end": xy(b.0), "width": (2.0 * w.0.dist(b.0)).max(1e-3)})
|
||||
}
|
||||
("ShapeArcSlotCenterTwoPoint", [c, a, b, w]) => {
|
||||
("sketch.slot.arc_center", [c, a, b, w]) => {
|
||||
let r = c.0.dist(a.0);
|
||||
json!({"center": xy(c.0), "start": xy(a.0), "end": xy(b.0), "width": (2.0 * (w.0.dist(c.0) - r).abs()).max(1e-3)})
|
||||
}
|
||||
("DrawSpline" | "DrawCVMSpline3D" | "DrawCVMSpline5D", pts) if pts.len() >= 2 => {
|
||||
("sketch.spline.fit_point" | "sketch.spline.control_point" | "sketch.spline.control_point_5", pts) if pts.len() >= 2 => {
|
||||
json!({"points": pts.iter().map(arg).collect::<Vec<_>>()})
|
||||
}
|
||||
("BlendG1CurveSketchCmd", [a, b]) => match (&a.1, &b.1) {
|
||||
("sketch.blend_curve", [a, b]) => match (&a.1, &b.1) {
|
||||
(Some(x), Some(y)) => json!({"a": x, "b": y}),
|
||||
_ => return,
|
||||
},
|
||||
("MTextCmd", [at]) => {
|
||||
("sketch.text", [at]) => {
|
||||
let at = at.0;
|
||||
TEXT.with(|t| *t.borrow_mut() = Some((at, String::new())));
|
||||
return;
|
||||
@@ -783,9 +778,9 @@ fn run_points(app: &mut SolveApp, t: &Tool) {
|
||||
fn run_corner(app: &mut SolveApp, cmd: &str, point: &str) {
|
||||
let size = round_nice(corner_len(app, point) * 0.2);
|
||||
let params = match cmd {
|
||||
"FilletSketchCmd" => json!({"point": point, "radius": size}),
|
||||
"ChamferSketchDistanceAngle" => json!({"point": point, "distance": size, "angle": 45}),
|
||||
"ChamferSketchDistanceDistance" => json!({"point": point, "distance": size, "distance2": size}),
|
||||
"sketch.fillet" => json!({"point": point, "radius": size}),
|
||||
"sketch.chamfer.distance_angle" => json!({"point": point, "distance": size, "angle": 45}),
|
||||
"sketch.chamfer.two_distance" => json!({"point": point, "distance": size, "distance2": size}),
|
||||
_ => json!({"point": point, "distance": size}),
|
||||
};
|
||||
if let Ok(v) = app.run(cmd, params)
|
||||
@@ -798,19 +793,19 @@ fn run_corner(app: &mut SolveApp, cmd: &str, point: &str) {
|
||||
fn run_entities(app: &mut SolveApp, t: &Tool) {
|
||||
let k = &t.picks;
|
||||
let params = match t.cmd {
|
||||
"ConstraintSmooth" => json!({"a": k[0], "b": k[1]}),
|
||||
"CircleTanTanRadius" => {
|
||||
"sketch.constraint.curvature" => json!({"a": k[0], "b": k[1]}),
|
||||
"sketch.circle.two_tangent" => {
|
||||
let r = round_nice(curve_len(app, &k[0]).min(curve_len(app, &k[1])) * 0.1);
|
||||
let near = t.pts.last().map(|p| xy(p.0)).unwrap_or(json!([0, 0]));
|
||||
json!({"curves": k, "radius": r, "near": near})
|
||||
}
|
||||
"CircleThreeTangent" => {
|
||||
"sketch.circle.three_tangent" => {
|
||||
// Start inside the three picks.
|
||||
let c = t.pts.iter().fold(Vec2::ZERO, |a, p| a + p.0) / t.pts.len().max(1) as f64;
|
||||
json!({"curves": k, "near": xy(c)})
|
||||
}
|
||||
"SketchConstrainer" => json!({"entities": k}),
|
||||
"SketchPolygonConstraintCmd" => json!({"lines": k}),
|
||||
"sketch.constrainer" => json!({"entities": k}),
|
||||
"sketch.constraint.polygon" => json!({"lines": k}),
|
||||
_ => return,
|
||||
};
|
||||
let _ = app.run(t.cmd, params);
|
||||
@@ -847,7 +842,7 @@ pub fn finish(app: &mut SolveApp, tool: &mut Tool) -> bool {
|
||||
|
||||
/// Offset: the second click sets the side and distance.
|
||||
fn offset_side(app: &mut SolveApp, tool: &mut Tool, p: Vec2) -> bool {
|
||||
if tool.cmd != "Offset" || tool.picks.is_empty() {
|
||||
if tool.cmd != "sketch.offset" || tool.picks.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let st = app.session.world_state();
|
||||
@@ -857,7 +852,7 @@ fn offset_side(app: &mut SolveApp, tool: &mut Tool, p: Vec2) -> bool {
|
||||
.map(|sh| sh.dist(p))
|
||||
.unwrap_or(0.0);
|
||||
if d > 1e-6 {
|
||||
let _ = app.run("Offset", json!({"curves": [tool.picks[0]], "distance": d, "side": xy(p)}));
|
||||
let _ = app.run("sketch.offset", json!({"curves": [tool.picks[0]], "distance": d, "side": xy(p)}));
|
||||
}
|
||||
tool.picks.clear();
|
||||
true
|
||||
@@ -1011,7 +1006,7 @@ pub fn show(app: &mut SolveApp, ui: &egui::Ui, painter: &egui::Painter, proj: &P
|
||||
snap_hint(app, painter, proj);
|
||||
combs(app, painter, proj);
|
||||
if DEFERRED.with(|d| d.replace(false)) {
|
||||
analysis(app, "FusionCenterOfMassCommand", json!({}));
|
||||
analysis(app, "inspect.center_of_mass", json!({}));
|
||||
}
|
||||
marks(app, painter, proj);
|
||||
iso_lines(app, painter, proj);
|
||||
@@ -1042,7 +1037,7 @@ fn text_entry(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
Some(true) => {
|
||||
if !text.trim().is_empty() {
|
||||
let h = round_nice(app.cam.half_height() * 0.1);
|
||||
let _ = app.run("MTextCmd", json!({"text": text, "at": xy(at), "height": h}));
|
||||
let _ = app.run("sketch.text", json!({"text": text, "at": xy(at), "height": h}));
|
||||
}
|
||||
}
|
||||
Some(false) => {}
|
||||
|
||||
@@ -62,7 +62,7 @@ fn delete_feature(app: &mut SolveApp, id: u64) {
|
||||
};
|
||||
let (with, fail) = (list("deleted_with_it"), list("would_fail"));
|
||||
if with.is_empty() && fail.is_empty() {
|
||||
let _ = app.run("FusionDeleteCommand", json!({ "features": [id.to_string()] }));
|
||||
let _ = app.run("timeline.delete", json!({ "features": [id.to_string()] }));
|
||||
} else {
|
||||
app.dialog = Some(crate::dialogs::Dialog::confirm_delete(id, with, fail));
|
||||
}
|
||||
@@ -99,7 +99,7 @@ pub fn timeline(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
Some(k) if k < n => json!({"position": k}),
|
||||
_ => json!({}),
|
||||
};
|
||||
let _ = app.run("timeline.rollTo", pos);
|
||||
let _ = app.run("timeline.roll_to", pos);
|
||||
}
|
||||
x += 24.0;
|
||||
}
|
||||
@@ -223,7 +223,7 @@ pub fn timeline(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
ui.close();
|
||||
}
|
||||
if ui.button("Roll History Marker Here").clicked() {
|
||||
let _ = app.run("timeline.rollTo", json!({ "feature": id }));
|
||||
let _ = app.run("timeline.roll_to", json!({ "feature": id }));
|
||||
ui.close();
|
||||
}
|
||||
if ui.button("Find in Browser").clicked() {
|
||||
@@ -271,7 +271,7 @@ pub fn timeline(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
let slot = slot_at(&xs, end, pp.x);
|
||||
match dg {
|
||||
Drag::Marker => {
|
||||
let _ = app.run("timeline.rollTo", if slot >= n { json!({}) } else { json!({ "position": slot }) });
|
||||
let _ = app.run("timeline.roll_to", if slot >= n { json!({}) } else { json!({ "position": slot }) });
|
||||
}
|
||||
Drag::Feature(id) => {
|
||||
let from = app.session.doc.feature_index(id).unwrap_or(0);
|
||||
|
||||
@@ -73,10 +73,10 @@ pub fn app_bar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
save(app);
|
||||
}
|
||||
if click(ui, "undo", "Undo (Ctrl+Z)") {
|
||||
let _ = app.run("UndoCommand", json!({}));
|
||||
let _ = app.run("edit.undo", json!({}));
|
||||
}
|
||||
if click(ui, "redo", "Redo (Ctrl+Y)") {
|
||||
let _ = app.run("RedoCommand", json!({}));
|
||||
let _ = app.run("edit.redo", json!({}));
|
||||
}
|
||||
if click(ui, "home", "Start page") {
|
||||
let rev = app.session.revision;
|
||||
@@ -151,11 +151,11 @@ fn file_menu(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
close = true;
|
||||
}
|
||||
if item(ui, "Insert STEP…", "") {
|
||||
app.start("FusionImportCommandFromToolbar");
|
||||
app.start("file.insert_step");
|
||||
close = true;
|
||||
}
|
||||
if item(ui, "Insert Mesh…", "") {
|
||||
app.start("ParaMeshInsertAlignCommand");
|
||||
app.start("file.insert_mesh");
|
||||
close = true;
|
||||
}
|
||||
if item(ui, "Save", "Ctrl+S") {
|
||||
@@ -211,7 +211,7 @@ pub fn save(app: &mut SolveApp) {
|
||||
return;
|
||||
}
|
||||
if app.session.path.is_some() {
|
||||
let _ = app.run("SaveDocumentCommand", json!({}));
|
||||
let _ = app.run("file.save", json!({}));
|
||||
} else {
|
||||
save_as(app);
|
||||
}
|
||||
@@ -220,14 +220,14 @@ pub fn save(app: &mut SolveApp) {
|
||||
fn save_as(app: &mut SolveApp) {
|
||||
let name = format!("{}.{}", app.session.doc.name, solvecraft_engine::io::DESIGN_EXT);
|
||||
if let Some(p) = app.services.pick_save.as_ref().and_then(|f| f(&name, &[solvecraft_engine::io::DESIGN_EXT])) {
|
||||
let _ = app.run("SaveDocumentAsCommand", json!({"path": p}));
|
||||
let _ = app.run("file.save_as", json!({"path": p}));
|
||||
}
|
||||
}
|
||||
|
||||
fn export(app: &mut SolveApp, ext: &str) {
|
||||
let name = format!("{}.{ext}", app.session.doc.name);
|
||||
if let Some(p) = app.services.pick_save.as_ref().and_then(|f| f(&name, &[ext]))
|
||||
&& app.run("ExportCommand", json!({"path": p})).is_ok()
|
||||
&& app.run("file.export", json!({"path": p})).is_ok()
|
||||
{
|
||||
app.set_status(format!("Exported {p}"), false);
|
||||
}
|
||||
@@ -305,7 +305,7 @@ pub fn toolbar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
} else {
|
||||
(t.border, t.panel_header, t.border)
|
||||
};
|
||||
let icon = if c.id == "SketchStop" { "finish" } else { c.icon };
|
||||
let icon = if c.id == "sketch.finish" { "finish" } else { c.icon };
|
||||
icons::paint(&painter, br.shrink(6.0), icon, ink, fill, acc);
|
||||
let tip = match &info.disabled_reason {
|
||||
Some(why) => format!("{}\n{why}", c.label),
|
||||
@@ -429,8 +429,8 @@ pub fn shortcuts(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
use egui::Key;
|
||||
if mods.command {
|
||||
match k {
|
||||
Key::Z => drop(app.run("UndoCommand", json!({}))),
|
||||
Key::Y => drop(app.run("RedoCommand", json!({}))),
|
||||
Key::Z => drop(app.run("edit.undo", json!({}))),
|
||||
Key::Y => drop(app.run("edit.redo", json!({}))),
|
||||
Key::S => save(app),
|
||||
Key::N => crate::documents::new_design(app),
|
||||
Key::O => {
|
||||
|
||||
+65
-65
@@ -38,33 +38,33 @@ pub struct Tool {
|
||||
impl Tool {
|
||||
pub fn for_command(id: &str) -> Option<Tool> {
|
||||
let (cmd, kind): (&'static str, Kind) = match id {
|
||||
"DrawPolyline" => ("DrawPolyline", Kind::Draw(0)),
|
||||
"ShapeRectangleTwoPoint" => ("ShapeRectangleTwoPoint", Kind::Draw(2)),
|
||||
"ShapeRectangleCenter" => ("ShapeRectangleCenter", Kind::Draw(2)),
|
||||
"ShapeRectangleThreePoint" => ("ShapeRectangleThreePoint", Kind::Draw(3)),
|
||||
"CircleCenterRadius" => ("CircleCenterRadius", Kind::Draw(2)),
|
||||
"CircleTwoPoint" => ("CircleTwoPoint", Kind::Draw(2)),
|
||||
"CircleThreePoint" => ("CircleThreePoint", Kind::Draw(3)),
|
||||
"ArcThreePoint" => ("ArcThreePoint", Kind::Draw(3)),
|
||||
"ArcCenterTwoPoint" => ("ArcCenterTwoPoint", Kind::Draw(3)),
|
||||
"ShapePolygonInscribed" => ("ShapePolygonInscribed", Kind::Draw(2)),
|
||||
"ShapePolygonCircumscribed" => ("ShapePolygonCircumscribed", Kind::Draw(2)),
|
||||
"ShapePolygonEdge" => ("ShapePolygonEdge", Kind::Draw(2)),
|
||||
"ShapeSlotCenterToCenter" => ("ShapeSlotCenterToCenter", Kind::Draw(3)),
|
||||
"ShapeSlotOverall" => ("ShapeSlotOverall", Kind::Draw(3)),
|
||||
"DrawPoint" => ("DrawPoint", Kind::Draw(1)),
|
||||
"SketchDimension" => ("SketchDimension", Kind::Dimension),
|
||||
"ConstraintHorizontalVertical" => ("ConstraintHorizontalVertical", Kind::Pick(1)),
|
||||
"ConstraintFix" => ("ConstraintFix", Kind::Pick(1)),
|
||||
"ConstraintCoincident" => ("ConstraintCoincident", Kind::Pick(2)),
|
||||
"ConstraintTangent" => ("ConstraintTangent", Kind::Pick(2)),
|
||||
"ConstraintEqual" => ("ConstraintEqual", Kind::Pick(2)),
|
||||
"ConstraintParallel" => ("ConstraintParallel", Kind::Pick(2)),
|
||||
"ConstraintPerpendicular" => ("ConstraintPerpendicular", Kind::Pick(2)),
|
||||
"ConstraintConcentric" => ("ConstraintConcentric", Kind::Pick(2)),
|
||||
"ConstraintCollinear" => ("ConstraintCollinear", Kind::Pick(2)),
|
||||
"ConstraintMidPoint" => ("ConstraintMidPoint", Kind::Pick(2)),
|
||||
"ConstraintSymmetry" => ("ConstraintSymmetry", Kind::Pick(3)),
|
||||
"sketch.line" => ("sketch.line", Kind::Draw(0)),
|
||||
"sketch.rectangle.two_point" => ("sketch.rectangle.two_point", Kind::Draw(2)),
|
||||
"sketch.rectangle.center" => ("sketch.rectangle.center", Kind::Draw(2)),
|
||||
"sketch.rectangle.three_point" => ("sketch.rectangle.three_point", Kind::Draw(3)),
|
||||
"sketch.circle.center" => ("sketch.circle.center", Kind::Draw(2)),
|
||||
"sketch.circle.two_point" => ("sketch.circle.two_point", Kind::Draw(2)),
|
||||
"sketch.circle.three_point" => ("sketch.circle.three_point", Kind::Draw(3)),
|
||||
"sketch.arc.three_point" => ("sketch.arc.three_point", Kind::Draw(3)),
|
||||
"sketch.arc.center_point" => ("sketch.arc.center_point", Kind::Draw(3)),
|
||||
"sketch.polygon.inscribed" => ("sketch.polygon.inscribed", Kind::Draw(2)),
|
||||
"sketch.polygon.circumscribed" => ("sketch.polygon.circumscribed", Kind::Draw(2)),
|
||||
"sketch.polygon.edge" => ("sketch.polygon.edge", Kind::Draw(2)),
|
||||
"sketch.slot.center_to_center" => ("sketch.slot.center_to_center", Kind::Draw(3)),
|
||||
"sketch.slot.overall" => ("sketch.slot.overall", Kind::Draw(3)),
|
||||
"sketch.point" => ("sketch.point", Kind::Draw(1)),
|
||||
"sketch.dimension" => ("sketch.dimension", Kind::Dimension),
|
||||
"sketch.constraint.horizontal_vertical" => ("sketch.constraint.horizontal_vertical", Kind::Pick(1)),
|
||||
"sketch.constraint.fix" => ("sketch.constraint.fix", Kind::Pick(1)),
|
||||
"sketch.constraint.coincident" => ("sketch.constraint.coincident", Kind::Pick(2)),
|
||||
"sketch.constraint.tangent" => ("sketch.constraint.tangent", Kind::Pick(2)),
|
||||
"sketch.constraint.equal" => ("sketch.constraint.equal", Kind::Pick(2)),
|
||||
"sketch.constraint.parallel" => ("sketch.constraint.parallel", Kind::Pick(2)),
|
||||
"sketch.constraint.perpendicular" => ("sketch.constraint.perpendicular", Kind::Pick(2)),
|
||||
"sketch.constraint.concentric" => ("sketch.constraint.concentric", Kind::Pick(2)),
|
||||
"sketch.constraint.collinear" => ("sketch.constraint.collinear", Kind::Pick(2)),
|
||||
"sketch.constraint.midpoint" => ("sketch.constraint.midpoint", Kind::Pick(2)),
|
||||
"sketch.constraint.symmetry" => ("sketch.constraint.symmetry", Kind::Pick(3)),
|
||||
_ => return crate::sketch_tools::tool_for(id),
|
||||
};
|
||||
Some(Tool { cmd, kind, pts: Vec::new(), hover: None, picks: Vec::new(), dims: Vec::new(), dims_stage: usize::MAX, dims_focus: false })
|
||||
@@ -73,16 +73,16 @@ impl Tool {
|
||||
|
||||
pub fn hint(id: &str) -> String {
|
||||
match id {
|
||||
"DrawPolyline" => "Line: click points; right-click or Esc to finish".into(),
|
||||
"ShapeRectangleTwoPoint" => "Rectangle: click two opposite corners".into(),
|
||||
"ShapeRectangleCenter" => "Center rectangle: click the centre, then a corner".into(),
|
||||
"ShapeRectangleThreePoint" => "3-point rectangle: click two points of one edge, then the width".into(),
|
||||
"CircleCenterRadius" => "Circle: click the centre, then a point on the circle".into(),
|
||||
"ArcThreePoint" => "Arc: click start, end, then a point on the arc".into(),
|
||||
"ArcCenterTwoPoint" => "Arc: click centre, start, then end (counter-clockwise)".into(),
|
||||
"SketchDimension" => "Dimension: pick a line, circle or arc (or two points/lines)".into(),
|
||||
"ShapeSlotCenterToCenter" | "ShapeSlotOverall" => "Slot: click both ends, then the width".into(),
|
||||
_ if id.starts_with("Constraint") => "Constraint: pick the sketch entities".into(),
|
||||
"sketch.line" => "Line: click points; right-click or Esc to finish".into(),
|
||||
"sketch.rectangle.two_point" => "Rectangle: click two opposite corners".into(),
|
||||
"sketch.rectangle.center" => "Center rectangle: click the centre, then a corner".into(),
|
||||
"sketch.rectangle.three_point" => "3-point rectangle: click two points of one edge, then the width".into(),
|
||||
"sketch.circle.center" => "Circle: click the centre, then a point on the circle".into(),
|
||||
"sketch.arc.three_point" => "Arc: click start, end, then a point on the arc".into(),
|
||||
"sketch.arc.center_point" => "Arc: click centre, start, then end (counter-clockwise)".into(),
|
||||
"sketch.dimension" => "Dimension: pick a line, circle or arc (or two points/lines)".into(),
|
||||
"sketch.slot.center_to_center" | "sketch.slot.overall" => "Slot: click both ends, then the width".into(),
|
||||
_ if id.starts_with("sketch.constraint.") => "Constraint: pick the sketch entities".into(),
|
||||
_ => crate::sketch_tools::hint(id).unwrap_or_else(|| "Click in the sketch".into()),
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ pub fn on_click(app: &mut SolveApp, proj: &Proj, pos: Pos2) {
|
||||
Kind::Draw(_) => {
|
||||
if let Some(mut p) = sketch_point_at(app, proj, pos) {
|
||||
// Typed values win over the cursor; lines snap to horizontal/vertical.
|
||||
if tool.dims.iter().any(|b| b.locked) || (tool.cmd == "DrawPolyline" && p.1.is_none()) {
|
||||
if tool.dims.iter().any(|b| b.locked) || (tool.cmd == "sketch.line" && p.1.is_none()) {
|
||||
p = (crate::sketch_dims::effective(app, &tool, p.0), None);
|
||||
}
|
||||
app.tool = Some(tool);
|
||||
@@ -159,7 +159,7 @@ pub fn on_click(app: &mut SolveApp, proj: &Proj, pos: Pos2) {
|
||||
let single_line = tool.picks.len() == 1 && is_line == Some(true);
|
||||
if ready || single_line {
|
||||
// A single line gets its length right away; a second pick (line or point) replaces it.
|
||||
if let Ok(v) = app.run("SketchDimension", json!({"entities": tool.picks}))
|
||||
if let Ok(v) = app.run("sketch.dimension", json!({"entities": tool.picks}))
|
||||
&& let Some(p) = v["param"].as_str()
|
||||
{
|
||||
// The value is edited in place, selected so typing replaces it.
|
||||
@@ -182,10 +182,10 @@ pub fn place(app: &mut SolveApp, p: (Vec2, Option<String>)) {
|
||||
return;
|
||||
};
|
||||
tool.pts.push(p);
|
||||
if tool.cmd == "DrawPolyline" {
|
||||
if tool.cmd == "sketch.line" {
|
||||
if tool.pts.len() >= 2 {
|
||||
let (a, b) = (tool.pts[tool.pts.len() - 2].clone(), tool.pts[tool.pts.len() - 1].clone());
|
||||
if let Ok(v) = app.run("DrawPolyline", json!({"points": [arg(&a), arg(&b)], "infer": true})) {
|
||||
if let Ok(v) = app.run("sketch.line", json!({"points": [arg(&a), arg(&b)], "infer": true})) {
|
||||
let curves: Vec<String> = v["curves"].as_array().into_iter().flatten().filter_map(|c| c.as_str().map(str::to_string)).collect();
|
||||
dimension(app, &tool, &curves);
|
||||
// Chain: the next segment starts at this one's end point.
|
||||
@@ -207,7 +207,7 @@ pub fn place(app: &mut SolveApp, p: (Vec2, Option<String>)) {
|
||||
/// Typed values become dimension constraints on the new curves.
|
||||
fn dimension(app: &mut SolveApp, t: &Tool, curves: &[String]) {
|
||||
for (ents, ty, value) in crate::sketch_dims::constraints(t, curves) {
|
||||
let _ = app.run("SketchDimension", crate::sketch_dims::dimension_params(&ents, ty, &value));
|
||||
let _ = app.run("sketch.dimension", crate::sketch_dims::dimension_params(&ents, ty, &value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,25 +216,25 @@ fn run_shape(app: &mut SolveApp, t: &Tool) -> Vec<String> {
|
||||
let d = |a: usize, b: usize| p[a].0.dist(p[b].0);
|
||||
let sides = crate::sketch_dims::locked_value(app, t, "Sides").map_or(6, |v| v.round().clamp(3.0, 200.0) as usize);
|
||||
let params = match t.cmd {
|
||||
"ShapeRectangleTwoPoint" => json!({"p0": xy(&p[0]), "p1": xy(&p[1])}),
|
||||
"ShapeRectangleCenter" => json!({"center": xy(&p[0]), "corner": xy(&p[1])}),
|
||||
"ShapeRectangleThreePoint" => json!({"p0": xy(&p[0]), "p1": xy(&p[1]), "p2": xy(&p[2])}),
|
||||
"CircleCenterRadius" => json!({"center": arg(&p[0]), "radius": d(0, 1)}),
|
||||
"CircleTwoPoint" => json!({"p0": xy(&p[0]), "p1": xy(&p[1])}),
|
||||
"CircleThreePoint" => json!({"p0": xy(&p[0]), "p1": xy(&p[1]), "p2": xy(&p[2])}),
|
||||
"ArcThreePoint" => json!({"start": arg(&p[0]), "end": arg(&p[1]), "through": xy(&p[2])}),
|
||||
"ArcCenterTwoPoint" => json!({"center": arg(&p[0]), "start": arg(&p[1]), "end": xy(&p[2])}),
|
||||
"ShapePolygonInscribed" | "ShapePolygonCircumscribed" => {
|
||||
"sketch.rectangle.two_point" => json!({"p0": xy(&p[0]), "p1": xy(&p[1])}),
|
||||
"sketch.rectangle.center" => json!({"center": xy(&p[0]), "corner": xy(&p[1])}),
|
||||
"sketch.rectangle.three_point" => json!({"p0": xy(&p[0]), "p1": xy(&p[1]), "p2": xy(&p[2])}),
|
||||
"sketch.circle.center" => json!({"center": arg(&p[0]), "radius": d(0, 1)}),
|
||||
"sketch.circle.two_point" => json!({"p0": xy(&p[0]), "p1": xy(&p[1])}),
|
||||
"sketch.circle.three_point" => json!({"p0": xy(&p[0]), "p1": xy(&p[1]), "p2": xy(&p[2])}),
|
||||
"sketch.arc.three_point" => json!({"start": arg(&p[0]), "end": arg(&p[1]), "through": xy(&p[2])}),
|
||||
"sketch.arc.center_point" => json!({"center": arg(&p[0]), "start": arg(&p[1]), "end": xy(&p[2])}),
|
||||
"sketch.polygon.inscribed" | "sketch.polygon.circumscribed" => {
|
||||
let a = (p[1].0 - p[0].0).angle().to_degrees();
|
||||
json!({"center": xy(&p[0]), "radius": d(0, 1), "sides": sides, "angle": a})
|
||||
}
|
||||
"ShapePolygonEdge" => json!({"p0": xy(&p[0]), "p1": xy(&p[1]), "sides": sides}),
|
||||
"ShapeSlotCenterToCenter" | "ShapeSlotOverall" => {
|
||||
"sketch.polygon.edge" => json!({"p0": xy(&p[0]), "p1": xy(&p[1]), "sides": sides}),
|
||||
"sketch.slot.center_to_center" | "sketch.slot.overall" => {
|
||||
let dir = (p[1].0 - p[0].0).normalized().unwrap_or(Vec2::X);
|
||||
let w = 2.0 * dir.cross(p[2].0 - p[0].0).abs();
|
||||
json!({"p0": xy(&p[0]), "p1": xy(&p[1]), "width": w.max(1e-3)})
|
||||
}
|
||||
"DrawPoint" => json!({"point": xy(&p[0])}),
|
||||
"sketch.point" => json!({"point": xy(&p[0])}),
|
||||
_ => return Vec::new(),
|
||||
};
|
||||
let curves: Vec<String> = app
|
||||
@@ -270,18 +270,18 @@ fn tie_snaps(app: &mut SolveApp, t: &Tool, curves: &[String]) {
|
||||
}
|
||||
}
|
||||
for (a, b) in ties {
|
||||
let _ = app.run("ConstraintCoincident", json!({"a": a, "b": b}));
|
||||
let _ = app.run("sketch.constraint.coincident", json!({"a": a, "b": b}));
|
||||
}
|
||||
}
|
||||
|
||||
fn run_pick(app: &mut SolveApp, t: &Tool) {
|
||||
let k = &t.picks;
|
||||
let params = match t.cmd {
|
||||
"ConstraintHorizontalVertical" => json!({"line": k[0]}),
|
||||
"ConstraintFix" => json!({"entity": k[0]}),
|
||||
"ConstraintCoincident" => json!({"a": k[0], "b": k[1]}),
|
||||
"ConstraintMidPoint" => json!({"point": k[0], "line": k[1]}),
|
||||
"ConstraintSymmetry" => json!({"a": k[0], "b": k[1], "line": k[2]}),
|
||||
"sketch.constraint.horizontal_vertical" => json!({"line": k[0]}),
|
||||
"sketch.constraint.fix" => json!({"entity": k[0]}),
|
||||
"sketch.constraint.coincident" => json!({"a": k[0], "b": k[1]}),
|
||||
"sketch.constraint.midpoint" => json!({"point": k[0], "line": k[1]}),
|
||||
"sketch.constraint.symmetry" => json!({"a": k[0], "b": k[1], "line": k[2]}),
|
||||
_ => json!({"a": k[0], "b": k[1]}),
|
||||
};
|
||||
let _ = app.run(t.cmd, params);
|
||||
@@ -347,7 +347,7 @@ pub fn preview(app: &SolveApp, t: &Tool, painter: &egui::Painter, proj: &Proj) {
|
||||
painter.add(Shape::line(v, stroke));
|
||||
};
|
||||
match (t.cmd, pts.as_slice()) {
|
||||
("DrawPolyline", [.., last]) => {
|
||||
("sketch.line", [.., last]) => {
|
||||
line(*last, h);
|
||||
// Horizontal/vertical inference marker.
|
||||
if let Some(horizontal) = crate::sketch_dims::axis_aligned(*last, h)
|
||||
@@ -363,14 +363,14 @@ pub fn preview(app: &SolveApp, t: &Tool, painter: &egui::Painter, proj: &Proj) {
|
||||
);
|
||||
}
|
||||
}
|
||||
("ShapeRectangleTwoPoint", [a]) => {
|
||||
("sketch.rectangle.two_point", [a]) => {
|
||||
let (b, c) = (Vec2::new(h.x, a.y), Vec2::new(a.x, h.y));
|
||||
line(*a, b);
|
||||
line(b, h);
|
||||
line(h, c);
|
||||
line(c, *a);
|
||||
}
|
||||
("ShapeRectangleCenter", [c]) => {
|
||||
("sketch.rectangle.center", [c]) => {
|
||||
let d = h - *c;
|
||||
let k =
|
||||
[Vec2::new(c.x - d.x, c.y - d.y), Vec2::new(c.x + d.x, c.y - d.y), Vec2::new(c.x + d.x, c.y + d.y), Vec2::new(c.x - d.x, c.y + d.y)];
|
||||
@@ -378,8 +378,8 @@ pub fn preview(app: &SolveApp, t: &Tool, painter: &egui::Painter, proj: &Proj) {
|
||||
line(k[i], k[(i + 1) % 4]);
|
||||
}
|
||||
}
|
||||
("CircleCenterRadius" | "ShapePolygonInscribed" | "ShapePolygonCircumscribed", [c]) => circle(*c, c.dist(h)),
|
||||
("CircleTwoPoint", [a]) => circle((*a + h) * 0.5, a.dist(h) * 0.5),
|
||||
("sketch.circle.center" | "sketch.polygon.inscribed" | "sketch.polygon.circumscribed", [c]) => circle(*c, c.dist(h)),
|
||||
("sketch.circle.two_point", [a]) => circle((*a + h) * 0.5, a.dist(h) * 0.5),
|
||||
(_, [a]) => line(*a, h),
|
||||
(_, [a, b]) => {
|
||||
line(*a, *b);
|
||||
|
||||
@@ -705,7 +705,7 @@ pub fn section_plane(app: &SolveApp) -> Option<(Vec3, Vec3)> {
|
||||
let cmds = crate::dialogs::apply_commands(app, d).ok()?;
|
||||
let (_, p) = cmds.into_iter().next()?;
|
||||
let mut scratch = app.session.scratch();
|
||||
scratch.execute("FusionHalfSectionViewCommand", &p).ok()?;
|
||||
scratch.execute("inspect.section", &p).ok()?;
|
||||
return scratch.section;
|
||||
}
|
||||
// Slice (Sketch Palette): cut the model at the plane of the sketch being edited.
|
||||
|
||||
@@ -8,83 +8,83 @@ use solvecraft_engine::CommandSpec;
|
||||
use solvecraft_engine::doc::FeatureKind;
|
||||
|
||||
const ASSEMBLE: &[&str] = &[
|
||||
"FusionCreateNewComponentCommand",
|
||||
"JointAssembleCmdNew",
|
||||
"JointAsBuiltCmd",
|
||||
"JointOrigin",
|
||||
"RigidGroupCmd",
|
||||
"FusionMoveJointsCommand",
|
||||
"FusionMotionRelationshipCommand",
|
||||
"FusionMotionStudyCommand",
|
||||
"component.create",
|
||||
"joint.create",
|
||||
"joint.as_built",
|
||||
"joint.origin",
|
||||
"joint.rigid_group",
|
||||
"joint.drive",
|
||||
"joint.motion_link",
|
||||
"motion.study",
|
||||
"explode.create",
|
||||
"EnableContactSetsCmd",
|
||||
"EnableAllContactCmd",
|
||||
"DisableAllContactCmd",
|
||||
"ContactSetCmd",
|
||||
"InterferenceCheckCommand",
|
||||
"contact.enable_sets",
|
||||
"contact.enable_all",
|
||||
"contact.disable_all",
|
||||
"contact.create",
|
||||
"inspect.interference",
|
||||
];
|
||||
|
||||
const SHEET_CREATE: &[&str] = &[
|
||||
"SketchCreate",
|
||||
"FusionSheetMetalFlangeCommand",
|
||||
"FusionSheetMetalFlatPatternCmd",
|
||||
"FusionSheetMetalHemFlangeCommand",
|
||||
"SheetMetalFoldCmd",
|
||||
"ConvertToSheetMetalCmd",
|
||||
"FusionCreateNewComponentCommand",
|
||||
"Extrude",
|
||||
"FusionHoleCommand",
|
||||
"FusionThreadCommand",
|
||||
"PatternRectangular",
|
||||
"PatternCircular",
|
||||
"PatternOnPath",
|
||||
"MirrorCommand",
|
||||
"JointOrigin",
|
||||
"sketch.create",
|
||||
"sheet.flange",
|
||||
"sheet.flat_pattern",
|
||||
"sheet.hem",
|
||||
"sheet.fold",
|
||||
"sheet.convert",
|
||||
"component.create",
|
||||
"solid.extrude",
|
||||
"solid.hole",
|
||||
"solid.thread",
|
||||
"solid.pattern.rectangular",
|
||||
"solid.pattern.circular",
|
||||
"solid.pattern.path",
|
||||
"solid.mirror",
|
||||
"joint.origin",
|
||||
];
|
||||
|
||||
const SHEET_MODIFY: &[&str] = &[
|
||||
"FusionSheetMetalRulesCommand",
|
||||
"FusionSheetmetalUnfoldCommand",
|
||||
"sheet.manage_rules",
|
||||
"sheet.unfold",
|
||||
"sheet.refold",
|
||||
"FusionFilletEdgesCommand",
|
||||
"FusionChamferCommand",
|
||||
"FusionMoveCommand",
|
||||
"AlignCmd",
|
||||
"FusionDeleteCommand",
|
||||
"SoftDeleteCommand",
|
||||
"PhysicalMaterialCommand",
|
||||
"ChangeParameterCommand",
|
||||
"solid.fillet",
|
||||
"solid.chamfer",
|
||||
"solid.move",
|
||||
"solid.align",
|
||||
"timeline.delete",
|
||||
"solid.remove",
|
||||
"material.assign",
|
||||
"parameters.change",
|
||||
];
|
||||
|
||||
const PLASTIC_SETUP: &[&str] = &["FusionManagePlasticRuleCommand", "FusionAssignPlasticRuleCommand"];
|
||||
const PLASTIC_SETUP: &[&str] = &["plastic.manage_rules", "plastic.assign_rule"];
|
||||
|
||||
const PLASTIC_CREATE: &[&str] = &[
|
||||
"SketchCreate",
|
||||
"FusionBossCommand",
|
||||
"FusionSnapFitCommand",
|
||||
"FusionLipCommand",
|
||||
"FusionRestCommand",
|
||||
"FusionCreateNewComponentCommand",
|
||||
"Extrude",
|
||||
"Revolve",
|
||||
"Sweep",
|
||||
"SolidLoft",
|
||||
"FusionRibCommand",
|
||||
"FusionWebCommand",
|
||||
"EmbossCmd",
|
||||
"FusionHoleCommand",
|
||||
"FusionThreadCommand",
|
||||
"PrimitiveBox",
|
||||
"PrimitiveCylinder",
|
||||
"PrimitiveSphere",
|
||||
"PrimitiveTorus",
|
||||
"PrimitiveCoil",
|
||||
"PrimitivePipe",
|
||||
"PatternRectangular",
|
||||
"PatternCircular",
|
||||
"PatternOnPath",
|
||||
"MirrorCommand",
|
||||
"JointOrigin",
|
||||
"sketch.create",
|
||||
"plastic.boss",
|
||||
"plastic.snap_fit",
|
||||
"plastic.lip",
|
||||
"plastic.rest",
|
||||
"component.create",
|
||||
"solid.extrude",
|
||||
"solid.revolve",
|
||||
"solid.sweep",
|
||||
"solid.loft",
|
||||
"solid.rib",
|
||||
"solid.web",
|
||||
"solid.emboss",
|
||||
"solid.hole",
|
||||
"solid.thread",
|
||||
"solid.box",
|
||||
"solid.cylinder",
|
||||
"solid.sphere",
|
||||
"solid.torus",
|
||||
"solid.coil",
|
||||
"solid.pipe",
|
||||
"solid.pattern.rectangular",
|
||||
"solid.pattern.circular",
|
||||
"solid.pattern.path",
|
||||
"solid.mirror",
|
||||
"joint.origin",
|
||||
];
|
||||
|
||||
/// Panels of a tab, when this module lays the tab out.
|
||||
@@ -152,17 +152,17 @@ mod tests {
|
||||
fn tabs_share_commands_in_fusion_order() {
|
||||
for tab in ["SOLID", "SHEET METAL", "PLASTIC"] {
|
||||
let a = ids(tab, "ASSEMBLE");
|
||||
assert_eq!(a.get(1).copied(), Some("JointAssembleCmdNew"), "{tab}: {a:?}");
|
||||
assert!(a.contains(&"FusionMoveJointsCommand") && a.contains(&"FusionCreateNewComponentCommand"));
|
||||
assert_eq!(a.get(1).copied(), Some("joint.create"), "{tab}: {a:?}");
|
||||
assert!(a.contains(&"joint.drive") && a.contains(&"component.create"));
|
||||
}
|
||||
let c = ids("SHEET METAL", "CREATE");
|
||||
assert_eq!(c.get(1).copied(), Some("FusionSheetMetalFlangeCommand"));
|
||||
assert!(c.contains(&"Extrude") && c.contains(&"FusionSheetMetalFlatPatternCmd"));
|
||||
assert!(ids("SHEET METAL", "MODIFY").contains(&"FusionSheetmetalUnfoldCommand"));
|
||||
assert!(ids("PLASTIC", "SETUP").contains(&"FusionManagePlasticRuleCommand"));
|
||||
assert_eq!(c.get(1).copied(), Some("sheet.flange"));
|
||||
assert!(c.contains(&"solid.extrude") && c.contains(&"sheet.flat_pattern"));
|
||||
assert!(ids("SHEET METAL", "MODIFY").contains(&"sheet.unfold"));
|
||||
assert!(ids("PLASTIC", "SETUP").contains(&"plastic.manage_rules"));
|
||||
let p = ids("PLASTIC", "CREATE");
|
||||
assert!(p.contains(&"FusionBossCommand") && p.contains(&"FusionRibCommand") && p.contains(&"FusionLipCommand"));
|
||||
assert!(ids("PLASTIC", "MODIFY").contains(&"FusionShellBodyCommand"));
|
||||
assert!(p.contains(&"plastic.boss") && p.contains(&"solid.rib") && p.contains(&"plastic.lip"));
|
||||
assert!(ids("PLASTIC", "MODIFY").contains(&"solid.shell"));
|
||||
assert!(panels("PLASTIC").is_some_and(|p| p.first() == Some(&"SETUP")));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -40,7 +40,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchDimension"
|
||||
"start": "sketch.dimension"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -77,7 +77,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"expect": {
|
||||
@@ -85,7 +85,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "20"
|
||||
@@ -104,7 +104,7 @@
|
||||
"shot": "s01_extrude"
|
||||
},
|
||||
{
|
||||
"start": "FusionFilletEdgesCommand"
|
||||
"start": "solid.fillet"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -145,7 +145,7 @@
|
||||
"shot": "s01_fillet"
|
||||
},
|
||||
{
|
||||
"start": "FusionShellBodyCommand"
|
||||
"start": "solid.shell"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -173,7 +173,7 @@
|
||||
"shot": "s01_shell"
|
||||
},
|
||||
{
|
||||
"start": "FusionHoleCommand"
|
||||
"start": "solid.hole"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
[
|
||||
{"start": "SketchCreate"},
|
||||
{"start": "sketch.create"},
|
||||
{"click": {"plane": "XZ"}},
|
||||
{"expect": {"sketching": true}},
|
||||
{"start": "ShapeRectangleTwoPoint"},
|
||||
{"start": "sketch.rectangle.two_point"},
|
||||
{"click": {"sketch": [10, 0]}},
|
||||
{"click": {"sketch": [20, 30]}},
|
||||
{"key": "Escape"},
|
||||
{"shot": "s02_sketch"},
|
||||
{"start": "SketchStop"},
|
||||
{"start": "sketch.finish"},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
{"start": "Revolve"},
|
||||
{"start": "solid.revolve"},
|
||||
{"click": {"axis": "Z"}},
|
||||
{"debug": 1},
|
||||
{"key": "Enter"},
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
[
|
||||
{"start": "SketchCreate"},
|
||||
{"start": "sketch.create"},
|
||||
{"click": {"plane": "XY"}},
|
||||
{"start": "ShapeRectangleTwoPoint"},
|
||||
{"start": "sketch.rectangle.two_point"},
|
||||
{"click": {"sketch": [0, 0]}},
|
||||
{"click": {"sketch": [40, 30]}},
|
||||
{"key": "Escape"},
|
||||
{"start": "SketchStop"},
|
||||
{"start": "sketch.finish"},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
{"start": "Extrude"},
|
||||
{"start": "solid.extrude"},
|
||||
{"text": "20"},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"bodies": 1, "volume": 24000}},
|
||||
{"start": "SketchCreate"},
|
||||
{"start": "sketch.create"},
|
||||
{"click": {"world": [20, 15, 20]}},
|
||||
{"expect": {"sketching": true}},
|
||||
{"start": "CircleCenterRadius"},
|
||||
{"start": "sketch.circle.center"},
|
||||
{"click": {"sketch": [20, 15]}},
|
||||
{"click": {"sketch": [25, 15]}},
|
||||
{"key": "Escape"},
|
||||
{"shot": "s03_sketch_on_face"},
|
||||
{"start": "SketchStop"},
|
||||
{"start": "Extrude"},
|
||||
{"start": "sketch.finish"},
|
||||
{"start": "solid.extrude"},
|
||||
{"click": {"world": [20, 15, 20]}},
|
||||
{"debug": 1},
|
||||
{"text": "-10"},
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
[
|
||||
{"start": "SketchCreate"},
|
||||
{"start": "sketch.create"},
|
||||
{"click": {"plane": "XY"}},
|
||||
{"start": "CircleCenterRadius"},
|
||||
{"start": "sketch.circle.center"},
|
||||
{"click": {"sketch": [20, 10]}},
|
||||
{"click": {"sketch": [25, 10]}},
|
||||
{"key": "Escape"},
|
||||
{"start": "SketchStop"},
|
||||
{"start": "sketch.finish"},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
{"start": "Extrude"},
|
||||
{"start": "solid.extrude"},
|
||||
{"text": "10"},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"bodies": 1}},
|
||||
{"start": "MirrorCommand"},
|
||||
{"start": "solid.mirror"},
|
||||
{"click": {"world": [20, 10, 10]}},
|
||||
{"call": "ui.dialogInput", "params": {"index": 1}},
|
||||
{"click": {"plane": "YZ"}},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchDimension"
|
||||
"start": "sketch.dimension"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -50,7 +50,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -59,7 +59,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -68,7 +68,7 @@
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "FusionFilletEdgesCommand"
|
||||
"start": "solid.fillet"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -130,7 +130,7 @@
|
||||
"shot": "s06_edit_sketch"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"expect": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -36,7 +36,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -45,7 +45,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -71,13 +71,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -85,7 +85,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -107,7 +107,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -116,7 +116,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -140,7 +140,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "JointAssembleCmdNew"
|
||||
"start": "joint.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -205,7 +205,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionMoveJointsCommand"
|
||||
"start": "joint.drive"
|
||||
},
|
||||
{
|
||||
"text": "90"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -36,7 +36,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -45,7 +45,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionSheetMetalFlangeCommand"
|
||||
"start": "sheet.flange"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -75,7 +75,7 @@
|
||||
"shot": "s08_base"
|
||||
},
|
||||
{
|
||||
"start": "FusionSheetMetalFlangeCommand"
|
||||
"start": "sheet.flange"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -100,7 +100,7 @@
|
||||
"shot": "s08_flange"
|
||||
},
|
||||
{
|
||||
"start": "FusionSheetMetalFlatPatternCmd"
|
||||
"start": "sheet.flat_pattern"
|
||||
},
|
||||
{
|
||||
"widget": "Export DXF\u2026"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -30,10 +30,10 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "15"
|
||||
@@ -99,7 +99,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionHoleCommand"
|
||||
"start": "solid.hole"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchDimension"
|
||||
"start": "sketch.dimension"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -50,10 +50,10 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -67,7 +67,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ChangeParameterCommand"
|
||||
"start": "parameters.change"
|
||||
},
|
||||
{
|
||||
"shot": "s10_params"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "DrawPolyline"
|
||||
"start": "sketch.line"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -63,7 +63,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "CircleCenterRadius"
|
||||
"start": "sketch.circle.center"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -85,7 +85,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchDimension"
|
||||
"start": "sketch.dimension"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -170,7 +170,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ConstraintFix"
|
||||
"start": "sketch.constraint.fix"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -208,7 +208,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"expect": {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
[
|
||||
{"start": "SketchCreate"},
|
||||
{"start": "sketch.create"},
|
||||
{"click": {"plane": "XY"}},
|
||||
{"start": "CircleCenterRadius"},
|
||||
{"start": "sketch.circle.center"},
|
||||
{"click": {"sketch": [10, 10]}},
|
||||
{"click": {"sketch": [14, 10]}},
|
||||
{"key": "Escape"},
|
||||
{"start": "SketchStop"},
|
||||
{"start": "sketch.finish"},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
{"start": "Extrude"},
|
||||
{"start": "solid.extrude"},
|
||||
{"text": "8"},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"bodies": 1}},
|
||||
{"start": "PatternRectangular"},
|
||||
{"start": "solid.pattern.rectangular"},
|
||||
{"click": {"world": [10, 10, 8]}},
|
||||
{"call": "ui.dialogInput", "params": {"index": 1}},
|
||||
{"click": {"axis": "X"}},
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -36,7 +36,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -45,7 +45,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "30"
|
||||
@@ -60,7 +60,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionShellBodyCommand"
|
||||
"start": "solid.shell"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -98,7 +98,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionBossCommand"
|
||||
"start": "plastic.boss"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -126,7 +126,7 @@
|
||||
"shot": "s13_boss"
|
||||
},
|
||||
{
|
||||
"start": "FusionLipCommand"
|
||||
"start": "plastic.lip"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -160,7 +160,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionSnapFitCommand"
|
||||
"start": "plastic.snap_fit"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{"until": {"home": false}},
|
||||
{"expect": {"errors": 0, "bodies": 1, "name": "Sample Plate", "param": {"width": 80}}},
|
||||
{"shot": "s15_sample"},
|
||||
{"start": "ChangeParameterCommand"},
|
||||
{"start": "parameters.change"},
|
||||
{"widget": "80 mm"},
|
||||
{"key": "A", "cmd": true},
|
||||
{"text": "100 mm"},
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
[
|
||||
{"start": "SketchCreate"},
|
||||
{"start": "sketch.create"},
|
||||
{"click": {"plane": "XY"}},
|
||||
{"start": "ShapeRectangleTwoPoint"},
|
||||
{"start": "sketch.rectangle.two_point"},
|
||||
{"click": {"sketch": [0, 0]}},
|
||||
{"click": {"sketch": [30, 20]}},
|
||||
{"key": "Escape"},
|
||||
{"start": "SketchStop"},
|
||||
{"start": "Extrude"},
|
||||
{"start": "sketch.finish"},
|
||||
{"start": "solid.extrude"},
|
||||
{"text": "10"},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"bodies": 1, "volume": 6000, "documents": 1}},
|
||||
{"key": "N", "cmd": true},
|
||||
{"expect": {"bodies": 0, "documents": 2, "features": 0}},
|
||||
{"start": "SketchCreate"},
|
||||
{"start": "sketch.create"},
|
||||
{"click": {"plane": "XY"}},
|
||||
{"start": "CircleCenterRadius"},
|
||||
{"start": "sketch.circle.center"},
|
||||
{"click": {"sketch": [0, 0]}},
|
||||
{"click": {"sketch": [10, 0]}},
|
||||
{"key": "Escape"},
|
||||
{"start": "SketchStop"},
|
||||
{"start": "Extrude"},
|
||||
{"start": "sketch.finish"},
|
||||
{"start": "solid.extrude"},
|
||||
{"text": "5"},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"bodies": 1, "volume": [1570.8, 1e-3]}},
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -30,7 +30,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "15"
|
||||
@@ -48,7 +48,7 @@
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -74,7 +74,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "DrawPolyline"
|
||||
"start": "sketch.line"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -126,10 +126,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"start": "PrimitivePipe"
|
||||
"start": "solid.pipe"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"choose_file": "pic.png"
|
||||
},
|
||||
{
|
||||
"start": "FusionAddCanvasCommand"
|
||||
"start": "canvas.insert"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -36,7 +36,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -45,7 +45,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -71,13 +71,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -85,7 +85,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -107,7 +107,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -116,7 +116,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -140,7 +140,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "JointAssembleCmdNew"
|
||||
"start": "joint.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -185,7 +185,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionHalfSectionViewCommand"
|
||||
"start": "inspect.section"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -205,7 +205,7 @@
|
||||
"shot": "s20_section"
|
||||
},
|
||||
{
|
||||
"start": "FusionMoveJointsCommand"
|
||||
"start": "joint.drive"
|
||||
},
|
||||
{
|
||||
"text": "90"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -30,10 +30,10 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"note": "Target: sheet metal plate folded along a sketch line (SheetMetalFoldCmd, solvecraft-params; dialog by solvecraft-dialogs). Adjust the dialog steps when it lands."
|
||||
"note": "Target: sheet metal plate folded along a sketch line (sheet.fold, solvecraft-params; dialog by solvecraft-dialogs). Adjust the dialog steps when it lands."
|
||||
},
|
||||
{
|
||||
"call": "ui.set",
|
||||
@@ -9,7 +9,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -39,7 +39,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionSheetMetalFlangeCommand"
|
||||
"start": "sheet.flange"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -69,7 +69,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -81,7 +81,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "DrawPolyline"
|
||||
"start": "sketch.line"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -106,10 +106,10 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"start": "SheetMetalFoldCmd"
|
||||
"start": "sheet.fold"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -139,7 +139,7 @@
|
||||
"shot": "s22_folded"
|
||||
},
|
||||
{
|
||||
"start": "FusionSheetMetalFlatPatternCmd"
|
||||
"start": "sheet.flat_pattern"
|
||||
},
|
||||
{
|
||||
"widget": "Export DXF\u2026"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[
|
||||
{
|
||||
"note": "Configurations: a parameter column (d1) in the configuration table switches the design (FusionStartDesignConfigModeCmd)."
|
||||
"note": "Configurations: a parameter column (d1) in the configuration table switches the design (config.configure)."
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -33,7 +33,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchDimension"
|
||||
"start": "sketch.dimension"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -53,10 +53,10 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -70,7 +70,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionStartDesignConfigModeCmd"
|
||||
"start": "config.configure"
|
||||
},
|
||||
{
|
||||
"widget": "Add Column"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
[
|
||||
{
|
||||
"note": "Motion study: key a revolute joint and play it (FusionMotionStudyCommand, motion.play)."
|
||||
"note": "Motion study: key a revolute joint and play it (motion.study, motion.play)."
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -39,7 +39,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -74,13 +74,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -88,7 +88,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -110,7 +110,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -119,7 +119,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -137,7 +137,7 @@
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "JointAssembleCmdNew"
|
||||
"start": "joint.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -167,7 +167,7 @@
|
||||
"widget": "OK"
|
||||
},
|
||||
{
|
||||
"start": "FusionMotionStudyCommand"
|
||||
"start": "motion.study"
|
||||
},
|
||||
{
|
||||
"widget": "Add Key"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"autosave": true
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -36,10 +36,10 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -36,7 +36,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -45,7 +45,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -71,13 +71,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -85,7 +85,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -107,7 +107,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -116,7 +116,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -140,7 +140,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "JointAssembleCmdNew"
|
||||
"start": "joint.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -188,7 +188,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionMoveJointsCommand"
|
||||
"start": "joint.drive"
|
||||
},
|
||||
{
|
||||
"text": "45"
|
||||
@@ -214,7 +214,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionMoveJointsCommand"
|
||||
"start": "joint.drive"
|
||||
},
|
||||
{
|
||||
"text": "-30 deg"
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
[
|
||||
{"note": "Move opens with nothing moved; the triad's X arrow moves +20 X only; a ring turns 90 degrees"},
|
||||
{"call": "engine.execute", "params": {"command": "PrimitiveBox", "params": {"length": 20, "width": 30, "height": 10}}},
|
||||
{"call": "engine.execute", "params": {"command": "solid.box", "params": {"length": 20, "width": 30, "height": 10}}},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
{"start": "FusionMoveCommand"},
|
||||
{"start": "solid.move"},
|
||||
{"click": {"world": [10, 15, 10]}},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"errors": 0, "dialog": null, "bbox": {"body": "Body1", "min": [0, 0, 0], "max": [20, 30, 10]}}},
|
||||
{"start": "FusionMoveCommand"},
|
||||
{"start": "solid.move"},
|
||||
{"click": {"world": [10, 15, 10]}},
|
||||
{"drag": [{"gizmo": "x"}, {"gizmo": "x", "by": [20, 0, 0]}]},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"errors": 0, "dialog": null, "bbox": {"body": "Body1", "min": [20, 0, 0], "max": [40, 30, 10]}}},
|
||||
{"start": "FusionMoveCommand"},
|
||||
{"start": "solid.move"},
|
||||
{"click": {"world": [30, 15, 10]}},
|
||||
{"drag": [{"gizmo": "rz", "turn": 60}, {"gizmo": "rz", "turn": 150}], "steps": 12},
|
||||
{"key": "Enter"},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"note": "Pattern a hole (Object Type: Features), the beginner workflow box \u2192 hole \u2192 pattern."
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -33,7 +33,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -42,7 +42,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "15"
|
||||
@@ -56,7 +56,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionHoleCommand"
|
||||
"start": "solid.hole"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -85,7 +85,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "PatternRectangular"
|
||||
"start": "solid.pattern.rectangular"
|
||||
},
|
||||
{
|
||||
"widget": "Bodies"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"note": "User bug 1 (owner solvecraft-sketch): Create Sketch on a body face projects the face's loops as reference geometry (linked), with no big square of real lines, and the face stays pickable for Project."
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -33,7 +33,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -42,7 +42,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "15"
|
||||
@@ -51,7 +51,7 @@
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -79,7 +79,7 @@
|
||||
"shot": "s26_face_sketch"
|
||||
},
|
||||
{
|
||||
"start": "ProjectNewCmd"
|
||||
"start": "sketch.project"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -102,7 +102,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "CircleCenterRadius"
|
||||
"start": "sketch.circle.center"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -131,7 +131,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"expect": {
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
"note": "User bug 2 (owner solvecraft-params): with two components, a cut made in the active one leaves the other untouched."
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -39,7 +39,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -74,13 +74,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -88,7 +88,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -110,7 +110,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -119,7 +119,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "20"
|
||||
@@ -154,7 +154,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -173,7 +173,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -202,10 +202,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"widget": "New Body"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
[
|
||||
{"note": "User bug 3 (owner solvecraft-ui): Move opens at 0, and dragging its X arrow moves only along X. The X arrow must be published as handle move_x (scenario::publish_handle)."},
|
||||
{"start": "SketchCreate"},
|
||||
{"start": "sketch.create"},
|
||||
{"click": {"plane": "XY"}},
|
||||
{"start": "ShapeRectangleTwoPoint"},
|
||||
{"start": "sketch.rectangle.two_point"},
|
||||
{"click": {"sketch": [0, 0]}},
|
||||
{"click": {"sketch": [20, 20]}},
|
||||
{"key": "Escape"},
|
||||
{"start": "SketchStop"},
|
||||
{"start": "sketch.finish"},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
{"start": "Extrude"},
|
||||
{"start": "solid.extrude"},
|
||||
{"text": "10"},
|
||||
{"key": "Enter"},
|
||||
{"call": "ui.menu", "params": {"x": 100, "y": 250, "target": {"type": "body", "name": "Body1"}}},
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
"note": "User bug 4 (owner solvecraft-repo): moving a component carries its sketches, and a sketch on a body face follows the body."
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -39,7 +39,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -57,7 +57,7 @@
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -69,7 +69,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "CircleCenterRadius"
|
||||
"start": "sketch.circle.center"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -91,7 +91,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"expect": {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
[
|
||||
{"note": "Construction mode like Fusion: with nothing selected, X (or the palette's Construction linetype) makes new curves construction until switched back; with a selection X converts it."},
|
||||
{"start": "SketchCreate"},
|
||||
{"start": "sketch.create"},
|
||||
{"click": {"plane": "XY"}},
|
||||
{"expect": {"dialog": null}},
|
||||
{"key": "X"},
|
||||
{"expect": {"tool": null}},
|
||||
{"start": "ShapeRectangleTwoPoint"},
|
||||
{"start": "sketch.rectangle.two_point"},
|
||||
{"click": {"sketch": [0, 0]}},
|
||||
{"click": {"sketch": [30, 20]}},
|
||||
{"key": "Escape"},
|
||||
{"start": "CircleCenterRadius"},
|
||||
{"start": "sketch.circle.center"},
|
||||
{"click": {"sketch": [15, 10]}},
|
||||
{"click": {"sketch": [20, 10]}},
|
||||
{"key": "Escape"},
|
||||
@@ -21,13 +21,13 @@
|
||||
{"key": "Y", "cmd": true},
|
||||
{"shot": "s35_construction_mode"},
|
||||
{"widget": "—"},
|
||||
{"start": "CircleCenterRadius"},
|
||||
{"start": "sketch.circle.center"},
|
||||
{"click": {"sketch": [40, 10]}},
|
||||
{"click": {"sketch": [45, 10]}},
|
||||
{"key": "Escape"},
|
||||
{"expect": {"query": {"command": "sketch.inspect", "path": ["curves", 5, "construction"], "value": false}}},
|
||||
{"widget": "- -"},
|
||||
{"start": "DrawPolyline"},
|
||||
{"start": "sketch.line"},
|
||||
{"click": {"sketch": [0, 30]}},
|
||||
{"click": {"sketch": [30, 30]}},
|
||||
{"key": "Escape"},
|
||||
@@ -38,7 +38,7 @@
|
||||
{"key": "X"},
|
||||
{"expect": {"query": {"command": "sketch.inspect", "path": ["curves", 5, "construction"], "value": true}}},
|
||||
{"key": "Escape"},
|
||||
{"start": "DrawPolyline"},
|
||||
{"start": "sketch.line"},
|
||||
{"click": {"sketch": [0, 40]}},
|
||||
{"click": {"sketch": [30, 40]}},
|
||||
{"key": "Escape"},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"expect": {
|
||||
@@ -34,7 +34,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"expect": {
|
||||
@@ -61,7 +61,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "ChangeParameterCommand"
|
||||
"start": "parameters.change"
|
||||
},
|
||||
{
|
||||
"widget": "Close window"
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
"note": "Browser chevrons expand and collapse every kind of node (folders, components, groups, the origin, named views, document settings). Model: two components with a body each, a joint, a sketch, an offset plane, a named view."
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -20,7 +20,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -42,7 +42,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -51,7 +51,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"text": "10"
|
||||
@@ -77,13 +77,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "FusionCreateNewComponentCommand"
|
||||
"start": "component.create"
|
||||
},
|
||||
{
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -91,7 +91,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "ShapeRectangleTwoPoint"
|
||||
"start": "sketch.rectangle.two_point"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -113,7 +113,7 @@
|
||||
"key": "Escape"
|
||||
},
|
||||
{
|
||||
"start": "SketchStop"
|
||||
"start": "sketch.finish"
|
||||
},
|
||||
{
|
||||
"call": "ui.view",
|
||||
@@ -122,7 +122,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "Extrude"
|
||||
"start": "solid.extrude"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -146,7 +146,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "JointAssembleCmdNew"
|
||||
"start": "joint.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -178,7 +178,7 @@
|
||||
{
|
||||
"call": "engine.execute",
|
||||
"params": {
|
||||
"command": "ConstructionPlaneOffsetFromPlaneCommand",
|
||||
"command": "construct.plane.offset",
|
||||
"params": {
|
||||
"base": "XY",
|
||||
"offset": 30
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"note": "Animating to Top or Bottom from iso, front or any orbit turns the view smoothly to the end: no frame (the last included) turns it more than 10 degrees."
|
||||
},
|
||||
{
|
||||
"start": "PrimitiveBox"
|
||||
"start": "solid.box"
|
||||
},
|
||||
{
|
||||
"key": "Escape"
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
{
|
||||
"expect": {
|
||||
"shown_handles": [
|
||||
"toolbar:Extrude",
|
||||
"toolbar:Revolve"
|
||||
"toolbar:solid.extrude",
|
||||
"toolbar:solid.revolve"
|
||||
],
|
||||
"hidden_handles": [
|
||||
"toolbar:FusionShellBodyCommand"
|
||||
"toolbar:solid.shell"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
"y": 80,
|
||||
"target": {
|
||||
"type": "toolbar_command",
|
||||
"id": "FusionShellBodyCommand"
|
||||
"id": "solid.shell"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -46,7 +46,7 @@
|
||||
{
|
||||
"expect": {
|
||||
"shown_handles": [
|
||||
"toolbar:FusionShellBodyCommand"
|
||||
"toolbar:solid.shell"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -57,7 +57,7 @@
|
||||
"y": 80,
|
||||
"target": {
|
||||
"type": "toolbar_command",
|
||||
"id": "Revolve"
|
||||
"id": "solid.revolve"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -70,17 +70,17 @@
|
||||
{
|
||||
"expect": {
|
||||
"hidden_handles": [
|
||||
"toolbar:Revolve"
|
||||
"toolbar:solid.revolve"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"drag": [
|
||||
{
|
||||
"handle": "toolbar:FusionShellBodyCommand"
|
||||
"handle": "toolbar:solid.shell"
|
||||
},
|
||||
{
|
||||
"handle": "toolbar:Extrude"
|
||||
"handle": "toolbar:solid.extrude"
|
||||
}
|
||||
],
|
||||
"steps": 12
|
||||
@@ -88,8 +88,8 @@
|
||||
{
|
||||
"expect": {
|
||||
"handle_order": [
|
||||
"toolbar:FusionShellBodyCommand",
|
||||
"toolbar:Extrude"
|
||||
"toolbar:solid.shell",
|
||||
"toolbar:solid.extrude"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -99,18 +99,18 @@
|
||||
{
|
||||
"expect": {
|
||||
"shown_handles": [
|
||||
"toolbar:FusionShellBodyCommand"
|
||||
"toolbar:solid.shell"
|
||||
],
|
||||
"hidden_handles": [
|
||||
"toolbar:Revolve"
|
||||
"toolbar:solid.revolve"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"expect": {
|
||||
"handle_order": [
|
||||
"toolbar:FusionShellBodyCommand",
|
||||
"toolbar:Extrude"
|
||||
"toolbar:solid.shell",
|
||||
"toolbar:solid.extrude"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[
|
||||
{"note": "Like Fusion, typing a number after picking the geometry goes into the dialog's heads-up value: Fillet after picking an edge, as Hole after picking a face and Extrude after opening do today."},
|
||||
{"start": "SketchCreate"}, {"click": {"plane": "XY"}},
|
||||
{"start": "ShapeRectangleTwoPoint"}, {"click": {"sketch": [0, 0]}}, {"click": {"sketch": [60, 40]}}, {"key": "Escape"},
|
||||
{"start": "SketchStop"},
|
||||
{"start": "sketch.create"}, {"click": {"plane": "XY"}},
|
||||
{"start": "sketch.rectangle.two_point"}, {"click": {"sketch": [0, 0]}}, {"click": {"sketch": [60, 40]}}, {"key": "Escape"},
|
||||
{"start": "sketch.finish"},
|
||||
{"call": "ui.view", "params": {"view": "home"}},
|
||||
{"start": "Extrude"}, {"text": "15"}, {"key": "Enter"},
|
||||
{"start": "solid.extrude"}, {"text": "15"}, {"key": "Enter"},
|
||||
{"expect": {"volume": 36000}},
|
||||
{"start": "FusionFilletEdgesCommand"},
|
||||
{"start": "solid.fillet"},
|
||||
{"click": {"world": [30, 0, 15]}},
|
||||
{"text": "3"},
|
||||
{"key": "Enter"},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"note": "Dragging sketch geometry snaps like Fusion: onto a point (coincident on release), level with a point (a dashed guide, no constraint), not at all with Ctrl held; a line dragged by its body snaps by its ends."
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
@@ -13,7 +13,7 @@
|
||||
{
|
||||
"call": "engine.execute",
|
||||
"params": {
|
||||
"command": "DrawPolyline",
|
||||
"command": "sketch.line",
|
||||
"params": {
|
||||
"points": [
|
||||
[
|
||||
@@ -31,7 +31,7 @@
|
||||
{
|
||||
"call": "engine.execute",
|
||||
"params": {
|
||||
"command": "DrawPolyline",
|
||||
"command": "sketch.line",
|
||||
"params": {
|
||||
"points": [
|
||||
[
|
||||
@@ -230,7 +230,7 @@
|
||||
{
|
||||
"call": "engine.execute",
|
||||
"params": {
|
||||
"command": "DrawPolyline",
|
||||
"command": "sketch.line",
|
||||
"params": {
|
||||
"points": [
|
||||
[
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
{
|
||||
"call": "engine.execute",
|
||||
"params": {
|
||||
"command": "FusionCreateNewComponentCommand",
|
||||
"command": "component.create",
|
||||
"params": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"call": "engine.execute",
|
||||
"params": {
|
||||
"command": "PrimitiveBox",
|
||||
"command": "solid.box",
|
||||
"params": {
|
||||
"length": 40,
|
||||
"width": 30,
|
||||
@@ -75,7 +75,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"start": "SketchCreate"
|
||||
"start": "sketch.create"
|
||||
},
|
||||
{
|
||||
"click": {
|
||||
|
||||
@@ -9,10 +9,10 @@ use solvecraft_ui_egui::scenario::{Harness, check};
|
||||
fn harness() -> Harness {
|
||||
let mut h = Harness::new();
|
||||
for (id, p) in [
|
||||
("SketchCreate", json!({"plane": "XY"})),
|
||||
("ShapeRectangleTwoPoint", json!({"p0": [0, 0], "p1": [40, 30]})),
|
||||
("SketchStop", json!({})),
|
||||
("Extrude", json!({"distance": 10})),
|
||||
("sketch.create", json!({"plane": "XY"})),
|
||||
("sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [40, 30]})),
|
||||
("sketch.finish", json!({})),
|
||||
("solid.extrude", json!({"distance": 10})),
|
||||
] {
|
||||
h.call("engine.execute", json!({"command": id, "params": p}));
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ fn window_widths(h: &Harness) -> Vec<(String, f32)> {
|
||||
|
||||
fn fresh() -> Harness {
|
||||
let mut h = Harness::new();
|
||||
h.call("engine.execute", json!({"command": "PrimitiveBox", "params": {"length": 40, "width": 30, "height": 20}}));
|
||||
h.call("engine.execute", json!({"command": "solid.box", "params": {"length": 40, "width": 30, "height": 20}}));
|
||||
h.frames(3);
|
||||
h
|
||||
}
|
||||
@@ -65,7 +65,7 @@ fn command_dialogs_shrink_to_their_content_within_limits() {
|
||||
fn common_dialogs_are_not_stretched() {
|
||||
// Simple dialogs have room to spare: they must not be pushed out to the maximum.
|
||||
let mut h = fresh();
|
||||
for id in ["Extrude", "FusionFilletEdgesCommand", "FusionShellBodyCommand", "FusionMoveCommand", "FusionHoleCommand"] {
|
||||
for id in ["solid.extrude", "solid.fillet", "solid.shell", "solid.move", "solid.hole"] {
|
||||
h.call("ui.start", json!({"command": id}));
|
||||
h.frames(6);
|
||||
let w: f32 = window_widths(&h).iter().map(|x| x.1).fold(0.0, f32::max);
|
||||
|
||||
Reference in new issue
Block a user