Dimension tool: a click near a line snaps the measured point onto the line (#2432)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
vangel76
2026-10-17 13:20:26 +07:00
committed by bflatastic
co-authored by Claude Opus 5.5
parent 6939a481a4
commit 848e7a296a
2 changed files with 36 additions and 5 deletions
+14
View File
@@ -407,3 +407,17 @@ fn shift_shows_the_constrained_line_while_picking_the_second_point() {
assert_eq!(end(&mut s, false).y, 310.0);
assert!((end(&mut s, true).y - 300.0).abs() < 1e-9);
}
/// A click near a line, away from its anchors, snaps to the closest point of the line (#2432).
#[test]
fn clicking_near_a_line_snaps_the_measured_point_onto_it() {
let mut s = session();
edge(&mut s);
s.select_tool("dimension", ViewInfo::default()).unwrap();
for (x, y) in [(130.0, 103.0), (150.0, 97.0), (140.0, 130.0)] {
ev(&mut s, PointerKind::Down, x, y);
ev(&mut s, PointerKind::Up, x, y);
}
let id = s.doc().unwrap().selection.objects[0].0;
assert_eq!(text(&s, id), "20.00 pt");
}
+22 -5
View File
@@ -119,6 +119,20 @@ fn anchor_near(cx: &ToolContext, edge: &SegmentRef, p: Point) -> Option<(usize,
.min_by(|(_, a), (_, b)| a.distance(p).total_cmp(&b.distance(p)))
}
/// The point of `edge` closest to `p`.
fn nearest_on(cx: &ToolContext, edge: &SegmentRef, p: Point) -> Option<Point> {
use vectorcraft_geom::kurbo::ParamCurveNearest;
let sp = cx.doc.node(edge.node)?.path_data()?.subpaths.get(edge.subpath)?;
if edge.segment >= sp.segment_count() {
return None;
}
let c = sp.segment(edge.segment);
let t = c.nearest(p, 1e-4).t;
use vectorcraft_geom::kurbo::ParamCurve;
let q = c.eval(t);
(q.x.is_finite() && q.y.is_finite()).then_some(q)
}
impl DimensionTool {
fn target(&self, cx: &ToolContext, p: Point) -> Option<Target> {
let edge = pick(cx, p)?;
@@ -216,11 +230,14 @@ impl DimensionTool {
Kind::Linear => {
let (mut at, _) = crate::guides::snap_pick(cx, p);
let mut reference = None;
if let Some(edge) = pick(cx, p)
&& let Some((anchor, point)) = anchor_near(cx, &edge, p)
{
at = point;
reference = Some(PointRef { node: edge.node, subpath: edge.subpath, anchor });
if let Some(edge) = pick(cx, p) {
if let Some((anchor, point)) = anchor_near(cx, &edge, p) {
at = point;
reference = Some(PointRef { node: edge.node, subpath: edge.subpath, anchor });
} else if let Some(point) = nearest_on(cx, &edge, p) {
// Not on an anchor: snap to the closest point of the line (#2432).
at = point;
}
}
if mods.shift
&& let Some((last, _)) = self.points.last()