mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 03:58:12 +08:00
Add the compass rose translation gizmo to the transform cage (#2277)
* Rotate pivot and squares to orient along quad * Add compass rose UI * Add compass rose functionality * Refactor code and polish things * Fix UI * Fix crash * More polish * Rework arrow to use different selection method * Adjust for rotated layer and show when within cage * Don't show when other modes are possible * Fix glitchy compass * fixes * fixes * WIP separate pivot and compass rose (not compiling) * Complete file moving fixes * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
e44c460cf8
commit
70b4beab49
@@ -0,0 +1,85 @@
|
||||
use crate::consts::{COMPASS_ROSE_ARROW_CLICK_TARGET_ANGLE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER};
|
||||
use crate::messages::prelude::DocumentMessageHandler;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::f64::consts::FRAC_PI_2;
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct CompassRose {
|
||||
compass_center: DVec2,
|
||||
}
|
||||
|
||||
impl CompassRose {
|
||||
pub fn refresh_transform(&mut self, document: &DocumentMessageHandler) {
|
||||
let [min, max] = document.selected_visible_and_unlock_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]);
|
||||
self.compass_center = (DAffine2::from_translation(min) * DAffine2::from_scale(max - min)).transform_point2(DVec2::splat(0.5));
|
||||
}
|
||||
|
||||
pub fn compass_rose_position(&self) -> DVec2 {
|
||||
self.compass_center
|
||||
}
|
||||
|
||||
pub fn compass_rose_state(&self, mouse: DVec2, angle: f64) -> CompassRoseState {
|
||||
const COMPASS_ROSE_RING_INNER_RADIUS_SQUARED: f64 = (COMPASS_ROSE_RING_INNER_DIAMETER / 2.) * (COMPASS_ROSE_RING_INNER_DIAMETER / 2.);
|
||||
const COMPASS_ROSE_HOVER_RING_RADIUS_SQUARED: f64 = (COMPASS_ROSE_HOVER_RING_DIAMETER / 2.) * (COMPASS_ROSE_HOVER_RING_DIAMETER / 2.);
|
||||
|
||||
let compass_distance_squared = mouse.distance_squared(self.compass_center);
|
||||
|
||||
if !(COMPASS_ROSE_RING_INNER_RADIUS_SQUARED..COMPASS_ROSE_HOVER_RING_RADIUS_SQUARED).contains(&compass_distance_squared) {
|
||||
return CompassRoseState::None;
|
||||
}
|
||||
|
||||
let angle = (mouse - self.compass_center).angle_to(DVec2::from_angle(angle)).abs();
|
||||
let resolved_angle = (FRAC_PI_2 - angle).abs();
|
||||
let angular_width = COMPASS_ROSE_ARROW_CLICK_TARGET_ANGLE.to_radians();
|
||||
|
||||
if resolved_angle < angular_width {
|
||||
CompassRoseState::AxisY
|
||||
} else if resolved_angle > (FRAC_PI_2 - angular_width) {
|
||||
CompassRoseState::AxisX
|
||||
} else {
|
||||
CompassRoseState::Ring
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum CompassRoseState {
|
||||
Ring,
|
||||
AxisX,
|
||||
AxisY,
|
||||
None,
|
||||
}
|
||||
|
||||
impl CompassRoseState {
|
||||
pub fn can_grab(&self) -> bool {
|
||||
matches!(self, Self::Ring | Self::AxisX | Self::AxisY)
|
||||
}
|
||||
|
||||
pub fn is_ring(&self) -> bool {
|
||||
matches!(self, Self::Ring)
|
||||
}
|
||||
|
||||
pub fn axis_type(&self) -> Option<Axis> {
|
||||
match self {
|
||||
CompassRoseState::AxisX => Some(Axis::X),
|
||||
CompassRoseState::AxisY => Some(Axis::Y),
|
||||
CompassRoseState::Ring => Some(Axis::None),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
|
||||
pub enum Axis {
|
||||
#[default]
|
||||
None,
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
impl Axis {
|
||||
pub fn is_constraint(&self) -> bool {
|
||||
matches!(self, Self::X | Self::Y)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod auto_panning;
|
||||
pub mod color_selector;
|
||||
pub mod compass_rose;
|
||||
pub mod graph_modification_utils;
|
||||
pub mod measure;
|
||||
pub mod pivot;
|
||||
|
||||
@@ -83,10 +83,10 @@ impl Pivot {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_pivot(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext) {
|
||||
pub fn update_pivot(&mut self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext, angle: f64) {
|
||||
self.recalculate_pivot(document);
|
||||
if let Some(pivot) = self.pivot {
|
||||
overlay_context.pivot(pivot);
|
||||
overlay_context.pivot(pivot, angle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::consts::{
|
||||
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,
|
||||
BOUNDS_ROTATE_THRESHOLD, BOUNDS_SELECT_THRESHOLD, COLOR_OVERLAY_WHITE, 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;
|
||||
@@ -379,7 +379,10 @@ impl BoundingBoxManager {
|
||||
// Draw the bounding box rectangle
|
||||
overlay_context.quad(quad, None);
|
||||
|
||||
let mut draw_handle = |point: DVec2| overlay_context.square(point, Some(6.), None, None);
|
||||
let mut draw_handle = |point: DVec2| {
|
||||
let quad = DAffine2::from_angle_translation((quad.top_left() - quad.top_right()).to_angle(), point) * Quad::from_box([DVec2::splat(-3.), DVec2::splat(3.)]);
|
||||
overlay_context.quad(quad, Some(COLOR_OVERLAY_WHITE));
|
||||
};
|
||||
|
||||
// Draw the horizontal midpoint drag handles
|
||||
if matches!(category, HandleDisplayCategory::Full | HandleDisplayCategory::Narrow | HandleDisplayCategory::ReducedLandscape) {
|
||||
|
||||
Reference in New Issue
Block a user