Rounded Rectangle sketch tool (#891) (#948)

* Rounded Rectangle sketch tool (#891)

A new SKETCH > CREATE command, sketch.rectangle.rounded, in the Rectangle
family: two opposite corners and a corner radius make a 2-point rectangle
whose four corners are filleted alike, as one undo step. The radius is one
parameter (one radius dimension, the other arcs equal to it), so changing it
changes all four corners. While drawing, Width, Height and Radius boxes take
typed values; typed width and height dimension the overall size between
opposite sides. Without a typed radius the corners get a fifth of the shorter
side.

Fixes #891

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rounded Rectangle in the Simplified Chinese catalog

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-13 23:52:10 -04:00
1 parent 19bf854dc9
commit 97ff7578c2
15 files changed
+330 -9

No files matched your search

+42
View File
@@ -207,3 +207,45 @@ fn a_rho_outside_zero_to_one_is_refused_before_it_reaches_the_command() {
assert_eq!(tool.dims.iter().map(|d| d.value.as_str()).collect::<Vec<_>>(), ["5"], "the refused value stays in its box");
assert!(h.app.status.as_ref().is_some_and(|(_, _, error)| *error), "the refusal is reported");
}
/// Rounded Rectangle (#891): typed width, height and corner radius make one profile whose
/// size is the typed overall size; the radius stays its own parameter.
#[test]
fn rounded_rectangle_takes_typed_width_height_and_radius() {
let mut h = drawing("sketch.rectangle.rounded");
assert_eq!(h.app.tool.as_ref().unwrap().dims.iter().map(|d| d.label).collect::<Vec<_>>(), ["Width", "Height", "Radius"]);
type_value(&mut h, "30");
tab(&mut h, false);
type_value(&mut h, "16");
tab(&mut h, false);
assert_focus(&h, 2);
type_value(&mut h, "4");
let undo = h.app.session.undo.len();
h.step(&json!({"key": "Enter"})).unwrap();
let state = h.app.session.world_state();
let sketch = state.sketch(h.app.session.active_sketch.unwrap()).unwrap();
let kinds: Vec<_> = sketch.sketch.curves.iter().map(|c| matches!(c.kind, solidcraft_engine::sketch::CurveKind::Arc { .. })).collect();
assert_eq!(kinds.iter().filter(|arc| **arc).count(), 4, "four corner arcs");
assert_eq!(kinds.len(), 8);
assert_eq!(sketch.profiles.len(), 1);
let want = 30.0 * 16.0 - (4.0 - std::f64::consts::PI) * 16.0;
assert!((sketch.profiles[0].area - want).abs() < 1e-6, "area {} want {want}", sketch.profiles[0].area);
assert_eq!(h.app.session.undo.len(), undo + 1, "one undo step");
}
/// Without a typed radius the corners get a fifth of the shorter side.
#[test]
fn rounded_rectangle_clicks_use_a_default_radius() {
let mut h = Harness::new();
h.app.run("sketch.create", json!({"plane": "XY"})).unwrap();
h.step(&json!({"start": "sketch.rectangle.rounded"})).unwrap();
h.step(&json!({"click": {"sketch": [0, 0]}})).unwrap();
h.step(&json!({"click": {"sketch": [40, 20]}})).unwrap();
let state = h.app.session.world_state();
let sketch = state.sketch(h.app.session.active_sketch.unwrap()).unwrap();
assert_eq!(sketch.profiles.len(), 1, "{:?}", h.app.status);
let (w, hh) = (40.0, 20.0);
let r: f64 = 0.2 * 20.0;
let want = w * hh - (4.0 - std::f64::consts::PI) * r * r;
assert!((sketch.profiles[0].area - want).abs() < 0.05 * want, "area {} want about {want}", sketch.profiles[0].area);
}