mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 22:28:11 +08:00
Enable free movement of transform cage edge during skew when Ctrl is held (#2358)
* Enable free movement of edge during skew when ctrl is held Fixes #2299 Follow-up to #2300 * Simplify code * Change test along the lines of new behaviour * Make the hint Title Case --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
6becaef980
commit
e9ecdc7e01
@@ -254,40 +254,36 @@ impl SelectedEdges {
|
|||||||
(DAffine2::from_scale(enlargement_factor), pivot)
|
(DAffine2::from_scale(enlargement_factor), pivot)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add free movement when Ctrl is pressed to allow dragging the whole edge, not just sliding it
|
pub fn skew_transform(&self, mouse: DVec2, to_viewport_transform: DAffine2, free_movement: bool) -> DAffine2 {
|
||||||
pub fn skew_transform(&self, mouse: DVec2, to_viewport_transform: DAffine2, _free_movement: bool) -> DAffine2 {
|
// Skip if the matrix is singular
|
||||||
// Skip if the matrix is singular (as it isn't really possible to skew).
|
|
||||||
if !to_viewport_transform.matrix2.determinant().recip().is_finite() {
|
if !to_viewport_transform.matrix2.determinant().recip().is_finite() {
|
||||||
return DAffine2::IDENTITY;
|
return DAffine2::IDENTITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
let opposite = self.pivot_from_bounds(self.bounds[0], self.bounds[1]);
|
let opposite = self.pivot_from_bounds(self.bounds[0], self.bounds[1]);
|
||||||
// This is the current handle that goes under the mouse.
|
|
||||||
let dragging_point = self.pivot_from_bounds(self.bounds[1], self.bounds[0]);
|
let dragging_point = self.pivot_from_bounds(self.bounds[1], self.bounds[0]);
|
||||||
|
|
||||||
let mut new_dragging_point = to_viewport_transform.transform_point2(dragging_point);
|
let viewport_dragging_point = to_viewport_transform.transform_point2(dragging_point);
|
||||||
let parallel_to_x = self.top || self.bottom;
|
let parallel_to_x = self.top || self.bottom;
|
||||||
let parallel_to_y = !parallel_to_x && (self.left || self.right);
|
let parallel_to_y = !parallel_to_x && (self.left || self.right);
|
||||||
|
|
||||||
// The target point is the projection in viewport space onto the line that the skew is parallel to.
|
let drag_vector = mouse - viewport_dragging_point;
|
||||||
if parallel_to_x {
|
let document_drag_vector = to_viewport_transform.inverse().transform_vector2(drag_vector);
|
||||||
new_dragging_point += (mouse - new_dragging_point).project_onto(to_viewport_transform.transform_vector2(DVec2::X));
|
|
||||||
} else if parallel_to_y {
|
|
||||||
new_dragging_point += (mouse - new_dragging_point).project_onto(to_viewport_transform.transform_vector2(DVec2::Y));
|
|
||||||
}
|
|
||||||
new_dragging_point = to_viewport_transform.inverse().transform_point2(new_dragging_point);
|
|
||||||
|
|
||||||
let movement = new_dragging_point - dragging_point;
|
let bounds = (self.bounds[1] - self.bounds[0]).abs();
|
||||||
|
let sign = if self.top || self.left { -1. } else { 1. };
|
||||||
|
let signed_bounds = sign * bounds;
|
||||||
|
|
||||||
|
let scale_factor = if parallel_to_x { signed_bounds.y.recip() } else { signed_bounds.x.recip() };
|
||||||
|
let scaled_document_drag = document_drag_vector * scale_factor;
|
||||||
|
|
||||||
// Produce a skew that moves the dragging point to the new dragging point (assuming the opposite is origin).
|
|
||||||
let skew = DAffine2::from_mat2(DMat2::from_cols_array(&[
|
let skew = DAffine2::from_mat2(DMat2::from_cols_array(&[
|
||||||
1.,
|
1. + if parallel_to_y && free_movement { scaled_document_drag.x } else { 0. },
|
||||||
if parallel_to_y { movement.y / (dragging_point - opposite).x } else { 0. },
|
if parallel_to_y { scaled_document_drag.y } else { 0. },
|
||||||
if parallel_to_x { movement.x / (dragging_point - opposite).y } else { 0. },
|
if parallel_to_x { scaled_document_drag.x } else { 0. },
|
||||||
1.,
|
1. + if parallel_to_x && free_movement { scaled_document_drag.y } else { 0. },
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// Combine that with a transform that makes opposite the origin.
|
|
||||||
DAffine2::from_translation(opposite) * skew * DAffine2::from_translation(-opposite)
|
DAffine2::from_translation(opposite) * skew * DAffine2::from_translation(-opposite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -810,21 +806,29 @@ fn skew_transform_correct() {
|
|||||||
let final_transform = edge.skew_transform(mouse, to_viewport_transform, false);
|
let final_transform = edge.skew_transform(mouse, to_viewport_transform, false);
|
||||||
|
|
||||||
// This is the current handle that goes under the mouse.
|
// This is the current handle that goes under the mouse.
|
||||||
|
let opposite = edge.pivot_from_bounds(edge.bounds[0], edge.bounds[1]);
|
||||||
let dragging_point = edge.pivot_from_bounds(edge.bounds[1], edge.bounds[0]);
|
let dragging_point = edge.pivot_from_bounds(edge.bounds[1], edge.bounds[0]);
|
||||||
|
|
||||||
|
let viewport_dragging_point = to_viewport_transform.transform_point2(dragging_point);
|
||||||
let parallel_to_x = edge.top || edge.bottom;
|
let parallel_to_x = edge.top || edge.bottom;
|
||||||
let parallel_to_y = !parallel_to_x && (edge.left || edge.right);
|
let parallel_to_y = !parallel_to_x && (edge.left || edge.right);
|
||||||
|
|
||||||
// The target point is the projection in viewport space onto the line that the skew is parallel to.
|
let drag_vector = mouse - viewport_dragging_point;
|
||||||
let mut target_dragging_point = to_viewport_transform.transform_point2(dragging_point);
|
let document_drag_vector = to_viewport_transform.inverse().transform_vector2(drag_vector);
|
||||||
if parallel_to_x {
|
|
||||||
target_dragging_point += (mouse - target_dragging_point).project_onto(to_viewport_transform.transform_vector2(DVec2::X));
|
|
||||||
} else if parallel_to_y {
|
|
||||||
target_dragging_point += (mouse - target_dragging_point).project_onto(to_viewport_transform.transform_vector2(DVec2::Y));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the final point in viewport space.
|
let sign = if edge.top || edge.left { -1. } else { 1. };
|
||||||
let final_dragging_point = to_viewport_transform.transform_point2(final_transform.transform_point2(dragging_point));
|
let scale_factor = (edge.bounds[1] - edge.bounds[0])[parallel_to_x as usize].abs().recip() * sign;
|
||||||
assert_eq!(final_dragging_point, target_dragging_point);
|
let scaled_document_drag = document_drag_vector * scale_factor;
|
||||||
|
|
||||||
|
let skew = DAffine2::from_mat2(DMat2::from_cols_array(&[
|
||||||
|
1.,
|
||||||
|
if parallel_to_y { scaled_document_drag.y } else { 0. },
|
||||||
|
if parallel_to_x { scaled_document_drag.x } else { 0. },
|
||||||
|
1.,
|
||||||
|
]));
|
||||||
|
|
||||||
|
let constructed_transform = DAffine2::from_translation(opposite) * skew * DAffine2::from_translation(-opposite);
|
||||||
|
|
||||||
|
assert_eq!(constructed_transform, final_transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1573,7 +1573,14 @@ impl Fsm for SelectToolFsmState {
|
|||||||
]);
|
]);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||||
}
|
}
|
||||||
SelectToolFsmState::DraggingPivot | SelectToolFsmState::SkewingBounds { .. } => {
|
SelectToolFsmState::SkewingBounds { .. } => {
|
||||||
|
let hint_data = HintData(vec![
|
||||||
|
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
|
||||||
|
HintGroup(vec![HintInfo::keys([Key::Control], "Unlock Slide")]),
|
||||||
|
]);
|
||||||
|
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||||
|
}
|
||||||
|
SelectToolFsmState::DraggingPivot => {
|
||||||
let hint_data = HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]);
|
let hint_data = HintData(vec![HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()])]);
|
||||||
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
responses.add(FrontendMessage::UpdateInputHints { hint_data });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ impl Quad {
|
|||||||
[[self.0[0], self.0[1]], [self.0[1], self.0[2]], [self.0[2], self.0[3]], [self.0[3], self.0[0]]]
|
[[self.0[0], self.0[1]], [self.0[1], self.0[2]], [self.0[2], self.0[3]], [self.0[3], self.0[0]]]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get two edges as orthogonal bases.
|
/// Get two edges as bases.
|
||||||
pub fn edges(&self) -> [[DVec2; 2]; 2] {
|
pub fn edges(&self) -> [[DVec2; 2]; 2] {
|
||||||
[[self.0[0], self.0[1]], [self.0[1], self.0[2]]]
|
[[self.0[0], self.0[1]], [self.0[1], self.0[2]]]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user