Fix the Select tool's transform dial not working on rotated layers (#4259)

This commit is contained in:
Keavon Chambers
2026-06-20 15:05:30 -07:00
committed by GitHub
parent e8f9413e03
commit 194c5b2222
3 changed files with 27 additions and 19 deletions

View File

@@ -8,7 +8,6 @@ use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::transformation::OriginalTransforms;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::compass_rose::Axis;
use crate::messages::tool::common_functionality::snapping::SnapTypeConfiguration;
use glam::{DAffine2, DMat2, DVec2};
use graphene_std::renderer::Quad;
@@ -302,9 +301,13 @@ impl SelectedEdges {
}
}
/// Aligns the mouse position to the closest axis
pub fn axis_align_drag(axis_align: bool, axis: Axis, position: DVec2, start: DVec2) -> DVec2 {
if axis_align {
/// Aligns the mouse position to the closest axis. The `axis_constraint`, if nonzero, is a unit vector along which dragging is constrained (e.g. the compass rose's local axis direction).
pub fn axis_align_drag(axis_align: bool, axis_constraint: DVec2, position: DVec2, start: DVec2) -> DVec2 {
// An explicit axis constraint (the compass rose's local axis) takes priority over the screen-space angle snapping
if axis_constraint != DVec2::ZERO {
let mouse_position = position - start;
start + axis_constraint * mouse_position.dot(axis_constraint)
} else if axis_align {
let mouse_position = position - start;
let snap_resolution = SELECTION_DRAG_ANGLE.to_radians();
let angle = -mouse_position.angle_to(DVec2::X);
@@ -315,18 +318,14 @@ pub fn axis_align_drag(axis_align: bool, axis: Axis, position: DVec2, start: DVe
} else {
start
}
} else if axis.is_constraint() {
let mouse_position = position - start;
let axis_vector: DVec2 = axis.into();
start + axis_vector * mouse_position.dot(axis_vector)
} else {
position
}
}
/// Snaps a dragging event from the artboard or select tool
pub fn snap_drag(start: DVec2, current: DVec2, snap_to_axis: bool, axis: Axis, snap_data: SnapData, snap_manager: &mut SnapManager, candidates: &[SnapCandidatePoint]) -> DVec2 {
let mouse_position = axis_align_drag(snap_to_axis, axis, snap_data.input.mouse.position, start);
/// Snaps a dragging event from the artboard or select tool. A nonzero `axis_constraint` is a unit vector constraining the drag to that direction.
pub fn snap_drag(start: DVec2, current: DVec2, snap_to_axis: bool, axis_constraint: DVec2, snap_data: SnapData, snap_manager: &mut SnapManager, candidates: &[SnapCandidatePoint]) -> DVec2 {
let mouse_position = axis_align_drag(snap_to_axis, axis_constraint, snap_data.input.mouse.position, start);
let document = snap_data.document;
let total_mouse_delta_document = document.metadata().document_to_viewport.inverse().transform_vector2(mouse_position - start);
let mouse_delta_document = document.metadata().document_to_viewport.inverse().transform_vector2(mouse_position - current);
@@ -346,7 +345,7 @@ pub fn snap_drag(start: DVec2, current: DVec2, snap_to_axis: bool, axis: Axis, s
let mut point = point.clone();
point.document_point += total_mouse_delta_document;
let constrained_along_axis = snap_to_axis || axis.is_constraint();
let constrained_along_axis = snap_to_axis || axis_constraint != DVec2::ZERO;
let snapped = if constrained_along_axis {
let constraint = SnapConstraint::Line {
origin: point.document_point,

View File

@@ -3,7 +3,6 @@ use crate::messages::portfolio::document::graph_operation::utility_types::Transf
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::compass_rose::Axis;
use crate::messages::tool::common_functionality::measure;
use crate::messages::tool::common_functionality::resize::Resize;
use crate::messages::tool::common_functionality::snapping;
@@ -336,7 +335,7 @@ impl Fsm for ArtboardToolFsmState {
let snap_data = SnapData::ignore(document, input, viewport, &ignore);
let document_to_viewport = document.metadata().document_to_viewport;
let [start, current] = [tool_data.drag_start, tool_data.drag_current].map(|point| document_to_viewport.transform_point2(point));
let mouse_delta = snap_drag(start, current, axis_align, Axis::None, snap_data, &mut tool_data.draw.snap_manager, &tool_data.snap_candidates);
let mouse_delta = snap_drag(start, current, axis_align, DVec2::ZERO, snap_data, &mut tool_data.draw.snap_manager, &tool_data.snap_candidates);
let size = bounds.bounds[1] - bounds.bounds[0];
let position = bounds.bounds[0] + bounds.transform.inverse().transform_vector2(mouse_delta);

View File

@@ -1282,13 +1282,23 @@ impl Fsm for SelectToolFsmState {
.map(|bounding_box_manager| bounding_box_manager.transform * Quad::from_box(bounding_box_manager.bounds))
.map_or(DVec2::X, |quad| (quad.top_left() - quad.top_right()).normalize_or(DVec2::X));
let mouse_delta = snap_drag(start, current, tool_data.axis_align, axis, snap_data, &mut tool_data.snap_manager, &tool_data.snap_candidates);
let mouse_delta = match axis {
Axis::X => mouse_delta.project_onto(e0),
Axis::Y => mouse_delta.project_onto(e0.perp()),
Axis::None => mouse_delta,
// Constrain the drag to the selection's local axis (not the world axis) so a rotated object's arrows track the mouse one-to-one
let axis_constraint = match axis {
Axis::X => e0,
Axis::Y => e0.perp(),
Axis::None => DVec2::ZERO,
};
let mouse_delta = snap_drag(
start,
current,
tool_data.axis_align,
axis_constraint,
snap_data,
&mut tool_data.snap_manager,
&tool_data.snap_candidates,
);
// TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481
for layer in document.network_interface.shallowest_unique_layers(&[]) {
responses.add_front(GraphOperationMessage::TransformChange {