Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xform cage viz #2277

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4dfa3ec
Rotate pivot and squares to orient along quad
mTvare6 Jan 30, 2025
dcfa4a8
Add compass rose UI
mTvare6 Feb 10, 2025
eed45d4
Merge branch 'master' into xform-cage-viz
mTvare6 Feb 10, 2025
6eac06d
Add compass rose functionality
mTvare6 Feb 11, 2025
8ec5d6c
Merge branch 'master' into xform-cage-viz
mTvare6 Feb 11, 2025
13e2a3c
Refactor code and polish things
mTvare6 Feb 11, 2025
d7bdda3
Fix UI
mTvare6 Feb 12, 2025
f635a93
Fix crash
mTvare6 Feb 12, 2025
01e3968
Merge branch 'master' into xform-cage-viz
mTvare6 Feb 12, 2025
ad4f229
More polish
mTvare6 Feb 12, 2025
fc04311
Merge branch 'master' into xform-cage-viz
mTvare6 Feb 12, 2025
f5bf218
Merge branch 'master' into xform-cage-viz
mTvare6 Feb 12, 2025
201d667
Rework arrow to use different selection method
mTvare6 Feb 12, 2025
cac48de
Merge branch 'xform-cage-viz' of ssh://github.com/mtvare6/Graphite in…
mTvare6 Feb 12, 2025
1bf90d0
Adjust for rotated layer and show when within cage
mTvare6 Feb 13, 2025
862cfff
Don't show when other modes are possible
mTvare6 Feb 13, 2025
e9ec785
Merge branch 'master' into xform-cage-viz
mTvare6 Feb 13, 2025
b4ca941
Fix glitchy compass
mTvare6 Feb 13, 2025
7e9872b
fixes
mTvare6 Feb 13, 2025
6b87674
Merge branch 'master' into xform-cage-viz
Keavon Feb 13, 2025
23b7bc0
fixes
mTvare6 Feb 13, 2025
f442d54
Merge branch 'xform-cage-viz' of ssh://github.com/mtvare6/Graphite in…
mTvare6 Feb 13, 2025
d830bde
WIP separate pivot and compass rose (not compiling)
Keavon Feb 13, 2025
363ce11
Complete file moving fixes
mTvare6 Feb 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion editor/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,19 @@ pub const DEFAULT_STROKE_WIDTH: f64 = 2.;
pub const SELECTION_TOLERANCE: f64 = 5.;
pub const DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD: f64 = 15.;
pub const SELECTION_DRAG_ANGLE: f64 = 90.;

// PIVOT
pub const PIVOT_CROSSHAIR_THICKNESS: f64 = 1.;
pub const PIVOT_CROSSHAIR_LENGTH: f64 = 9.;
pub const PIVOT_DIAMETER: f64 = 5.;

// COMPASS ROSE
pub const COMPASS_ROSE_ANGLE_WIDTH: f64 = 20.; // Must be less than 45
pub const COMPASS_ROSE_RING_INNER_DIAMETER: f64 = 13.;
pub const COMPASS_ROSE_MAIN_RING_DIAMETER: f64 = 15.;
pub const COMPASS_ROSE_HOVER_RING_DIAMETER: f64 = 23.;
pub const COMPASS_ROSE_ARROW_SIZE: f64 = 5.;

// TRANSFORM OVERLAY
pub const ANGLE_MEASURE_RADIUS_FACTOR: f64 = 0.04;
pub const ARC_MEASURE_RADIUS_FACTOR_RANGE: (f64, f64) = (0.05, 0.15);
Expand Down Expand Up @@ -108,7 +117,6 @@ pub const COLOR_OVERLAY_RED: &str = "#ef5454";
pub const COLOR_OVERLAY_GRAY: &str = "#cccccc";
pub const COLOR_OVERLAY_WHITE: &str = "#ffffff";
pub const COLOR_OVERLAY_LABEL_BACKGROUND: &str = "#000000cc";
pub const COLOR_OVERLAY_TRANSPARENT: &str = "#ffffff00";

// DOCUMENT
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
Expand Down
97 changes: 88 additions & 9 deletions editor/src/messages/portfolio/document/overlays/utility_types.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use super::utility_functions::overlay_canvas_context;
use crate::consts::{
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_TRANSPARENT, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS,
};
use crate::consts::{COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, PIVOT_DIAMETER};
use crate::messages::prelude::Message;

use bezier_rs::{Bezier, Subpath};
use graphene_core::renderer::Quad;
use graphene_std::vector::{PointId, SegmentId, VectorData};

use core::borrow::Borrow;
use core::f64::consts::TAU;
use core::f64::consts::{FRAC_PI_2, TAU};
use glam::{DAffine2, DVec2};
use std::collections::HashMap;
use wasm_bindgen::JsValue;
Expand Down Expand Up @@ -294,9 +295,15 @@ impl OverlayContext {

pub fn draw_scale(&mut self, start: DVec2, scale: f64, radius: f64, text: &str) {
let sign = scale.signum();
let mut fill_color = graphene_std::Color::from_rgb_str(crate::consts::COLOR_OVERLAY_WHITE.strip_prefix('#').unwrap())
.unwrap()
.with_alpha(0.05)
.rgba_hex();
fill_color.insert(0, '#');
let fill_color = Some(fill_color.as_str());
self.line(start + DVec2::X * radius * sign, start + DVec2::X * (radius * scale), None);
self.circle(start, radius, Some(COLOR_OVERLAY_TRANSPARENT), None);
self.circle(start, radius * scale.abs(), Some(COLOR_OVERLAY_TRANSPARENT), None);
self.circle(start, radius, fill_color, None);
self.circle(start, radius * scale.abs(), fill_color, None);
self.text(
text,
COLOR_OVERLAY_BLUE,
Expand All @@ -307,7 +314,79 @@ impl OverlayContext {
)
}

pub fn pivot(&mut self, position: DVec2) {
pub fn compass_rose(&mut self, compass_center: DVec2, angle: f64, show_compass_with_hover_ring: Option<bool>) {
let (compass_x, compass_y) = (compass_center.round() - DVec2::splat(0.5)).into();
self.start_dpi_aware_transform();

if let Some(show_hover_ring) = show_compass_with_hover_ring {
let old_line_width = self.render_context.line_width();

const HOVER_RING_OUTER_RADIUS: f64 = COMPASS_ROSE_HOVER_RING_DIAMETER / 2.;
const MAIN_RING_OUTER_RADIUS: f64 = COMPASS_ROSE_MAIN_RING_DIAMETER / 2.;
const MAIN_RING_INNER_RADIUS: f64 = COMPASS_ROSE_RING_INNER_DIAMETER / 2.;
const ARROW_RADIUS: f64 = COMPASS_ROSE_ARROW_SIZE / 2.;

// Hover ring
if show_hover_ring {
let hover_ring_stroke_width = HOVER_RING_OUTER_RADIUS - MAIN_RING_INNER_RADIUS;
let hover_ring_center_radius = (HOVER_RING_OUTER_RADIUS + MAIN_RING_INNER_RADIUS) / 2.0;

let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.5).rgba_hex();
fill_color.insert(0, '#');

self.render_context.set_line_width(hover_ring_stroke_width);
self.render_context.begin_path();
self.render_context.arc(compass_x, compass_y, hover_ring_center_radius, 0., TAU).expect("Failed to draw hover ring");
self.render_context.set_stroke_style_str(&fill_color);
self.render_context.stroke();
}

// Arrows
self.render_context.set_line_width(0.01);
for i in 0..4 {
let base_angle = i as f64 * FRAC_PI_2 + angle;
let direction = DVec2::from_angle(base_angle);
let color = if i % 2 == 0 { COLOR_OVERLAY_RED } else { COLOR_OVERLAY_GREEN };

let center = DVec2::new(compass_x, compass_y);

let tip = center + direction * HOVER_RING_OUTER_RADIUS;
let base = center + direction * (MAIN_RING_INNER_RADIUS + MAIN_RING_OUTER_RADIUS) / 2.;

let r = (ARROW_RADIUS.powi(2) + MAIN_RING_INNER_RADIUS.powi(2)).sqrt();
let (cos, sin) = (MAIN_RING_INNER_RADIUS / r, ARROW_RADIUS / r);
let side1 = center + r * DVec2::new(cos * direction.x - sin * direction.y, sin * direction.x + direction.y * cos);
let side2 = center + r * DVec2::new(cos * direction.x + sin * direction.y, -sin * direction.x + direction.y * cos);

self.render_context.begin_path();
self.render_context.move_to(tip.x, tip.y);
self.render_context.line_to(side1.x, side1.y);
self.render_context.line_to(base.x, base.y);
self.render_context.line_to(side2.x, side2.y);
self.render_context.close_path();

self.render_context.set_fill_style_str(color);
self.render_context.fill();
self.render_context.set_stroke_style_str(color);
self.render_context.stroke();
}

// Main ring
let main_ring_stroke_width = MAIN_RING_OUTER_RADIUS - MAIN_RING_INNER_RADIUS;
let main_ring_center_radius = (MAIN_RING_OUTER_RADIUS + MAIN_RING_INNER_RADIUS) / 2.0;

self.render_context.set_line_width(main_ring_stroke_width);
self.render_context.begin_path();
self.render_context.arc(compass_x, compass_y, main_ring_center_radius, 0., TAU).expect("Failed to draw main ring");
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE);
self.render_context.stroke();

self.render_context.set_line_width(old_line_width);
}
}

pub fn pivot(&mut self, position: DVec2, angle: f64) {
let uv = DVec2::from_angle(angle);
let (x, y) = (position.round() - DVec2::splat(0.5)).into();

self.start_dpi_aware_transform();
Expand All @@ -328,13 +407,13 @@ impl OverlayContext {
self.render_context.set_line_cap("round");

self.render_context.begin_path();
self.render_context.move_to(x - crosshair_radius, y);
self.render_context.line_to(x + crosshair_radius, y);
self.render_context.move_to(x + crosshair_radius * uv.x, y + crosshair_radius * uv.y);
self.render_context.line_to(x - crosshair_radius * uv.x, y - crosshair_radius * uv.y);
self.render_context.stroke();

self.render_context.begin_path();
self.render_context.move_to(x, y - crosshair_radius);
self.render_context.line_to(x, y + crosshair_radius);
self.render_context.move_to(x - crosshair_radius * uv.y, y + crosshair_radius * uv.x);
self.render_context.line_to(x + crosshair_radius * uv.y, y - crosshair_radius * uv.x);
self.render_context.stroke();

self.render_context.set_line_cap("butt");
Expand Down
86 changes: 86 additions & 0 deletions editor/src/messages/tool/common_functionality/compass_rose.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use glam::{DAffine2, DVec2};

use crate::consts::{COMPASS_ROSE_ANGLE_WIDTH, COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER};
use crate::messages::prelude::DocumentMessageHandler;
use std::f64::consts::FRAC_PI_2;

#[derive(Clone, Default, Debug)]
pub struct CompassRose {
/// Transform to get from normalized pivot to viewspace
transform_from_normalized: DAffine2,
}

impl CompassRose {
pub fn get_compass_position(&self) -> DVec2 {
self.transform_from_normalized.transform_point2(DVec2::splat(0.5))
}

pub fn change_transform(&mut self, document: &DocumentMessageHandler) {
let [min, max] = document.selected_visible_and_unlock_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]);
self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min);
}

/// Answers if the pointer is currently positioned over the pivot.
pub fn compass_rose_state(&self, mouse: DVec2, angle: f64) -> CompassRoseState {
let compass_center = self.get_compass_position();

let compass_distance_squared = mouse.distance_squared(compass_center);

if (COMPASS_ROSE_RING_INNER_DIAMETER / 2.).powi(2) < compass_distance_squared && compass_distance_squared < (COMPASS_ROSE_HOVER_RING_DIAMETER / 2.).powi(2) {
let angle = (mouse - compass_center).angle_to(DVec2::from_angle(angle)).abs();
let resolved_angle = (FRAC_PI_2 - angle).abs();
let width = COMPASS_ROSE_ANGLE_WIDTH.to_radians();

if resolved_angle < width {
CompassRoseState::AxisY
} else if resolved_angle > (FRAC_PI_2 - width) {
CompassRoseState::AxisX
} else {
CompassRoseState::Ring
}
} else {
CompassRoseState::None
}
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum CompassRoseState {
Ring,
AxisX,
AxisY,
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)
}
}

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,
}
}
}
1 change: 1 addition & 0 deletions editor/src/messages/tool/common_functionality/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
4 changes: 2 additions & 2 deletions editor/src/messages/tool/common_functionality/pivot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
Loading
Loading