Make the transform cage resize about the pivot when Alt is pressed (#2226)

* Change the pivot behaviour when resizing bounds with alt in the select tool

* Add scale factor maximum

* Fix bug when encountering snapping; tidy up and comment the code

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
James Lindsay 2025-01-31 02:07:40 +00:00 committed by GitHub
parent fb7eae8f02
commit 5a8eb9dd1b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 41 additions and 15 deletions

View File

@ -68,6 +68,11 @@ pub const MIN_LENGTH_FOR_MIDPOINT_VISIBILITY: f64 = 20.;
pub const MIN_LENGTH_FOR_CORNERS_VISIBILITY: f64 = 12.;
/// When the width or height of the transform cage is less than this value, only the exterior of the bounding box will act as a click target for resizing.
pub const MIN_LENGTH_FOR_RESIZE_TO_INCLUDE_INTERIOR: f64 = 40.;
/// When dragging the edge of a cage with Alt, it centers around the pivot.
/// However if the pivot is on or near the same edge you are dragging, we should avoid scaling by a massive factor caused by the small denominator.
///
/// The motion of the user's cursor by an `x` pixel offset results in `x * scale_factor` pixels of offset on the other side.
pub const MAXIMUM_ALT_SCALE_FACTOR: f64 = 25.;
// PATH TOOL
pub const MANIPULATOR_GROUP_MARKER_SIZE: f64 = 6.;

View File

@ -1,5 +1,6 @@
use crate::consts::{
BOUNDS_ROTATE_THRESHOLD, BOUNDS_SELECT_THRESHOLD, MIN_LENGTH_FOR_CORNERS_VISIBILITY, MIN_LENGTH_FOR_MIDPOINT_VISIBILITY, MIN_LENGTH_FOR_RESIZE_TO_INCLUDE_INTERIOR, SELECTION_DRAG_ANGLE,
BOUNDS_ROTATE_THRESHOLD, BOUNDS_SELECT_THRESHOLD, MAXIMUM_ALT_SCALE_FACTOR, MIN_LENGTH_FOR_CORNERS_VISIBILITY, MIN_LENGTH_FOR_MIDPOINT_VISIBILITY, MIN_LENGTH_FOR_RESIZE_TO_INCLUDE_INTERIOR,
SELECTION_DRAG_ANGLE,
};
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
@ -8,9 +9,9 @@ use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::snapping::SnapTypeConfiguration;
use graphene_core::renderer::Quad;
use graphene_std::renderer::Rect;
use glam::{DAffine2, DVec2};
use graphene_std::renderer::Rect;
use super::snapping::{self, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnappedPoint};
@ -88,6 +89,7 @@ impl SelectedEdges {
let mut min = self.bounds[0];
let mut max = self.bounds[1];
if self.top {
min.y = mouse.y;
} else if self.bottom {
@ -100,24 +102,43 @@ impl SelectedEdges {
}
let mut pivot = self.pivot_from_bounds(min, max);
// Alt: Scaling around the pivot
if let Some(center_around) = center_around {
let center_around = transform.inverse().transform_point2(center_around);
if self.top {
pivot.y = center_around.y;
max.y = center_around.y * 2. - min.y;
} else if self.bottom {
pivot.y = center_around.y;
min.y = center_around.y * 2. - max.y;
}
if self.left {
pivot.x = center_around.x;
max.x = center_around.x * 2. - min.x;
} else if self.right {
pivot.x = center_around.x;
min.x = center_around.x * 2. - max.x;
let calculate_distance = |moving_opposite_to_drag: &mut f64, center: f64, dragging: f64, original_dragging: f64, current_side: bool| {
if !current_side {
return true;
}
// The motion of the user's cursor by an `x` pixel offset results in `x * scale_factor` pixels of offset on the other side
let scale_factor = (center - *moving_opposite_to_drag) / (center - original_dragging);
let new_distance = center - scale_factor * (center - dragging);
// Ignore the Alt key press and scale the dragged edge normally
if !new_distance.is_finite() || scale_factor.abs() > MAXIMUM_ALT_SCALE_FACTOR {
// Don't go on to check the other sides since this side is already invalid, so Alt-dragging is disabled and updating the pivot would be incorrect
return false;
}
*moving_opposite_to_drag = new_distance;
true
};
// Update the value of the first argument through mutation, and if we make it through all of them without
// encountering a case where the pivot is too near the edge, we also update the pivot so scaling occurs around it
if calculate_distance(&mut max.y, center_around.y, min.y, self.bounds[0].y, self.top)
&& calculate_distance(&mut min.y, center_around.y, max.y, self.bounds[1].y, self.bottom)
&& calculate_distance(&mut max.x, center_around.x, min.x, self.bounds[0].x, self.left)
&& calculate_distance(&mut min.x, center_around.x, max.x, self.bounds[1].x, self.right)
{
pivot = center_around;
}
}
// Shift: Aspect ratio constraint
if constrain {
let size = max - min;
let min_pivot = (pivot - min) / size;