M2.6: Flare tool ↑/↓ add or remove rays while drawing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-01 22:21:58 -04:00
parent 09fc2eedd9
commit 8434ef4404
2 changed files with 29 additions and 7 deletions

View File

@ -1,7 +1,7 @@
use serde_json::json;
use super::*;
use vectorcraft_tools::{PointerEvent, PointerKind};
use vectorcraft_tools::{PointerEvent, PointerKind, ToolKey};
fn session() -> Session {
let mut s = Session::new();
@ -319,6 +319,9 @@ fn flare_tool_two_step_gesture_is_one_undo() {
s.select_tool("flare", v).unwrap();
s.pointer(&PointerEvent::new(PointerKind::Down, 200.0, 200.0), v).unwrap();
s.pointer(&PointerEvent::new(PointerKind::Drag, 240.0, 200.0), v).unwrap();
// ↓ twice: 13 rays (one compound path with a subpath per ray).
s.tool_key(ToolKey::Down, Default::default(), v).unwrap();
s.tool_key(ToolKey::Down, Default::default(), v).unwrap();
s.pointer(&PointerEvent::new(PointerKind::Up, 240.0, 200.0), v).unwrap();
s.pointer(&PointerEvent::new(PointerKind::Move, 500.0, 400.0), v).unwrap();
s.pointer(&PointerEvent::new(PointerKind::Down, 500.0, 400.0), v).unwrap();
@ -334,6 +337,8 @@ fn flare_tool_two_step_gesture_is_one_undo() {
assert!(b.x1 > 400.0 && b.y1 > 300.0, "{b:?}");
let centre = children.iter().find(|c| c.name.as_deref() == Some("Center")).unwrap().geometric_bounds().unwrap();
assert!((centre.width() - 80.0).abs() < 0.5);
let rays = children.iter().find(|c| c.name.as_deref() == Some("Rays")).unwrap();
assert_eq!(rays.path_data().unwrap().subpaths.len(), 13);
}
#[test]

View File

@ -27,11 +27,16 @@ pub struct FlareTool {
placed: Option<(Point, f64)>,
hover: Point,
began: bool,
/// Ray count changed with ↑/↓ during the gesture (None = the command's default, 15).
rays: Option<u64>,
}
impl FlareTool {
fn params(c: Point, diameter: f64, end: Option<Point>) -> Value {
fn params(&self, c: Point, diameter: f64, end: Option<Point>) -> Value {
let mut v = json!({ "cx": c.x, "cy": c.y, "diameter": diameter.max(1.0) });
if let Some(r) = self.rays {
v["rays"] = json!(r);
}
if let Some(e) = end {
v["x2"] = json!(e.x);
v["y2"] = json!(e.y);
@ -63,21 +68,21 @@ impl Tool for FlareTool {
self.began = true;
return vec![
Action::Begin("Flare".into()),
Action::Preview("shape.flare".into(), Self::params(c, 2.0 * ev.pos.distance(c), None)),
Action::Preview("shape.flare".into(), self.params(c, 2.0 * ev.pos.distance(c), None)),
];
}
if self.began {
return vec![Action::Preview("shape.flare".into(), Self::params(c, 2.0 * ev.pos.distance(c), None))];
return vec![Action::Preview("shape.flare".into(), self.params(c, 2.0 * ev.pos.distance(c), None))];
}
} else if let Some((c, d)) = self.placed {
return vec![Action::Preview("shape.flare".into(), Self::params(c, d, Some(ev.pos)))];
return vec![Action::Preview("shape.flare".into(), self.params(c, d, Some(ev.pos)))];
}
vec![]
}
PointerKind::Up => {
if let Some((c, d)) = self.placed.take() {
self.began = false;
return vec![Action::Preview("shape.flare".into(), Self::params(c, d, Some(ev.pos))), Action::Commit];
return vec![Action::Preview("shape.flare".into(), self.params(c, d, Some(ev.pos))), Action::Commit];
}
let Some((c, p)) = self.drag.take() else { return vec![] };
if self.began {
@ -90,7 +95,7 @@ impl Tool for FlareTool {
}
PointerKind::Move => {
if let Some((c, d)) = self.placed {
return vec![Action::Preview("shape.flare".into(), Self::params(c, d, Some(ev.pos)))];
return vec![Action::Preview("shape.flare".into(), self.params(c, d, Some(ev.pos)))];
}
vec![]
}
@ -98,7 +103,19 @@ impl Tool for FlareTool {
}
}
fn key(&mut self, _cx: &ToolContext, key: ToolKey, _mods: Mods) -> Vec<Action> {
// ↑/↓ while drawing add/remove rays.
if matches!(key, ToolKey::Up | ToolKey::Down) && self.began {
let r = self.rays.unwrap_or(15);
self.rays = Some(if key == ToolKey::Up { (r + 1).min(50) } else { r.saturating_sub(1) });
let preview = match (self.drag, self.placed) {
(Some((c, p)), _) => self.params(c, 2.0 * p.distance(c), None),
(_, Some((c, d))) => self.params(c, d, Some(self.hover)),
_ => return vec![],
};
return vec![Action::Preview("shape.flare".into(), preview)];
}
if key == ToolKey::Escape && self.busy() {
self.rays = None;
self.drag = None;
self.placed = None;
let began = std::mem::take(&mut self.began);