Fix #2120: a nearly level or upright segment still lines up with its other end; Curvature keeps to the extension too

This commit is contained in:
Marco Arnulfo
2026-10-16 14:58:08 +07:00
committed by bflatastic
parent 54a8d60dae
commit f88242f576
4 changed files with 44 additions and 4 deletions
+13
View File
@@ -954,6 +954,19 @@ mod tests {
assert_eq!(press(&mut t, &cx, 100.0, 0.0, false).len(), 1);
}
/// #2120: the dragged end of a slanted straight path keeps to its extension (Smart Guides).
#[test]
fn a_dragged_end_of_a_straight_path_keeps_to_its_extension() {
let (d, id) = doc_with(SubPath::polyline(&[pt(300.0, 400.0), pt(380.0, 340.0)], false));
let (s, p) = (selected(id, None), paint());
let cx = cx(&d, &s, &p);
let mut t = CurvatureTool::default();
t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 380.0, 340.0));
let a = t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 421.5, 312.0));
let moved = a.get(1).and_then(edit_op).map(|(op, v)| (op, v["x"].as_f64().unwrap(), v["y"].as_f64().unwrap()));
assert!(matches!(moved, Some(("move", x, y)) if (x - 420.0).abs() < 1e-9 && (y - 310.0).abs() < 1e-9), "{a:?}");
}
#[test]
fn alt_click_or_double_click_toggles_and_delete_removes() {
let (d, id) = doc_with(wave());
+29 -2
View File
@@ -514,14 +514,16 @@ impl Targets {
}
/// The segment extension nearest `p`, within `tol` of it (and `p` farther than that from the
/// end it starts at): that end, its direction and where `p` lands on it.
/// end it starts at): that end, its direction and where `p` lands on it. Where `p` is nearer
/// the horizontal or the vertical through that end there is none: lining up with it ("align")
/// is what straightens a segment that is nearly level or upright.
fn extension(&self, p: Point, tol: f64) -> Option<(Point, Vec2, Point)> {
self.extensions
.iter()
.filter(|(at, _)| at.distance(p) > tol)
.map(|&(at, d)| (at, d, at + d * (p - at).dot(d)))
.map(|(at, d, q)| (at, d, q, q.distance(p)))
.filter(|(.., dist)| *dist <= tol)
.filter(|(at, .., dist)| *dist <= tol && (p.x - at.x).abs().min((p.y - at.y).abs()) > *dist)
.min_by(|a, b| a.3.total_cmp(&b.3))
.map(|(at, d, q, _)| (at, d, q))
}
@@ -1464,6 +1466,31 @@ mod tests {
assert_eq!(t.snap_point(Point::new(421.5, 312.0), 4.0), (Point::new(421.5, 312.0), vec![]));
}
/// A segment nearly level (0.3°) is still straightened by dragging its end into line with
/// the other one; along its own direction, farther from level, it keeps to its extension.
#[test]
fn a_nearly_level_segment_still_lines_up_with_its_other_end() {
use vectorcraft_doc::{Appearance, Node};
let (mut d, _) = doc_with_rect();
let l = d.layers[0].id;
let id = d.alloc_id();
d.insert(
Some(l),
1,
Node::path(id, vectorcraft_geom::shapes::line(Point::new(100.0, 300.0), Point::new(300.0, 301.0)), Appearance::default_art()),
)
.unwrap();
let (mut s, p) = (Selection::default(), paint());
s.set([id]);
s.anchors.insert(id, [(0, 1)].into_iter().collect());
let t = Targets::for_anchor_drag(&d, &s).styled(&cx(&d, &s, &p));
let (q, ov) = t.snap_point(Point::new(300.0, 300.0), 4.0);
assert_eq!((q, labels(&ov)), (Point::new(300.0, 300.0), vec!["align"]));
let (q, ov) = t.snap_point(Point::new(400.0, 301.5), 4.0);
assert!((q - Point::new(400.0, 301.5)).hypot() < 1e-9, "{q:?}");
assert_eq!(lines(&ov), vec![(Point::new(100.0, 300.0), q)]);
}
fn labels(ov: &[Overlay]) -> Vec<&str> {
ov.iter().filter_map(|o| if let Overlay::Label { text, .. } = o { Some(text.as_str()) } else { None }).collect()
}
+1 -1
View File
@@ -72,7 +72,7 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
- **Done (#1253):** adding paint to an unpainted zero-width stroke restores the existing 1 pt default, including new-art defaults and Swap Fill/Stroke. The real Eyedropper inheritance sequence, targeted appearance items, preserved widths and Undo/Redo have regression coverage.
- **Missing:** Average and Join in one step, with a corner or smooth choice for the joined anchor. `path.average` and `path.join` are separate commands and Join has no such choice (coinciding anchors merge into one since #1533). [ui-parity.md](ui-parity.md) listed "Join with corner/smooth choice" as done until 2026-10-13. About 1–2 h (estimated), inside the estimate below.
- **Implemented (#1760):** Effect › Warp has a shared style selector with original previews, orientation buttons and percent sliders. Existing effects retain their stack slot and target. Focused checks passed and original captures were reviewed; optional Properties integration and remembered defaults remain outside this change.
- **Done (#2120):** Smart Guides line extension: dragging the end of a straight segment with Direct Selection, the point snaps to the segment's own direction at any angle and the guide runs along it from the other end. Inside the estimate below.
- **Done (#2120):** Smart Guides line extension: dragging the end of a straight segment with Direct Selection or Curvature, the point snaps to the segment's own direction at any angle and the guide runs along it from the other end. Inside the estimate below.
- **Missing (noted for reporters, 2026-10-14):** Tab in the Color Picker moves through its fields in order (#1692); aligning anchor points selected with
Direct Selection to a key anchor (from #1636: they align to their bounds; the current public Align page does
not say how the key anchor is chosen, so this waits for the owner). Inside the estimate below.
+1 -1
View File
@@ -92,7 +92,7 @@ tools, never checked side by side) isn't captured row by row.
| Anchor/Path labels ("anchor", "center", "path", "align") | done | |
| "intersect" label and snapping to path intersections | partial | #1658 (part of #1602): a point drawn with the Pen, the shape tools or the other drawing tools and an anchor dragged with Direct Selection snap to where paths and ruler guides cross; a moved selection's own anchors and the Width tool don't |
| Construction guides at preference angles while drawing | done | #506 |
| Line extension: the dragged end of a straight segment snaps to the segment's own direction at any angle, past its other end or short of it, with the guide along it | done | #2120; Direct Selection, one anchor dragged (with several, the move has no single direction); horizontal and vertical segments keep lining up with their other end ("align") |
| Line extension: the dragged end of a straight segment snaps to the segment's own direction at any angle, past its other end or short of it, with the guide along it | done | #2120; Direct Selection and Curvature, one anchor dragged (with several, the move has no single direction); nearer the horizontal or vertical through the other end, the point lines up with it ("align"), so a nearly level segment can still be straightened |
| Measurement labels while drawing, moving and resizing | partial | sizes and offsets; angle and distance readouts while drawing missing |
| Spacing guides (equal distances) | partial | for moves (#394); not for resizing or drawing |
| Snapping tolerance preference | done | |