Surface toolset: icons and planned commands (#808)
Icons for the surface toolset, and its planned commands registered and disabled with the issue that tracks each one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dd084ec9e1
commit
c8d668108a
6 files changed
+437
-31
No files matched your search
@@ -1,5 +1,5 @@
|
||||
//! Surface commands: Patch, Stitch, Thicken and Trim (surface bodies and turning them into
|
||||
//! solids).
|
||||
//! solids), and the planned surface toolset (`planned!`: registered, not built yet).
|
||||
|
||||
use serde_json::Value;
|
||||
use solidcraft_doc::{FaceAt, FeatureKind, PlaneRef, ProfileSel, expr::Kind};
|
||||
@@ -30,6 +30,54 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.at("SURFACE", "MODIFY")
|
||||
.icon("trim")
|
||||
.params("body: surface name; plane: XY|XZ|YZ|plane name|{origin, normal} or tool: {body, point} (a face, its surface extended); keep: [x,y,z] (a point on the side to keep)"),
|
||||
planned!("surface.extrude", "Surface Extrude", "757")
|
||||
.at("SURFACE", "CREATE")
|
||||
.icon("surface_extrude")
|
||||
.params("planned: the parameters of solid.extrude, with open curve chains as well as profiles (walls only, no caps); caps?: bool (close a closed profile)"),
|
||||
planned!("surface.revolve", "Surface Revolve", "757")
|
||||
.at("SURFACE", "CREATE")
|
||||
.icon("surface_revolve")
|
||||
.params("planned: the parameters of solid.revolve, with open curve chains as well as profiles (no caps); caps?: bool"),
|
||||
planned!("surface.sweep", "Surface Sweep", "757")
|
||||
.at("SURFACE", "CREATE")
|
||||
.icon("surface_sweep")
|
||||
.params("planned: the parameters of solid.sweep, with an open profile chain as well as a closed one (no caps); caps?: bool"),
|
||||
planned!("surface.loft", "Surface Loft", "757")
|
||||
.at("SURFACE", "CREATE")
|
||||
.icon("surface_loft")
|
||||
.params("planned: the parameters of solid.loft, with open sections as well as closed ones (no caps); caps?: bool"),
|
||||
planned!("surface.offset", "Offset Surface", "758")
|
||||
.at("SURFACE", "CREATE")
|
||||
.icon("surface_offset")
|
||||
.params("planned: faces: [{body, point}]; distance: expr (signed, along the normals; 0 copies the faces); chain?: bool (the whole tangent chain of each pick); tolerance?: expr (B-spline fit, default 0.01 mm)"),
|
||||
planned!("surface.ruled", "Ruled Surface", "762")
|
||||
.at("SURFACE", "CREATE")
|
||||
.icon("ruled")
|
||||
.params("planned: body; edges: [[x,y,z]] (points on an edge chain); distance: expr; mode?: normal|tangent|angle|direction; angle?: expr; direction?: [x,y,z]; flip?: bool; corners?: mitre|round"),
|
||||
planned!("surface.boundary", "Boundary Surface", "763")
|
||||
.at("SURFACE", "CREATE")
|
||||
.icon("boundary")
|
||||
.params("planned: u: [curves], v?: [curves] (sketch curves, 3D sketch wires or {body, point} edges; 2–4 boundaries, more make a network); continuity?: [{curve, g: 0|1|2}] for edge boundaries; operation?: new|join"),
|
||||
planned!("surface.blend", "Blend Surface", "764")
|
||||
.at("SURFACE", "CREATE")
|
||||
.icon("surface_blend")
|
||||
.params("planned: from, to: {body, edges: [[x,y,z]]} (two edge chains apart); continuity?: [g0|g1|g2, g0|g1|g2] (each side); bulge?: [number, number]; align?: auto|[[x,y,z], [x,y,z]]; stitch?: bool"),
|
||||
planned!("surface.untrim", "Untrim", "761")
|
||||
.at("SURFACE", "MODIFY")
|
||||
.icon("untrim")
|
||||
.params("planned: face: {body, point}; holes?: all|[[x,y,z]] (points on the inner loops to remove); outer?: bool (restore the natural bounds); in_place?: bool (a surface body only)"),
|
||||
planned!("surface.unstitch", "Unstitch", "766")
|
||||
.at("SURFACE", "MODIFY")
|
||||
.icon("unstitch")
|
||||
.params("planned: body; faces?: [[x,y,z]] (points on the faces to split off; default: every face its own surface body)"),
|
||||
planned!("surface.reverse_normal", "Reverse Normal", "766")
|
||||
.at("SURFACE", "MODIFY")
|
||||
.icon("reverse_normal")
|
||||
.params("planned: body (a surface); faces?: [[x,y,z]] (default: all of them)"),
|
||||
planned!("surface.fillet", "Surface Fillet", "769")
|
||||
.at("SURFACE", "MODIFY")
|
||||
.icon("surface_fillet")
|
||||
.params("planned: a, b: {body, point} (two faces of separate bodies that meet or cross; the points pick the side the ball rolls on); radius: expr; trim?: both|first|none"),
|
||||
];
|
||||
|
||||
fn patch(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
@@ -129,6 +177,43 @@ mod tests {
|
||||
solidcraft_kernel::measure(&st.body(name).unwrap().body).unwrap().volume
|
||||
}
|
||||
|
||||
/// The planned surface commands are registered but refuse to run: disabled with the
|
||||
/// issue that tracks them, an error (never a panic) from every caller, and the design left
|
||||
/// as it was.
|
||||
#[test]
|
||||
fn planned_commands_refuse_and_leave_the_design_alone() {
|
||||
let planned = [
|
||||
"surface.extrude",
|
||||
"surface.revolve",
|
||||
"surface.sweep",
|
||||
"surface.loft",
|
||||
"surface.offset",
|
||||
"surface.ruled",
|
||||
"surface.boundary",
|
||||
"surface.blend",
|
||||
"surface.untrim",
|
||||
"surface.unstitch",
|
||||
"surface.reverse_normal",
|
||||
"surface.fillet",
|
||||
"inspect.continuity",
|
||||
];
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "body_name": "B"}));
|
||||
let before = s.doc.features.len();
|
||||
for id in planned {
|
||||
let spec = crate::find_command(id).unwrap_or_else(|| panic!("{id} is not registered"));
|
||||
let info = spec.info(&s);
|
||||
assert!(!info.enabled, "{id} must stay disabled until it is built");
|
||||
let why = info.disabled_reason.unwrap_or_default();
|
||||
assert!(why.starts_with("not supported yet (issue #"), "{id}: {why}");
|
||||
assert!(!spec.icon.is_empty() && !spec.params.is_empty(), "{id}: icon and params doc");
|
||||
assert!(s.execute(id, &json!({"body": "B"})).is_err(), "{id} ran");
|
||||
let e = (spec.run)(&mut s, &json!({})).unwrap_err().to_string();
|
||||
assert!(e.starts_with("not supported yet: ") && e.contains("(issue #"), "{id}: {e}");
|
||||
}
|
||||
assert_eq!(s.doc.features.len(), before, "a planned command changed the design");
|
||||
}
|
||||
|
||||
/// A rectangle patched, trimmed by a plane and thickened; a box's open top patched; a rim
|
||||
/// that is not planar patched.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user