Knife: Alt cuts in a straight line and Shift+Alt cuts at 45° steps

The Knife cut along the freehand drag whatever keys were held. The
reference app cuts in a straight line with Alt-drag, and it cuts at 45
or 90 degrees with Shift+Alt-drag.

With Alt held at the press, or pressed before the drag has moved, the
Knife's stroke is the press point and the pointer: the dashed line
shows it while dragging, and the release sends path.knife with those
two points. With Shift held too, the line keeps to 45° steps from the
Constrain Angle, as the Pencil's Shift line does. Shift is read on each
drag event and at the release. The cut stays straight when Alt is let
go, a line taken back to its press point sends no command, and Shift
alone leaves the cut freehand.

Docs: docs/target-app-parity.md.
This commit is contained in:
dotCooCoo
2026-10-30 23:51:51 +07:00
committed by bflatastic
parent aea9296fa2
commit cd9eb0acd5
4 changed files with 144 additions and 6 deletions
+21
View File
@@ -1011,6 +1011,27 @@ fn knife_cuts_rect_into_two_pieces() {
assert_eq!(paths(&s).len(), 1);
}
/// An Alt-drag with the Knife cuts along the straight line from the press point to the release,
/// wherever the drag went between, and Shift+Alt along the nearest 45° step, in one undo step.
#[test]
fn knife_alt_drag_cuts_along_a_straight_line() {
let alt = Mods { alt: true, ..Default::default() };
let shift_alt = Mods { shift: true, alt: true, ..Default::default() };
// From (150, 50) to (250, 250) the line crosses the rectangle's top at x 175 and its bottom at
// x 225, so the left piece holds 10000 pt²; a vertical cut at x 150 leaves 5000 pt².
for (m, to, left) in [(alt, (250.0, 250.0), 10000.0), (shift_alt, (160.0, 240.0), 5000.0)] {
let mut s = session();
rect(&mut s, 100.0, 100.0, 200.0, 100.0);
s.execute("select.none", &json!({})).unwrap();
s.select_tool("knife", view()).unwrap();
assert_eq!(gesture(&mut s, &[(150.0, 50.0), (290.0, 150.0), to], m), 1, "{m:?}: one undo step");
let ps = paths(&s);
assert_eq!(ps.len(), 2, "{m:?}");
let small = ps.iter().map(|(_, p)| area(p)).fold(f64::INFINITY, f64::min);
assert!((small - left).abs() < 30.0, "{m:?}: {small}");
}
}
#[test]
fn knife_partial_cut_does_nothing() {
let mut s = session();