G1: Star tool shows inner and outer radii in measurement labels

## Summary
While dragging with the Star tool, the measurement label lists the outer
radius (R1) and inner radius (R2) from the centre to the pointer, matching
the radii used for the live preview (including Ctrl/Cmd holding the inner
radius and Alt straightening the points).

## Tests
- `star_drag_measure_label_shows_outer_and_inner_radii` in
  `crates/tools/src/shape.rs`.

## Sources
- https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-live-shapes/change-inner-and-outer-radius-of-stars.html — R1 is the outer radius and R2 the inner radius of a star.
- https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html — while dragging a star, Command/Ctrl keeps the inner radius constant as the outer radius follows the pointer.

Part of gap G1

Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-10-26 09:09:44 +07:00
committed by bflatastic
co-authored by nrq6993-a11y
parent 142942e6c4
commit f3205e7344
2 changed files with 43 additions and 8 deletions
+4
View File
@@ -392,6 +392,10 @@ impl ToolContext<'_> {
pub fn size_label(&self, w: f64, h: f64) -> String {
format!("W: {}\nH: {}", self.len(w), self.len(h))
}
/// Star tool drag: outer (R1) and inner (R2) radii from the centre to the pointer.
pub fn star_radii_label(&self, outer: f64, inner: f64) -> String {
format!("R1: {}\nR2: {}", self.len(outer), self.len(inner))
}
/// A move label: `dX: …` over `dY: …`.
pub fn offset_label(&self, dx: f64, dy: f64) -> String {
format!("dX: {}\ndY: {}", self.len(dx), self.len(dy))
+39 -8
View File
@@ -308,15 +308,24 @@ impl Tool for ShapeTool {
cx.segment_measure_label(s, self.last)
} else {
// A box's size as drawn (from its centre with Alt, square with Shift).
let (w, h) = match self.id {
"polygon" | "star" => ((self.last - s).x.abs(), (self.last - s).y.abs()),
_ => {
// Along the Constrain Angle's axes, as the box is drawn (`command_at`).
let (r, _) = turned_box(s, self.last, self.mods, cx.constrain_angle);
(r.width(), r.height())
match self.id {
"star" => {
let outer = (self.last - s).hypot().max(0.01);
let ratio = if self.mods.alt { straight_ratio(self.points).unwrap_or(self.star_ratio) } else { self.star_ratio };
let inner = self.inner.unwrap_or(outer * ratio);
cx.star_radii_label(outer, inner)
}
};
cx.size_label(w, h)
_ => {
let (w, h) = match self.id {
"polygon" => ((self.last - s).x.abs(), (self.last - s).y.abs()),
_ => {
let (r, _) = turned_box(s, self.last, self.mods, cx.constrain_angle);
(r.width(), r.height())
}
};
cx.size_label(w, h)
}
}
};
o.push(Overlay::Measure { p, text });
}
@@ -396,6 +405,28 @@ mod tests {
assert!(matches!(a.last(), Some(Action::Preview(_, v)) if v["cx"] == 60.0 && v["cy"] == 60.0), "{a:?}");
}
#[test]
fn star_drag_measure_label_shows_outer_and_inner_radii() {
let (d, _) = doc_with_rect();
let s = Selection::default();
let p = paint();
let cx = ToolContext { measurement_labels: true, smart_guides: false, ..cx(&d, &s, &p) };
let mut t = ShapeTool::new("star");
t.star_ratio = 0.5;
t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 10.0, 10.0));
t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 50.0, 10.0));
let want = cx.star_radii_label(40.0, 20.0);
let label = t
.overlays(&cx)
.into_iter()
.find_map(|o| match o {
Overlay::Measure { text, .. } => Some(text),
_ => None,
})
.unwrap_or_default();
assert_eq!(label, want, "{label}");
}
/// During a star drag, Ctrl/Cmd keeps the inner radius while the outer one follows the pointer
/// (the new ratio stays once the key is let go), and Alt keeps the shoulders straight (#812).
#[test]