Add lock angle modifier to the Pen tool (#1049)

* adds lock angle to pen tool

 The current [Shift] Break Handle behavior should become Alt instead of Shift
 The current [Ctrl] Snap 15° behavior should become Shift instead of Ctrl
 Implement the extend-in-this-direction feature called [Ctrl] Lock Angle that exists in the Line tool, but doesn't yet exist in the Pen tool

* avoid clone by passing exlusive ref to tool data field directly

* Rearrange hints

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Adam Jahn 2023-02-22 02:27:49 -05:00 committed by Keavon Chambers
parent 92cb553961
commit 069a2351f2
2 changed files with 31 additions and 18 deletions

View File

@ -171,7 +171,7 @@ pub fn default_mapping() -> Mapping {
entry!(DoubleClick; action_dispatch=PathToolMessage::InsertPoint),
//
// PenToolMessage
entry!(PointerMove; refresh_keys=[Shift, Control], action_dispatch=PenToolMessage::PointerMove { snap_angle: Control, break_handle: Shift }),
entry!(PointerMove; refresh_keys=[Shift, Control], action_dispatch=PenToolMessage::PointerMove { snap_angle: Shift, break_handle: Alt, lock_angle: Control}),
entry!(KeyDown(Lmb); action_dispatch=PenToolMessage::DragStart),
entry!(KeyUp(Lmb); action_dispatch=PenToolMessage::DragStop),
entry!(KeyDown(Rmb); action_dispatch=PenToolMessage::Confirm),

View File

@ -54,6 +54,7 @@ pub enum PenToolMessage {
PointerMove {
snap_angle: Key,
break_handle: Key,
lock_angle: Key,
},
Undo,
UpdateOptions(PenOptionsUpdate),
@ -148,6 +149,7 @@ struct PenToolData {
should_mirror: bool,
// Indicates that curve extension is occurring from the first point, rather than (more commonly) the last point
from_start: bool,
angle: f64,
}
impl Fsm for PenToolFsmState {
@ -399,7 +401,7 @@ impl Fsm for PenToolFsmState {
tool_data.should_mirror = true;
process().unwrap_or(PenToolFsmState::PlacingAnchor)
}
(PenToolFsmState::DraggingHandle, PenToolMessage::PointerMove { snap_angle, break_handle }) => {
(PenToolFsmState::DraggingHandle, PenToolMessage::PointerMove { snap_angle, break_handle, lock_angle }) => {
let mut process = || {
// Get subpath
let layer_path = tool_data.path.as_ref()?;
@ -418,7 +420,10 @@ impl Fsm for PenToolFsmState {
let mouse = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
let pos = transform.inverse().transform_point2(mouse);
let pos = compute_snapped_angle(input, snap_angle, pos, last_anchor.position);
let snap_angle = input.keyboard.get(snap_angle as usize);
let lock_angle = input.keyboard.get(lock_angle as usize);
let pos = compute_snapped_angle(&mut tool_data.angle, lock_angle, snap_angle, pos, last_anchor.position);
// Update points on current segment (to show preview of new handle)
let msg = Operation::MoveManipulatorPoint {
@ -460,10 +465,11 @@ impl Fsm for PenToolFsmState {
self
}
}
(PenToolFsmState::PlacingAnchor, PenToolMessage::PointerMove { snap_angle, .. }) => {
(PenToolFsmState::PlacingAnchor, PenToolMessage::PointerMove { snap_angle, lock_angle, .. }) => {
let mut process = || {
// Get subpath
let layer_path = tool_data.path.as_ref()?;
let data = tool_data.clone();
let layer_path = data.path.as_ref()?;
let subpath = document.document_legacy.layer(layer_path).ok().and_then(|layer| layer.as_subpath())?;
// Get the last manipulator group and the one previous to that
@ -491,7 +497,9 @@ impl Fsm for PenToolFsmState {
if mouse.distance_squared(transform.transform_point2(relative.position)) < crate::consts::SNAP_POINT_TOLERANCE.powi(2) {
pos = relative.position;
} else {
pos = compute_snapped_angle(input, snap_angle, pos, relative.position);
let snap_angle = input.keyboard.get(snap_angle as usize);
let lock_angle = input.keyboard.get(lock_angle as usize);
pos = compute_snapped_angle(&mut tool_data.angle, lock_angle, snap_angle, pos, relative.position);
}
}
@ -592,10 +600,9 @@ impl Fsm for PenToolFsmState {
let hint_data = match self {
PenToolFsmState::Ready => HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Draw Path")])]),
PenToolFsmState::DraggingHandle | PenToolFsmState::PlacingAnchor => HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Add Handle")]),
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Add Anchor")]),
HintGroup(vec![HintInfo::keys([Key::Control], "Snap 15°")]),
HintGroup(vec![HintInfo::keys([Key::Shift], "Break Handle")]),
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Add Anchor"), HintInfo::mouse(MouseMotion::LmbDrag, "Add Handle")]),
HintGroup(vec![HintInfo::keys([Key::Shift], "Snap 15°"), HintInfo::keys([Key::Control], "Lock Angle")]),
HintGroup(vec![HintInfo::keys([Key::Alt], "Break Handle")]), // TODO: Show this only when dragging a handle
HintGroup(vec![HintInfo::keys([Key::Enter], "End Path")]),
]),
};
@ -608,22 +615,28 @@ impl Fsm for PenToolFsmState {
}
}
// TODO: Expand `pos` name below to the full word (position?)
/// Snap the angle of the line from relative to pos if the key is pressed.
fn compute_snapped_angle(input: &InputPreprocessorMessageHandler, key: Key, pos: DVec2, relative: DVec2) -> DVec2 {
if input.keyboard.get(key as usize) {
let delta = relative - pos;
/// Snap the angle of the line from relative to position if the key is pressed.
fn compute_snapped_angle(cached_angle: &mut f64, lock_angle: bool, snap_angle: bool, position: DVec2, relative: DVec2) -> DVec2 {
let delta = relative - position;
let mut angle = -delta.angle_between(DVec2::X);
let length = delta.length();
let mut angle = -delta.angle_between(DVec2::X);
if lock_angle {
angle = *cached_angle;
}
if snap_angle {
let snap_resolution = LINE_ROTATE_SNAP_ANGLE.to_radians();
angle = (angle / snap_resolution).round() * snap_resolution;
}
*cached_angle = angle;
if snap_angle || lock_angle {
let length = delta.length();
let rotated = DVec2::new(length * angle.cos(), length * angle.sin());
relative - rotated
} else {
pos
position
}
}