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
@@ -22,7 +22,7 @@ fn sketch(s: &Session) -> solvecraft_sketch::Sketch {
|
||||
|
||||
fn new_sketch() -> Session {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "SketchCreate", json!({"plane": "XY"}));
|
||||
run(&mut s, "sketch.create", json!({"plane": "XY"}));
|
||||
s
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@ fn close(a: f64, b: f64) -> bool {
|
||||
fn trim_extend_break() {
|
||||
let mut s = new_sketch();
|
||||
// A cross: horizontal line crossed by a vertical one.
|
||||
let h = ids(&run(&mut s, "DrawPolyline", json!({"points": [[0, 0], [20, 0]]}))["curves"])[0].clone();
|
||||
let v = ids(&run(&mut s, "DrawPolyline", json!({"points": [[10, -5], [10, 5]]}))["curves"])[0].clone();
|
||||
let h = ids(&run(&mut s, "sketch.line", json!({"points": [[0, 0], [20, 0]]}))["curves"])[0].clone();
|
||||
let v = ids(&run(&mut s, "sketch.line", json!({"points": [[10, -5], [10, 5]]}))["curves"])[0].clone();
|
||||
// Trim the right half of the horizontal line.
|
||||
run(&mut s, "TrimSketchCmd", json!({"curve": h, "at": [15, 0]}));
|
||||
run(&mut s, "sketch.trim", json!({"curve": h, "at": [15, 0]}));
|
||||
let sk = sketch(&s);
|
||||
let sh = sk.shape(sk.curve_index(&h).unwrap()).unwrap();
|
||||
assert!(
|
||||
@@ -52,32 +52,32 @@ fn trim_extend_break() {
|
||||
"{sh:?}"
|
||||
);
|
||||
// Trim the lower half of the vertical line: the original id survives on the upper piece.
|
||||
run(&mut s, "TrimSketchCmd", json!({"curve": v, "at": [10, -3]}));
|
||||
run(&mut s, "sketch.trim", json!({"curve": v, "at": [10, -3]}));
|
||||
let sk = sketch(&s);
|
||||
assert_eq!(sk.curves.len(), 2);
|
||||
// Extend: a short line reaching toward the vertical one.
|
||||
let e = ids(&run(&mut s, "DrawPolyline", json!({"points": [[0, 3], [4, 3]]}))["curves"])[0].clone();
|
||||
run(&mut s, "ExtendSketchCmd", json!({"curve": e, "at": [4, 3]}));
|
||||
let e = ids(&run(&mut s, "sketch.line", json!({"points": [[0, 3], [4, 3]]}))["curves"])[0].clone();
|
||||
run(&mut s, "sketch.extend", json!({"curve": e, "at": [4, 3]}));
|
||||
let sk = sketch(&s);
|
||||
let end = sk.resolve_point(&format!("{e}.end")).and_then(|i| sk.point(i)).unwrap();
|
||||
assert!(end.dist(Vec2::new(10.0, 3.0)) < 1e-7, "{end:?}");
|
||||
// Break the horizontal line where... nothing crosses it inside: error.
|
||||
assert!(s.execute("BreakSketchCmd", &json!({"curve": h, "at": [5, 0]})).is_err());
|
||||
assert!(s.execute("sketch.break", &json!({"curve": h, "at": [5, 0]})).is_err());
|
||||
// Break the vertical at the extended line: two pieces.
|
||||
let r = run(&mut s, "BreakSketchCmd", json!({"curve": v, "at": [10, 1]}));
|
||||
let r = run(&mut s, "sketch.break", json!({"curve": v, "at": [10, 1]}));
|
||||
assert_eq!(ids(&r["curves"]).len(), 2, "{r}");
|
||||
// A lone curve trims away entirely.
|
||||
let lone = ids(&run(&mut s, "DrawPolyline", json!({"points": [[50, 50], [60, 50]]}))["curves"])[0].clone();
|
||||
run(&mut s, "TrimSketchCmd", json!({"curve": lone, "at": [55, 50]}));
|
||||
let lone = ids(&run(&mut s, "sketch.line", json!({"points": [[50, 50], [60, 50]]}))["curves"])[0].clone();
|
||||
run(&mut s, "sketch.trim", json!({"curve": lone, "at": [55, 50]}));
|
||||
assert!(sketch(&s).curve_index(&lone).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trim_circle_makes_an_arc_and_a_profile() {
|
||||
let mut s = new_sketch();
|
||||
let c = ids(&run(&mut s, "CircleCenterRadius", json!({"center": [0, 0], "radius": 10}))["curves"])[0].clone();
|
||||
run(&mut s, "DrawPolyline", json!({"points": [[-20, 0], [20, 0]]}));
|
||||
run(&mut s, "TrimSketchCmd", json!({"curve": c, "at": [0, -10]}));
|
||||
let c = ids(&run(&mut s, "sketch.circle.center", json!({"center": [0, 0], "radius": 10}))["curves"])[0].clone();
|
||||
run(&mut s, "sketch.line", json!({"points": [[-20, 0], [20, 0]]}));
|
||||
run(&mut s, "sketch.trim", json!({"curve": c, "at": [0, -10]}));
|
||||
let sk = sketch(&s);
|
||||
let ci = sk.curve_index(&c).unwrap();
|
||||
assert!(matches!(sk.curves[ci].kind, CurveKind::Arc { .. }));
|
||||
@@ -90,65 +90,65 @@ fn trim_circle_makes_an_arc_and_a_profile() {
|
||||
#[test]
|
||||
fn fillet_and_chamfers() {
|
||||
let mut s = new_sketch();
|
||||
let r = run(&mut s, "ShapeRectangleTwoPoint", json!({"p0": [0, 0], "p1": [40, 30]}));
|
||||
let r = run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [40, 30]}));
|
||||
let l = ids(&r["curves"]);
|
||||
// Pin the rectangle so a dimension change only moves the fillet.
|
||||
run(&mut s, "ConstraintFix", json!({"entity": l[2]}));
|
||||
run(&mut s, "ConstraintFix", json!({"entity": l[3]}));
|
||||
let f = run(&mut s, "FilletSketchCmd", json!({"a": l[0], "b": l[1], "radius": 5}));
|
||||
run(&mut s, "sketch.constraint.fix", json!({"entity": l[2]}));
|
||||
run(&mut s, "sketch.constraint.fix", json!({"entity": l[3]}));
|
||||
let f = run(&mut s, "sketch.fillet", json!({"a": l[0], "b": l[1], "radius": 5}));
|
||||
assert!(f["param"].is_string());
|
||||
let a = profiles(&s);
|
||||
assert!(close(a[0], 1200.0 - (25.0 - std::f64::consts::PI * 25.0 / 4.0)), "{a:?}");
|
||||
// The fillet radius is a dimension: change it.
|
||||
let pname = f["param"].as_str().unwrap().to_string();
|
||||
run(&mut s, "ChangeParameterCommand", json!({"name": pname, "expression": "8 mm"}));
|
||||
run(&mut s, "parameters.change", json!({"name": pname, "expression": "8 mm"}));
|
||||
let a = profiles(&s);
|
||||
assert!(close(a[0], 1200.0 - (64.0 - std::f64::consts::PI * 64.0 / 4.0)), "{a:?}");
|
||||
run(&mut s, "ChamferSketchEqualDistance", json!({"a": l[2], "b": l[3], "distance": 4}));
|
||||
run(&mut s, "sketch.chamfer.equal_distance", json!({"a": l[2], "b": l[3], "distance": 4}));
|
||||
let a2 = profiles(&s)[0];
|
||||
assert!(close(a[0] - a2, 8.0), "{a2}");
|
||||
run(&mut s, "ChamferSketchDistanceDistance", json!({"a": l[1], "b": l[2], "distance": 2, "distance2": 6}));
|
||||
run(&mut s, "sketch.chamfer.two_distance", json!({"a": l[1], "b": l[2], "distance": 2, "distance2": 6}));
|
||||
let a3 = profiles(&s)[0];
|
||||
assert!(close(a2 - a3, 6.0), "{a3}");
|
||||
// Distance-angle at the last corner (origin, 90 degrees): 45 deg gives an isosceles cut.
|
||||
run(&mut s, "ChamferSketchDistanceAngle", json!({"a": l[3], "b": l[0], "distance": 3, "angle": 45}));
|
||||
run(&mut s, "sketch.chamfer.distance_angle", json!({"a": l[3], "b": l[0], "distance": 3, "angle": 45}));
|
||||
let a4 = profiles(&s)[0];
|
||||
assert!(close(a3 - a4, 4.5), "{a4}");
|
||||
assert!(s.execute("FilletSketchCmd", &json!({"a": l[0], "b": l[2], "radius": 1})).is_err(), "no shared corner");
|
||||
assert!(s.execute("sketch.fillet", &json!({"a": l[0], "b": l[2], "radius": 1})).is_err(), "no shared corner");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn offset_chain_mirror_patterns_move_scale() {
|
||||
let mut s = new_sketch();
|
||||
let r = run(&mut s, "ShapeRectangleTwoPoint", json!({"p0": [0, 0], "p1": [40, 30]}));
|
||||
let r = run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [40, 30]}));
|
||||
let l = ids(&r["curves"]);
|
||||
// Outward offset by 2 (toward a point outside).
|
||||
let o = run(&mut s, "Offset", json!({"curves": [l[0]], "distance": 2, "side": [20, -10]}));
|
||||
let o = run(&mut s, "sketch.offset", json!({"curves": [l[0]], "distance": 2, "side": [20, -10]}));
|
||||
assert_eq!(ids(&o["curves"]).len(), 4, "{o}");
|
||||
let a = profiles(&s);
|
||||
assert!(close(a[1], 44.0 * 34.0 - 1200.0) || close(a.iter().sum::<f64>(), 44.0 * 34.0), "{a:?}");
|
||||
// Circle offset inward.
|
||||
let c = ids(&run(&mut s, "CircleCenterRadius", json!({"center": [100, 0], "radius": 10}))["curves"])[0].clone();
|
||||
run(&mut s, "Offset", json!({"curves": [c], "distance": 3, "side": [100, 0]}));
|
||||
let c = ids(&run(&mut s, "sketch.circle.center", json!({"center": [100, 0], "radius": 10}))["curves"])[0].clone();
|
||||
run(&mut s, "sketch.offset", json!({"curves": [c], "distance": 3, "side": [100, 0]}));
|
||||
// Mirror a circle about a vertical line.
|
||||
let m = ids(&run(&mut s, "DrawPolyline", json!({"points": [[120, -50], [120, 50]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "MirrorSketchCommand", json!({"entities": [c], "line": m}));
|
||||
let m = ids(&run(&mut s, "sketch.line", json!({"points": [[120, -50], [120, 50]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "sketch.mirror", json!({"entities": [c], "line": m}));
|
||||
let mc = ids(&r["curves"])[0].clone();
|
||||
let sk = sketch(&s);
|
||||
assert!(sk.center(sk.curve_index(&mc).unwrap()).unwrap().dist(Vec2::new(140.0, 0.0)) < 1e-7);
|
||||
// Circular pattern of the mirrored circle: 4 around (120, 0).
|
||||
let r = run(&mut s, "CircularSketchPatternCommand", json!({"entities": [mc], "center": [120, 0], "count": 4}));
|
||||
let r = run(&mut s, "sketch.pattern.circular", json!({"entities": [mc], "center": [120, 0], "count": 4}));
|
||||
assert_eq!(ids(&r["curves"]).len(), 3);
|
||||
let sk = sketch(&s);
|
||||
let c2 = sk.center(sk.curve_index(&ids(&r["curves"])[0]).unwrap()).unwrap();
|
||||
assert!(c2.dist(Vec2::new(120.0, 20.0)) < 1e-7, "{c2:?}");
|
||||
// Rectangular pattern 3 x 2.
|
||||
let small = ids(&run(&mut s, "CircleCenterRadius", json!({"center": [0, 100], "radius": 1}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "RectangularSketchPatternCommand", json!({"entities": [small], "count": 3, "spacing": 5, "count2": 2, "spacing2": 4}));
|
||||
let small = ids(&run(&mut s, "sketch.circle.center", json!({"center": [0, 100], "radius": 1}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "sketch.pattern.rectangular", json!({"entities": [small], "count": 3, "spacing": 5, "count2": 2, "spacing2": 4}));
|
||||
assert_eq!(ids(&r["curves"]).len(), 5);
|
||||
// Move and scale.
|
||||
run(&mut s, "sketch.move", json!({"entities": [small], "translate": [0, 10]}));
|
||||
run(&mut s, "SketchScaleCmd", json!({"entities": [small], "base": [0, 110], "factor": 3}));
|
||||
run(&mut s, "sketch.scale", json!({"entities": [small], "base": [0, 110], "factor": 3}));
|
||||
let sk = sketch(&s);
|
||||
let ci = sk.curve_index(&small).unwrap();
|
||||
assert!(sk.center(ci).unwrap().dist(Vec2::new(0.0, 110.0)) < 1e-7);
|
||||
@@ -162,31 +162,31 @@ fn offset_chain_mirror_patterns_move_scale() {
|
||||
#[test]
|
||||
fn new_creation_tools() {
|
||||
let mut s = new_sketch();
|
||||
let r = run(&mut s, "SketchMidpointLine", json!({"mid": [0, 0], "end": [10, 5]}));
|
||||
let r = run(&mut s, "sketch.line.midpoint", json!({"mid": [0, 0], "end": [10, 5]}));
|
||||
let l = ids(&r["curves"])[0].clone();
|
||||
let sk = sketch(&s);
|
||||
let start = sk.resolve_point(&format!("{l}.start")).and_then(|i| sk.point(i)).unwrap();
|
||||
assert!(start.dist(Vec2::new(-10.0, -5.0)) < 1e-9);
|
||||
// Tangent arc off the end of a horizontal line.
|
||||
let h = ids(&run(&mut s, "DrawPolyline", json!({"points": [[0, 20], [10, 20]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "ArcTangent", json!({"start": format!("{h}.end"), "end": [20, 30]}));
|
||||
let h = ids(&run(&mut s, "sketch.line", json!({"points": [[0, 20], [10, 20]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "sketch.arc.tangent", json!({"start": format!("{h}.end"), "end": [20, 30]}));
|
||||
let a = ids(&r["curves"])[0].clone();
|
||||
let sk = sketch(&s);
|
||||
let ai = sk.curve_index(&a).unwrap();
|
||||
assert!(sk.center(ai).unwrap().dist(Vec2::new(10.0, 30.0)) < 1e-7, "{:?}", sk.center(ai));
|
||||
assert!(close(sk.radius(ai).unwrap(), 10.0));
|
||||
// 2-tangent circle in the corner of two lines.
|
||||
let x = ids(&run(&mut s, "DrawPolyline", json!({"points": [[100, 0], [150, 0]]}))["curves"])[0].clone();
|
||||
let y = ids(&run(&mut s, "DrawPolyline", json!({"points": [[100, 0], [100, 50]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "CircleTanTanRadius", json!({"curves": [x, y], "radius": 5, "near": [106, 104 - 100]}));
|
||||
let x = ids(&run(&mut s, "sketch.line", json!({"points": [[100, 0], [150, 0]]}))["curves"])[0].clone();
|
||||
let y = ids(&run(&mut s, "sketch.line", json!({"points": [[100, 0], [100, 50]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "sketch.circle.two_tangent", json!({"curves": [x, y], "radius": 5, "near": [106, 104 - 100]}));
|
||||
let c = ids(&r["curves"])[0].clone();
|
||||
let sk = sketch(&s);
|
||||
let ci = sk.curve_index(&c).unwrap();
|
||||
assert!(sk.center(ci).unwrap().dist(Vec2::new(105.0, 5.0)) < 1e-6, "{:?}", sk.center(ci));
|
||||
// 3-tangent circle inside a triangle: the incircle of the 3-4-5 triangle (r = 1).
|
||||
let t = run(&mut s, "DrawPolyline", json!({"points": [[200, 0], [203, 0], [200, 4]], "closed": true}));
|
||||
let t = run(&mut s, "sketch.line", json!({"points": [[200, 0], [203, 0], [200, 4]], "closed": true}));
|
||||
let tl = ids(&t["curves"]);
|
||||
let r = run(&mut s, "CircleThreeTangent", json!({"curves": tl, "near": [201, 1.2]}));
|
||||
let r = run(&mut s, "sketch.circle.three_tangent", json!({"curves": tl, "near": [201, 1.2]}));
|
||||
let c = ids(&r["curves"])[0].clone();
|
||||
let sk = sketch(&s);
|
||||
let ci = sk.curve_index(&c).unwrap();
|
||||
@@ -194,9 +194,9 @@ fn new_creation_tools() {
|
||||
assert!(sk.center(ci).unwrap().dist(Vec2::new(201.0, 1.0)) < 1e-6);
|
||||
// Slots.
|
||||
let before = profiles(&s).len();
|
||||
run(&mut s, "ShapeSlotCenterPoint", json!({"center": [0, -100], "end": [10, -100], "width": 4}));
|
||||
run(&mut s, "ShapeArcSlotThreePoint", json!({"start": [100, -100], "through": [110, -90], "end": [120, -100], "width": 2}));
|
||||
run(&mut s, "ShapeArcSlotCenterTwoPoint", json!({"center": [200, -100], "start": [210, -100], "end": [200, -90], "width": 2}));
|
||||
run(&mut s, "sketch.slot.center_point", json!({"center": [0, -100], "end": [10, -100], "width": 4}));
|
||||
run(&mut s, "sketch.slot.arc_three_point", json!({"start": [100, -100], "through": [110, -90], "end": [120, -100], "width": 2}));
|
||||
run(&mut s, "sketch.slot.arc_center", json!({"center": [200, -100], "start": [210, -100], "end": [200, -90], "width": 2}));
|
||||
let a = profiles(&s);
|
||||
assert_eq!(a.len(), before + 3, "{a:?}");
|
||||
let pi = std::f64::consts::PI;
|
||||
@@ -210,13 +210,13 @@ fn new_creation_tools() {
|
||||
fn ellipse_splines_conic() {
|
||||
let pi = std::f64::consts::PI;
|
||||
let mut s = new_sketch();
|
||||
let e = run(&mut s, "CircleElipse", json!({"center": [0, 0], "major": [10, 0], "minor": [0, 4]}));
|
||||
let e = run(&mut s, "sketch.ellipse", json!({"center": [0, 0], "major": [10, 0], "minor": [0, 4]}));
|
||||
let eid = ids(&e["curves"])[0].clone();
|
||||
let a = profiles(&s);
|
||||
assert!((a[0] - pi * 40.0).abs() / (pi * 40.0) < 2e-3, "{a:?}");
|
||||
// A point constrained onto the ellipse lands on it.
|
||||
run(&mut s, "DrawPoint", json!({"point": [3, 5], "id": "q"}));
|
||||
run(&mut s, "ConstraintCoincident", json!({"a": "q", "b": eid}));
|
||||
run(&mut s, "sketch.point", json!({"point": [3, 5], "id": "q"}));
|
||||
run(&mut s, "sketch.constraint.coincident", json!({"a": "q", "b": eid}));
|
||||
let sk = sketch(&s);
|
||||
let q = sk.point(sk.point_index("q").unwrap()).unwrap();
|
||||
let poly = sk.polyline(sk.curve_index(&eid).unwrap());
|
||||
@@ -227,9 +227,9 @@ fn ellipse_splines_conic() {
|
||||
});
|
||||
assert!(d.fold(f64::INFINITY, f64::min) < 1e-6, "{q:?}");
|
||||
// Fit spline closed by a line: one profile.
|
||||
let sp = run(&mut s, "DrawSpline", json!({"points": [[100, 0], [110, 8], [120, 3], [130, 0]]}));
|
||||
let sp = run(&mut s, "sketch.spline.fit_point", json!({"points": [[100, 0], [110, 8], [120, 3], [130, 0]]}));
|
||||
let sid = ids(&sp["curves"])[0].clone();
|
||||
run(&mut s, "DrawPolyline", json!({"points": [format!("{sid}.end"), format!("{sid}.start")]}));
|
||||
run(&mut s, "sketch.line", json!({"points": [format!("{sid}.end"), format!("{sid}.start")]}));
|
||||
assert_eq!(profiles(&s).len(), 2);
|
||||
// Moving a fit point reshapes the spline (it is a sketch point).
|
||||
let sk = sketch(&s);
|
||||
@@ -240,31 +240,31 @@ fn ellipse_splines_conic() {
|
||||
let sk = sketch(&s);
|
||||
assert!(sk.polyline(si).iter().any(|q| q.dist(Vec2::new(110.0, 12.0)) < 1e-9));
|
||||
// Control point splines (degree 3 and 5) and a conic.
|
||||
run(&mut s, "DrawCVMSpline3D", json!({"points": [[0, 50], [0, 60], [10, 60], [10, 50]]}));
|
||||
run(&mut s, "DrawCVMSpline5D", json!({"points": [[20, 50], [20, 60], [25, 65], [30, 60], [35, 62], [40, 50]]}));
|
||||
run(&mut s, "sketch.spline.control_point", json!({"points": [[0, 50], [0, 60], [10, 60], [10, 50]]}));
|
||||
run(&mut s, "sketch.spline.control_point_5", json!({"points": [[20, 50], [20, 60], [25, 65], [30, 60], [35, 62], [40, 50]]}));
|
||||
let w = std::f64::consts::FRAC_1_SQRT_2;
|
||||
let k = run(&mut s, "ConicCurveCmd", json!({"start": [210, 0], "end": [200, 10], "apex": [210, 10], "rho": w / (1.0 + w)}));
|
||||
let k = run(&mut s, "sketch.conic", json!({"start": [210, 0], "end": [200, 10], "apex": [210, 10], "rho": w / (1.0 + w)}));
|
||||
let kid = ids(&k["curves"])[0].clone();
|
||||
// A line tangent to the conic at its end: the tangent arc tool continues it.
|
||||
let r = run(&mut s, "ArcTangent", json!({"start": format!("{kid}.end"), "end": [190, 0]}));
|
||||
let r = run(&mut s, "sketch.arc.tangent", json!({"start": format!("{kid}.end"), "end": [190, 0]}));
|
||||
let aid = ids(&r["curves"])[0].clone();
|
||||
let sk = sketch(&s);
|
||||
// The conic is a quarter circle about (200, 0) leaving (200, 10) heading -x: the tangent
|
||||
// arc from there to (190, 0) continues the same circle.
|
||||
let ai = sk.curve_index(&aid).unwrap();
|
||||
assert!(sk.center(ai).unwrap().dist(Vec2::new(200.0, 0.0)) < 1e-6, "{:?}", sk.center(ai));
|
||||
assert!(s.execute("DrawSpline", &json!({"points": [[0, 0]]})).is_err());
|
||||
assert!(s.execute("ConicCurveCmd", &json!({"start": [0, 0], "end": [1, 0], "apex": [0, 1], "rho": 1.5})).is_err());
|
||||
assert!(s.execute("sketch.spline.fit_point", &json!({"points": [[0, 0]]})).is_err());
|
||||
assert!(s.execute("sketch.conic", &json!({"start": [0, 0], "end": [1, 0], "apex": [0, 1], "rho": 1.5})).is_err());
|
||||
// Mirror and pattern copy free-form curves too.
|
||||
let m = ids(&run(&mut s, "DrawPolyline", json!({"points": [[-50, -50], [-50, 50]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "MirrorSketchCommand", json!({"entities": [eid, sid], "line": m}));
|
||||
let m = ids(&run(&mut s, "sketch.line", json!({"points": [[-50, -50], [-50, 50]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "sketch.mirror", json!({"entities": [eid, sid], "line": m}));
|
||||
assert_eq!(ids(&r["curves"]).len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_makes_profiles_that_extrude_and_edit() {
|
||||
let mut s = new_sketch();
|
||||
let r = run(&mut s, "MTextCmd", json!({"text": "SO", "at": [0, 0], "height": 10}));
|
||||
let r = run(&mut s, "sketch.text", json!({"text": "SO", "at": [0, 0], "height": 10}));
|
||||
let link = r["link"].as_str().unwrap().to_string();
|
||||
let a = profiles(&s);
|
||||
// S (one region) + O (ring = outer with a hole, plus the counter as its own region).
|
||||
@@ -276,21 +276,21 @@ fn text_makes_profiles_that_extrude_and_edit() {
|
||||
assert_eq!(r2["link"], json!(link));
|
||||
let a = profiles(&s);
|
||||
assert_eq!(a.len(), 1, "{a:?}");
|
||||
run(&mut s, "SketchStop", json!({}));
|
||||
run(&mut s, "Extrude", json!({"distance": 2}));
|
||||
let m = run(&mut s, "MeasureCommand", json!({}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"distance": 2}));
|
||||
let m = run(&mut s, "inspect.measure", json!({}));
|
||||
let v = m["total"]["volume_mm3"].as_f64().unwrap();
|
||||
// An "I" 20 mm tall is a bar a couple of mm wide.
|
||||
assert!(v > 40.0 && v < 200.0, "{v}");
|
||||
assert!(s.execute("MTextCmd", &json!({"text": "", "at": [0, 0]})).is_err());
|
||||
assert!(s.execute("sketch.text", &json!({"text": "", "at": [0, 0]})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blend_curve_joins_tangentially() {
|
||||
let mut s = new_sketch();
|
||||
let a = ids(&run(&mut s, "DrawPolyline", json!({"points": [[0, 0], [10, 0]]}))["curves"])[0].clone();
|
||||
let b = ids(&run(&mut s, "DrawPolyline", json!({"points": [[20, 10], [20, 20]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "BlendG1CurveSketchCmd", json!({"a": format!("{a}.end"), "b": format!("{b}.start")}));
|
||||
let a = ids(&run(&mut s, "sketch.line", json!({"points": [[0, 0], [10, 0]]}))["curves"])[0].clone();
|
||||
let b = ids(&run(&mut s, "sketch.line", json!({"points": [[20, 10], [20, 20]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "sketch.blend_curve", json!({"a": format!("{a}.end"), "b": format!("{b}.start")}));
|
||||
assert_eq!(r["sketch"]["solved"], true, "{r}");
|
||||
let sp = ids(&r["curves"])[0].clone();
|
||||
let sk = sketch(&s);
|
||||
@@ -305,9 +305,9 @@ fn blend_curve_joins_tangentially() {
|
||||
#[test]
|
||||
fn centerline_is_left_out_of_profiles() {
|
||||
let mut s = new_sketch();
|
||||
let r = run(&mut s, "ShapeRectangleTwoPoint", json!({"p0": [0, 0], "p1": [10, 10]}));
|
||||
let r = run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [10, 10]}));
|
||||
let l = ids(&r["curves"]);
|
||||
let m = ids(&run(&mut s, "DrawPolyline", json!({"points": [[5, -5], [5, 15]]}))["curves"])[0].clone();
|
||||
let m = ids(&run(&mut s, "sketch.line", json!({"points": [[5, -5], [5, 15]]}))["curves"])[0].clone();
|
||||
assert_eq!(profiles(&s).len(), 2);
|
||||
run(&mut s, "sketch.centerline", json!({"curves": [m]}));
|
||||
assert_eq!(profiles(&s).len(), 1);
|
||||
@@ -318,57 +318,59 @@ fn centerline_is_left_out_of_profiles() {
|
||||
#[test]
|
||||
fn free_form_profiles_extrude_exactly() {
|
||||
let pi = std::f64::consts::PI;
|
||||
let vol = |s: &mut Session| run(s, "MeasureCommand", json!({}))["total"]["volume_mm3"].as_f64().unwrap();
|
||||
let vol = |s: &mut Session| run(s, "inspect.measure", json!({}))["total"]["volume_mm3"].as_f64().unwrap();
|
||||
// Ellipse a = 10, b = 4: π a b h.
|
||||
let mut s = new_sketch();
|
||||
run(&mut s, "CircleElipse", json!({"center": [0, 0], "major": [10, 0], "minor_radius": 4}));
|
||||
run(&mut s, "sketch.ellipse", json!({"center": [0, 0], "major": [10, 0], "minor_radius": 4}));
|
||||
let a = profiles(&s)[0];
|
||||
assert!((a - pi * 40.0).abs() < 1e-9, "{a}");
|
||||
run(&mut s, "SketchStop", json!({}));
|
||||
run(&mut s, "Extrude", json!({"distance": 5}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"distance": 5}));
|
||||
let v = vol(&mut s);
|
||||
assert!((v - pi * 40.0 * 5.0).abs() / (pi * 200.0) < 2e-4, "{v}");
|
||||
let m = run(&mut s, "MeasureCommand", json!({}));
|
||||
let m = run(&mut s, "inspect.measure", json!({}));
|
||||
assert_eq!(m["bodies"][0]["faces"], 3, "an exact ellipse: top, bottom and one side ({m})");
|
||||
// A region closed by a fit spline and a line.
|
||||
let mut s = new_sketch();
|
||||
let sp = ids(&run(&mut s, "DrawSpline", json!({"points": [[0, 0], [10, 8], [20, 0]]}))["curves"])[0].clone();
|
||||
run(&mut s, "DrawPolyline", json!({"points": [format!("{sp}.end"), format!("{sp}.start")]}));
|
||||
let sp = ids(&run(&mut s, "sketch.spline.fit_point", json!({"points": [[0, 0], [10, 8], [20, 0]]}))["curves"])[0].clone();
|
||||
run(&mut s, "sketch.line", json!({"points": [format!("{sp}.end"), format!("{sp}.start")]}));
|
||||
let a = profiles(&s)[0];
|
||||
run(&mut s, "SketchStop", json!({}));
|
||||
run(&mut s, "Extrude", json!({"distance": 2}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"distance": 2}));
|
||||
let v = vol(&mut s);
|
||||
assert!((v - a * 2.0).abs() / (a * 2.0) < 2e-4, "{v} vs {}", a * 2.0);
|
||||
let m = run(&mut s, "MeasureCommand", json!({}));
|
||||
let m = run(&mut s, "inspect.measure", json!({}));
|
||||
assert!(m["bodies"][0]["faces"].as_u64().unwrap() <= 5, "{m}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn degree_five_and_cut_free_form_profiles_extrude_exactly() {
|
||||
let vol = |s: &mut Session| run(s, "MeasureCommand", json!({}))["total"]["volume_mm3"].as_f64().unwrap();
|
||||
let vol = |s: &mut Session| run(s, "inspect.measure", json!({}))["total"]["volume_mm3"].as_f64().unwrap();
|
||||
// A degree-5 control spline closed by a line: one B-spline side face.
|
||||
let mut s = new_sketch();
|
||||
let sp = ids(&run(&mut s, "DrawCVMSpline5D", json!({"points": [[0, 0], [4, 10], [10, 12], [16, 12], [22, 10], [26, 0]]}))["curves"])[0].clone();
|
||||
run(&mut s, "DrawPolyline", json!({"points": [format!("{sp}.end"), format!("{sp}.start")]}));
|
||||
let sp =
|
||||
ids(&run(&mut s, "sketch.spline.control_point_5", json!({"points": [[0, 0], [4, 10], [10, 12], [16, 12], [22, 10], [26, 0]]}))["curves"])[0]
|
||||
.clone();
|
||||
run(&mut s, "sketch.line", json!({"points": [format!("{sp}.end"), format!("{sp}.start")]}));
|
||||
let a = profiles(&s)[0];
|
||||
run(&mut s, "SketchStop", json!({}));
|
||||
run(&mut s, "Extrude", json!({"distance": 3}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"distance": 3}));
|
||||
let v = vol(&mut s);
|
||||
assert!((v - a * 3.0).abs() / (a * 3.0) < 2e-4, "{v} vs {}", a * 3.0);
|
||||
let m = run(&mut s, "MeasureCommand", json!({}));
|
||||
let m = run(&mut s, "inspect.measure", json!({}));
|
||||
assert_eq!(m["bodies"][0]["faces"], 4, "{m}");
|
||||
// A fit spline arch cut by a line: both sides extrude with exact curved faces.
|
||||
let mut s = new_sketch();
|
||||
let sp = ids(&run(&mut s, "DrawSpline", json!({"points": [[0, 0], [6, 9], [14, 9], [20, 0]]}))["curves"])[0].clone();
|
||||
run(&mut s, "DrawPolyline", json!({"points": [format!("{sp}.end"), format!("{sp}.start")]}));
|
||||
run(&mut s, "DrawPolyline", json!({"points": [[10, -5], [10, 20]]}));
|
||||
let sp = ids(&run(&mut s, "sketch.spline.fit_point", json!({"points": [[0, 0], [6, 9], [14, 9], [20, 0]]}))["curves"])[0].clone();
|
||||
run(&mut s, "sketch.line", json!({"points": [format!("{sp}.end"), format!("{sp}.start")]}));
|
||||
run(&mut s, "sketch.line", json!({"points": [[10, -5], [10, 20]]}));
|
||||
let areas = profiles(&s);
|
||||
assert_eq!(areas.len(), 2, "{areas:?}");
|
||||
run(&mut s, "SketchStop", json!({}));
|
||||
run(&mut s, "Extrude", json!({"distance": 2, "profiles": [0]}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"distance": 2, "profiles": [0]}));
|
||||
let v = vol(&mut s);
|
||||
assert!((v - areas[0] * 2.0).abs() / (areas[0] * 2.0) < 2e-4, "{v} vs {}", areas[0] * 2.0);
|
||||
let m = run(&mut s, "MeasureCommand", json!({}));
|
||||
let m = run(&mut s, "inspect.measure", json!({}));
|
||||
assert!(m["bodies"][0]["faces"].as_u64().unwrap() <= 7, "no polyline facets: {m}");
|
||||
}
|
||||
|
||||
@@ -378,9 +380,9 @@ fn trim_and_break_free_form_curves_exactly() {
|
||||
let mut s = new_sketch();
|
||||
// An ellipse cut by a line through its centre: trimming the lower half leaves the upper
|
||||
// half, which closes with the line into half the ellipse.
|
||||
let e = ids(&run(&mut s, "CircleElipse", json!({"center": [0, 0], "major": [10, 0], "minor_radius": 4}))["curves"])[0].clone();
|
||||
let l = ids(&run(&mut s, "DrawPolyline", json!({"points": [[-15, 0], [15, 0]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "TrimSketchCmd", json!({"curve": e, "at": [0, -4]}));
|
||||
let e = ids(&run(&mut s, "sketch.ellipse", json!({"center": [0, 0], "major": [10, 0], "minor_radius": 4}))["curves"])[0].clone();
|
||||
let l = ids(&run(&mut s, "sketch.line", json!({"points": [[-15, 0], [15, 0]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "sketch.trim", json!({"curve": e, "at": [0, -4]}));
|
||||
assert!(!r["result"]["pieces"].as_array().unwrap().is_empty(), "{r}");
|
||||
let a = profiles(&s);
|
||||
assert_eq!(a.len(), 1, "{a:?}");
|
||||
@@ -388,10 +390,10 @@ fn trim_and_break_free_form_curves_exactly() {
|
||||
let _ = l;
|
||||
// A fit spline broken where a line crosses it: two pieces that still trace the curve.
|
||||
let mut s = new_sketch();
|
||||
let sp = ids(&run(&mut s, "DrawSpline", json!({"points": [[0, 0], [10, 10], [20, 0], [30, 5]]}))["curves"])[0].clone();
|
||||
let sp = ids(&run(&mut s, "sketch.spline.fit_point", json!({"points": [[0, 0], [10, 10], [20, 0], [30, 5]]}))["curves"])[0].clone();
|
||||
let before = sketch(&s).polyline(sketch(&s).curve_index(&sp).unwrap());
|
||||
run(&mut s, "DrawPolyline", json!({"points": [[15, -10], [15, 20]]}));
|
||||
let r = run(&mut s, "BreakSketchCmd", json!({"curve": sp, "at": [15, 5]}));
|
||||
run(&mut s, "sketch.line", json!({"points": [[15, -10], [15, 20]]}));
|
||||
let r = run(&mut s, "sketch.break", json!({"curve": sp, "at": [15, 5]}));
|
||||
let pieces = ids(&r["curves"]);
|
||||
assert_eq!(pieces.len(), 4, "three Béziers, the middle one split: {r}");
|
||||
let sk = sketch(&s);
|
||||
@@ -415,9 +417,9 @@ fn trim_and_break_free_form_curves_exactly() {
|
||||
#[test]
|
||||
fn ellipse_radii_dimensions() {
|
||||
let mut s = new_sketch();
|
||||
let e = ids(&run(&mut s, "CircleElipse", json!({"center": [0, 0], "major": [10, 0], "minor_radius": 4}))["curves"])[0].clone();
|
||||
run(&mut s, "SketchDimension", json!({"entities": [e], "type": "major", "value": 12}));
|
||||
run(&mut s, "SketchDimension", json!({"entities": [e], "value": 5}));
|
||||
let e = ids(&run(&mut s, "sketch.ellipse", json!({"center": [0, 0], "major": [10, 0], "minor_radius": 4}))["curves"])[0].clone();
|
||||
run(&mut s, "sketch.dimension", json!({"entities": [e], "type": "major", "value": 12}));
|
||||
run(&mut s, "sketch.dimension", json!({"entities": [e], "value": 5}));
|
||||
let a = profiles(&s)[0];
|
||||
assert!((a - std::f64::consts::PI * 60.0).abs() < 1e-6, "{a}");
|
||||
}
|
||||
@@ -426,10 +428,11 @@ fn ellipse_radii_dimensions() {
|
||||
fn fillet_a_line_and_an_arc() {
|
||||
let mut s = new_sketch();
|
||||
// A line into a quarter arc meeting at a sharp corner.
|
||||
let l = ids(&run(&mut s, "DrawPolyline", json!({"points": [[0, 0], [20, 0]]}))["curves"])[0].clone();
|
||||
let a = ids(&run(&mut s, "ArcCenterTwoPoint", json!({"center": [30, 0], "start": format!("{l}.end"), "end": [30, -10]}))["curves"])[0].clone();
|
||||
let l = ids(&run(&mut s, "sketch.line", json!({"points": [[0, 0], [20, 0]]}))["curves"])[0].clone();
|
||||
let a =
|
||||
ids(&run(&mut s, "sketch.arc.center_point", json!({"center": [30, 0], "start": format!("{l}.end"), "end": [30, -10]}))["curves"])[0].clone();
|
||||
let _ = a;
|
||||
let f = run(&mut s, "FilletSketchCmd", json!({"point": format!("{l}.end"), "radius": 2}));
|
||||
let f = run(&mut s, "sketch.fillet", json!({"point": format!("{l}.end"), "radius": 2}));
|
||||
let arc = ids(&f["curves"])[0].clone();
|
||||
let sk = sketch(&s);
|
||||
let ai = sk.curve_index(&arc).unwrap();
|
||||
@@ -444,8 +447,8 @@ fn fillet_a_line_and_an_arc() {
|
||||
#[test]
|
||||
fn offset_free_form_curves() {
|
||||
let mut s = new_sketch();
|
||||
let sp = ids(&run(&mut s, "DrawSpline", json!({"points": [[0, 0], [10, 8], [20, 0], [30, 6]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "Offset", json!({"curves": [sp], "distance": 2, "side": [10, 20]}));
|
||||
let sp = ids(&run(&mut s, "sketch.spline.fit_point", json!({"points": [[0, 0], [10, 8], [20, 0], [30, 6]]}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "sketch.offset", json!({"curves": [sp], "distance": 2, "side": [10, 20]}));
|
||||
let made = ids(&r["curves"]);
|
||||
assert_eq!(made.len(), 1, "{r}");
|
||||
let sk = sketch(&s);
|
||||
@@ -464,7 +467,7 @@ fn offset_free_form_curves() {
|
||||
assert!((dist(*q) - 2.0).abs() < 0.08, "{q:?} {}", dist(*q));
|
||||
}
|
||||
// An ellipse grows by the offset all round.
|
||||
let e = ids(&run(&mut s, "CircleElipse", json!({"center": [100, 0], "major": [110, 0], "minor_radius": 5}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "Offset", json!({"curves": [e], "distance": 1, "side": [100, 20]}));
|
||||
let e = ids(&run(&mut s, "sketch.ellipse", json!({"center": [100, 0], "major": [110, 0], "minor_radius": 5}))["curves"])[0].clone();
|
||||
let r = run(&mut s, "sketch.offset", json!({"curves": [e], "distance": 1, "side": [100, 20]}));
|
||||
assert_eq!(ids(&r["curves"]).len(), 1, "{r}");
|
||||
}
|
||||
Reference in new issue
Block a user