mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-12 15:52:52 +00:00
Merge pull request #6718 from dotCooCoo/fix/issue-6634
Fix #6634: Pen closing drag no longer gives the first anchor symmetric handles
This commit is contained in:
@@ -3146,7 +3146,7 @@ fn shift_reshapes_the_closing_segment_of_a_rotated_square_and_undoes() {
|
||||
fn pen_drag_from_the_first_anchor_closes_the_path() {
|
||||
// #501: closing a path by dragging out of its first anchor flickered between a close and a new
|
||||
// path, and released at the wrong moment left the shape open. The press decides: the drag
|
||||
// shapes the closing curve, symmetric, and the release closes the path wherever it ends.
|
||||
// shapes the closing curve, and the release closes the path wherever it ends.
|
||||
let none = Mods::default();
|
||||
let v = view();
|
||||
let mut s = session();
|
||||
@@ -3167,13 +3167,119 @@ fn pen_drag_from_the_first_anchor_closes_the_path() {
|
||||
let sp = &path(&s, id).subpaths[0];
|
||||
assert!(sp.closed && sp.anchors.len() == 3, "{sp:?}");
|
||||
let a = sp.anchors[0];
|
||||
assert_eq!((a.h_in, a.h_out), (Point::new(40.0, 348.0), Point::new(160.0, 252.0)), "symmetric");
|
||||
assert_eq!((a.h_in, a.h_out), (Point::new(40.0, 348.0), Point::new(100.0, 300.0)), "only the closing segment curves");
|
||||
assert_eq!(paths(&s).len(), 1);
|
||||
// The next click starts a new path.
|
||||
gesture(&mut s, &[(500.0, 500.0)], none);
|
||||
assert_eq!(paths(&s).len(), 2);
|
||||
}
|
||||
|
||||
/// #6634: closing a path by dragging on its first anchor does not give that anchor symmetric
|
||||
/// handles. Its incoming handle mirrors the pointer; its outgoing handle stays where it is on a
|
||||
/// corner, so the first segment keeps its shape, and on a smooth anchor turns to stay in line at
|
||||
/// its own length. Alt keeps the outgoing handle where it was when Alt went down, Shift keeps the
|
||||
/// drag to 45° steps, and a click leaves the anchor a corner with the closing segment straight
|
||||
/// into it. Each close is one undo step.
|
||||
#[test]
|
||||
fn pen_closing_drag_does_not_make_the_first_anchor_symmetric() {
|
||||
use vectorcraft_geom::AnchorKind;
|
||||
let (none, alt, shift) = (Mods::default(), Mods { alt: true, ..Mods::default() }, Mods { shift: true, ..Mods::default() });
|
||||
let v = view();
|
||||
// Draws a path from (100, 300), dragged out to `start` when given (a smooth anchor), through
|
||||
// (200, 200) and (300, 300), and returns it with its first anchor as drawn.
|
||||
let draw = |s: &mut Session, start: Option<(f64, f64)>| {
|
||||
s.select_tool("pen", v).unwrap();
|
||||
let first = match start {
|
||||
Some(out) => vec![(100.0, 300.0), out],
|
||||
None => vec![(100.0, 300.0)],
|
||||
};
|
||||
gesture(s, &first, none);
|
||||
gesture(s, &[(200.0, 200.0)], none);
|
||||
gesture(s, &[(300.0, 300.0)], none);
|
||||
let id = s.doc().unwrap().selection.objects[0];
|
||||
let anchor = path(s, id).subpaths[0].anchors[0];
|
||||
(id, anchor)
|
||||
};
|
||||
let first = |s: &Session, id: NodeId| {
|
||||
let sp = &path(s, id).subpaths[0];
|
||||
assert!(sp.closed, "{sp:?}");
|
||||
sp.anchors[0]
|
||||
};
|
||||
|
||||
// A corner: the first segment stays straight however the drag turns.
|
||||
let mut s = session();
|
||||
let (id, before) = draw(&mut s, None);
|
||||
assert_eq!(gesture(&mut s, &[(100.0, 300.0), (80.0, 250.0), (60.0, 200.0)], none), 1, "one undo step");
|
||||
let a = first(&s, id);
|
||||
assert_eq!((a.h_in, a.h_out, a.kind), (Point::new(140.0, 400.0), before.h_out, AnchorKind::Corner), "{a:?}");
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert!(!path(&s, id).subpaths[0].closed, "the undo opens the path again");
|
||||
|
||||
// A smooth anchor: the outgoing handle turns opposite the incoming one, keeping its length 50.
|
||||
let mut s = session();
|
||||
let (id, before) = draw(&mut s, Some((150.0, 300.0)));
|
||||
assert_eq!((before.h_out, before.kind), (Point::new(150.0, 300.0), AnchorKind::Smooth));
|
||||
gesture(&mut s, &[(100.0, 300.0), (100.0, 250.0), (100.0, 200.0)], none);
|
||||
let a = first(&s, id);
|
||||
assert_eq!((a.h_in, a.kind), (Point::new(100.0, 400.0), AnchorKind::Smooth), "{a:?}");
|
||||
assert!(near(a.h_out, Point::new(100.0, 250.0)), "in line, at its own length: {a:?}");
|
||||
|
||||
// Alt held from the press: a corner, its outgoing handle where it was.
|
||||
let mut s = session();
|
||||
let (id, before) = draw(&mut s, Some((150.0, 300.0)));
|
||||
gesture(&mut s, &[(100.0, 300.0), (100.0, 250.0), (100.0, 200.0)], alt);
|
||||
let a = first(&s, id);
|
||||
assert_eq!((a.h_in, a.h_out, a.kind), (Point::new(100.0, 400.0), before.h_out, AnchorKind::Corner), "{a:?}");
|
||||
|
||||
// Alt pressed mid-drag: the outgoing handle stays where the drag had turned it.
|
||||
let mut s = session();
|
||||
let (id, _) = draw(&mut s, Some((150.0, 300.0)));
|
||||
s.pointer(&PointerEvent::new(PointerKind::Down, 100.0, 300.0), v).unwrap();
|
||||
s.pointer(&PointerEvent::new(PointerKind::Drag, 100.0, 200.0), v).unwrap();
|
||||
s.pointer(&PointerEvent::new(PointerKind::Drag, 130.0, 200.0).with_mods(alt), v).unwrap();
|
||||
s.pointer(&PointerEvent::new(PointerKind::Up, 130.0, 200.0).with_mods(alt), v).unwrap();
|
||||
let a = first(&s, id);
|
||||
assert_eq!((a.h_in, a.kind), (Point::new(70.0, 400.0), AnchorKind::Corner), "{a:?}");
|
||||
assert!(near(a.h_out, Point::new(100.0, 250.0)), "{a:?}");
|
||||
|
||||
// Shift: the drag keeps to 45° steps (straight up here), at the pointer's distance from the
|
||||
// anchor, mirrored into the incoming handle.
|
||||
let mut s = session();
|
||||
let (id, before) = draw(&mut s, None);
|
||||
gesture(&mut s, &[(100.0, 300.0), (95.0, 250.0), (90.0, 200.0)], shift);
|
||||
let a = first(&s, id);
|
||||
assert!(near(a.h_in, Point::new(100.0, 300.0 + 10f64.hypot(100.0))) && a.h_out == before.h_out, "{a:?}");
|
||||
|
||||
// A click: the closing segment comes straight in, and a smooth anchor keeps its outgoing
|
||||
// handle and becomes a corner.
|
||||
let mut s = session();
|
||||
let (id, before) = draw(&mut s, Some((150.0, 300.0)));
|
||||
assert_eq!(gesture(&mut s, &[(100.0, 300.0)], none), 1);
|
||||
let a = first(&s, id);
|
||||
assert_eq!((a.h_in, a.h_out, a.kind), (Point::new(100.0, 300.0), before.h_out, AnchorKind::Corner), "{a:?}");
|
||||
}
|
||||
|
||||
/// Close Path's `in` moves the first anchor's incoming handle as Move Direction Handle does, and
|
||||
/// `out` places its outgoing handle, making it a corner; `out` that is not [x, y] is refused.
|
||||
#[test]
|
||||
fn close_path_places_the_first_anchors_handles() {
|
||||
use vectorcraft_geom::AnchorKind;
|
||||
let mut s = session();
|
||||
let id = curve(&mut s);
|
||||
let a = path(&s, id).subpaths[0].anchors[0];
|
||||
assert!(a.has_out() && a.kind == AnchorKind::Corner, "{a:?}");
|
||||
for h in [json!("1, 2"), json!([1]), json!([1, "2"])] {
|
||||
refuses_with(&mut s, "path.close", &json!({"id": id.0, "out": h}), "`out` must be [x, y]");
|
||||
}
|
||||
s.execute("path.close", &json!({"id": id.0, "in": [60.0, 140.0]})).unwrap();
|
||||
let b = path(&s, id).subpaths[0].anchors[0];
|
||||
assert_eq!((b.h_in, b.h_out, b.kind), (Point::new(60.0, 140.0), a.h_out, AnchorKind::Corner), "a corner keeps its outgoing handle");
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
s.execute("path.close", &json!({"id": id.0, "in": [60.0, 140.0], "out": [120.0, 60.0]})).unwrap();
|
||||
let b = path(&s, id).subpaths[0].anchors[0];
|
||||
assert_eq!((b.h_in, b.h_out, b.kind), (Point::new(60.0, 140.0), Point::new(120.0, 60.0), AnchorKind::Corner));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pen_with_alt_converts_the_anchors_of_the_path_being_drawn() {
|
||||
// #504: while drawing, Alt-click on a smooth anchor of the path makes it a corner and Alt-drag
|
||||
|
||||
Reference in New Issue
Block a user