mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-03 01:58:11 +08:00
Hide handle overlays too close to the parent anchor (#951)
Hide handle overlay Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
2a27471363
commit
741e61a59d
@@ -54,6 +54,7 @@ pub const BOUNDS_ROTATE_THRESHOLD: f64 = 20.;
|
|||||||
// Path tool
|
// Path tool
|
||||||
pub const MANIPULATOR_GROUP_MARKER_SIZE: f64 = 5.;
|
pub const MANIPULATOR_GROUP_MARKER_SIZE: f64 = 5.;
|
||||||
pub const SELECTION_THRESHOLD: f64 = 10.;
|
pub const SELECTION_THRESHOLD: f64 = 10.;
|
||||||
|
pub const HIDE_HANDLE_DISTANCE: f64 = 3.;
|
||||||
|
|
||||||
// Pen tool
|
// Pen tool
|
||||||
pub const CREATE_CURVE_THRESHOLD: f64 = 5.;
|
pub const CREATE_CURVE_THRESHOLD: f64 = 5.;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::application::generate_uuid;
|
use crate::application::generate_uuid;
|
||||||
use crate::consts::VIEWPORT_GRID_ROUNDING_BIAS;
|
use crate::consts::VIEWPORT_GRID_ROUNDING_BIAS;
|
||||||
use crate::consts::{COLOR_ACCENT, MANIPULATOR_GROUP_MARKER_SIZE, PATH_OUTLINE_WEIGHT};
|
use crate::consts::{COLOR_ACCENT, HIDE_HANDLE_DISTANCE, MANIPULATOR_GROUP_MARKER_SIZE, PATH_OUTLINE_WEIGHT};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use document_legacy::color::Color;
|
use document_legacy::color::Color;
|
||||||
@@ -45,7 +45,7 @@ impl OverlayRenderer {
|
|||||||
trace!("Overlay: Outline cache {:?}", &outline_cache);
|
trace!("Overlay: Outline cache {:?}", &outline_cache);
|
||||||
|
|
||||||
// Create an outline if we do not have a cached one
|
// Create an outline if we do not have a cached one
|
||||||
if outline_cache == None {
|
if outline_cache.is_none() {
|
||||||
let outline_path = self.create_shape_outline_overlay(shape.clone(), responses);
|
let outline_path = self.create_shape_outline_overlay(shape.clone(), responses);
|
||||||
self.shape_overlay_cache.insert(*layer_id, outline_path.clone());
|
self.shape_overlay_cache.insert(*layer_id, outline_path.clone());
|
||||||
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
Self::place_outline_overlays(outline_path.clone(), &transform, responses);
|
||||||
@@ -58,26 +58,42 @@ impl OverlayRenderer {
|
|||||||
|
|
||||||
// Create, place, and style the manipulator overlays
|
// Create, place, and style the manipulator overlays
|
||||||
for (manipulator_group_id, manipulator_group) in shape.manipulator_groups().enumerate() {
|
for (manipulator_group_id, manipulator_group) in shape.manipulator_groups().enumerate() {
|
||||||
let manipulator_group_cache = self.manipulator_group_overlay_cache.get_mut(&(*layer_id, *manipulator_group_id));
|
let manipulator_group_cache = self.manipulator_group_overlay_cache.entry((*layer_id, *manipulator_group_id)).or_insert(Default::default());
|
||||||
|
|
||||||
// If cached update placement and style
|
// Only view in and out handles if they are not on top of the anchor
|
||||||
if let Some(manipulator_group_overlays) = manipulator_group_cache {
|
let [in_handle, out_handle] = {
|
||||||
trace!("Overlay: Updating detail overlays for {:?}", manipulator_group_overlays);
|
let Some(anchor) = manipulator_group.points[ManipulatorType::Anchor].as_ref() else{
|
||||||
Self::place_manipulator_group_overlays(manipulator_group, manipulator_group_overlays, &transform, responses);
|
continue;
|
||||||
Self::style_overlays(manipulator_group, manipulator_group_overlays, responses);
|
};
|
||||||
|
|
||||||
|
let anchor_position = transform.transform_point2(anchor.position);
|
||||||
|
let filter_position = |handle: &&ManipulatorPoint| transform.transform_point2(handle.position).distance_squared(anchor_position) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE;
|
||||||
|
let filter_manipulator_point = |manipulator_type| manipulator_group.points[manipulator_type as usize].as_ref().filter(filter_position);
|
||||||
|
[filter_manipulator_point(ManipulatorType::InHandle), filter_manipulator_point(ManipulatorType::OutHandle)]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create anchor
|
||||||
|
manipulator_group_cache[0] = manipulator_group_cache[0].take().or_else(|| Some(Self::create_anchor_overlay(responses)));
|
||||||
|
// Create or delete in handle
|
||||||
|
if in_handle.is_none() {
|
||||||
|
Self::remove_overlay(manipulator_group_cache[1].take(), responses);
|
||||||
|
Self::remove_overlay(manipulator_group_cache[3].take(), responses);
|
||||||
} else {
|
} else {
|
||||||
// Create if not cached
|
manipulator_group_cache[1] = manipulator_group_cache[1].take().or_else(|| Self::create_handle_overlay_if_exists(in_handle, responses));
|
||||||
let mut manipulator_group_overlays = [
|
manipulator_group_cache[3] = manipulator_group_cache[3].take().or_else(|| Self::create_handle_line_overlay_if_exists(in_handle, responses));
|
||||||
Some(self.create_anchor_overlay(responses)),
|
|
||||||
Self::create_handle_overlay_if_exists(&manipulator_group.points[ManipulatorType::InHandle], responses),
|
|
||||||
Self::create_handle_overlay_if_exists(&manipulator_group.points[ManipulatorType::OutHandle], responses),
|
|
||||||
Self::create_handle_line_overlay_if_exists(&manipulator_group.points[ManipulatorType::InHandle], responses),
|
|
||||||
Self::create_handle_line_overlay_if_exists(&manipulator_group.points[ManipulatorType::OutHandle], responses),
|
|
||||||
];
|
|
||||||
Self::place_manipulator_group_overlays(manipulator_group, &mut manipulator_group_overlays, &transform, responses);
|
|
||||||
Self::style_overlays(manipulator_group, &manipulator_group_overlays, responses);
|
|
||||||
self.manipulator_group_overlay_cache.insert((*layer_id, *manipulator_group_id), manipulator_group_overlays);
|
|
||||||
}
|
}
|
||||||
|
// Create or delete out handle
|
||||||
|
if out_handle.is_none() {
|
||||||
|
Self::remove_overlay(manipulator_group_cache[2].take(), responses);
|
||||||
|
Self::remove_overlay(manipulator_group_cache[4].take(), responses);
|
||||||
|
} else {
|
||||||
|
manipulator_group_cache[2] = manipulator_group_cache[2].take().or_else(|| Self::create_handle_overlay_if_exists(out_handle, responses));
|
||||||
|
manipulator_group_cache[4] = manipulator_group_cache[4].take().or_else(|| Self::create_handle_line_overlay_if_exists(out_handle, responses));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update placement and style
|
||||||
|
Self::place_manipulator_group_overlays(manipulator_group, manipulator_group_cache, &transform, responses);
|
||||||
|
Self::style_overlays(manipulator_group, manipulator_group_cache, responses);
|
||||||
}
|
}
|
||||||
// TODO Handle removing shapes from cache so we don't memory leak
|
// TODO Handle removing shapes from cache so we don't memory leak
|
||||||
// Eventually will get replaced with am immediate mode renderer for overlays
|
// Eventually will get replaced with am immediate mode renderer for overlays
|
||||||
@@ -143,7 +159,7 @@ impl OverlayRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a single anchor overlay and return its layer ID.
|
/// Create a single anchor overlay and return its layer ID.
|
||||||
fn create_anchor_overlay(&self, responses: &mut VecDeque<Message>) -> Vec<LayerId> {
|
fn create_anchor_overlay(responses: &mut VecDeque<Message>) -> Vec<LayerId> {
|
||||||
let layer_path = vec![generate_uuid()];
|
let layer_path = vec![generate_uuid()];
|
||||||
let operation = Operation::AddRect {
|
let operation = Operation::AddRect {
|
||||||
path: layer_path.clone(),
|
path: layer_path.clone(),
|
||||||
@@ -169,8 +185,15 @@ impl OverlayRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a single handle overlay and return its layer id if it exists.
|
/// Create a single handle overlay and return its layer id if it exists.
|
||||||
fn create_handle_overlay_if_exists(handle: &Option<ManipulatorPoint>, responses: &mut VecDeque<Message>) -> Option<Vec<LayerId>> {
|
fn create_handle_overlay_if_exists(handle: Option<&ManipulatorPoint>, responses: &mut VecDeque<Message>) -> Option<Vec<LayerId>> {
|
||||||
handle.as_ref().map(|_| Self::create_handle_overlay(responses))
|
handle.map(|_| Self::create_handle_overlay(responses))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove an overlay at the specified path
|
||||||
|
fn remove_overlay(path: Option<Vec<LayerId>>, responses: &mut VecDeque<Message>) {
|
||||||
|
if let Some(path) = path {
|
||||||
|
responses.push_back(DocumentMessage::Overlays(Operation::DeleteLayer { path }.into()).into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create the shape outline overlay and return its layer ID.
|
/// Create the shape outline overlay and return its layer ID.
|
||||||
@@ -187,7 +210,7 @@ impl OverlayRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create the shape outline overlay and return its layer ID.
|
/// Create the shape outline overlay and return its layer ID.
|
||||||
fn create_handle_line_overlay_if_exists(handle: &Option<ManipulatorPoint>, responses: &mut VecDeque<Message>) -> Option<Vec<LayerId>> {
|
fn create_handle_line_overlay_if_exists(handle: Option<&ManipulatorPoint>, responses: &mut VecDeque<Message>) -> Option<Vec<LayerId>> {
|
||||||
handle.as_ref().map(|_| Self::create_handle_line_overlay(responses))
|
handle.as_ref().map(|_| Self::create_handle_line_overlay(responses))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,15 +228,13 @@ impl OverlayRenderer {
|
|||||||
fn place_manipulator_group_overlays(manipulator_group: &ManipulatorGroup, overlays: &mut ManipulatorGroupOverlays, parent_transform: &DAffine2, responses: &mut VecDeque<Message>) {
|
fn place_manipulator_group_overlays(manipulator_group: &ManipulatorGroup, overlays: &mut ManipulatorGroupOverlays, parent_transform: &DAffine2, responses: &mut VecDeque<Message>) {
|
||||||
if let Some(manipulator_point) = &manipulator_group.points[ManipulatorType::Anchor] {
|
if let Some(manipulator_point) = &manipulator_group.points[ManipulatorType::Anchor] {
|
||||||
// Helper function to keep things DRY (don't-repeat-yourself)
|
// Helper function to keep things DRY (don't-repeat-yourself)
|
||||||
let mut place_handle_and_line = |handle: &ManipulatorPoint, line_source: &mut Option<Vec<LayerId>>, marker_source: &mut Option<Vec<LayerId>>| {
|
let mut place_handle_and_line = |handle: &ManipulatorPoint, line_overlay: &mut Vec<LayerId>, marker_source: &mut Option<Vec<LayerId>>| {
|
||||||
let line_overlay = line_source.take().unwrap_or_else(|| Self::create_handle_line_overlay(responses));
|
|
||||||
let line_vector = parent_transform.transform_point2(manipulator_point.position) - parent_transform.transform_point2(handle.position);
|
let line_vector = parent_transform.transform_point2(manipulator_point.position) - parent_transform.transform_point2(handle.position);
|
||||||
let scale = DVec2::splat(line_vector.length());
|
let scale = DVec2::splat(line_vector.length());
|
||||||
let angle = -line_vector.angle_between(DVec2::X);
|
let angle = -line_vector.angle_between(DVec2::X);
|
||||||
let translation = (parent_transform.transform_point2(handle.position) + VIEWPORT_GRID_ROUNDING_BIAS).round() + DVec2::splat(0.5);
|
let translation = (parent_transform.transform_point2(handle.position) + VIEWPORT_GRID_ROUNDING_BIAS).round() + DVec2::splat(0.5);
|
||||||
let transform = DAffine2::from_scale_angle_translation(scale, angle, translation).to_cols_array();
|
let transform = DAffine2::from_scale_angle_translation(scale, angle, translation).to_cols_array();
|
||||||
responses.push_back(Self::overlay_transform_message(line_overlay.clone(), transform));
|
responses.push_back(Self::overlay_transform_message(line_overlay.clone(), transform));
|
||||||
*line_source = Some(line_overlay);
|
|
||||||
|
|
||||||
let marker_overlay = marker_source.take().unwrap_or_else(|| Self::create_handle_overlay(responses));
|
let marker_overlay = marker_source.take().unwrap_or_else(|| Self::create_handle_overlay(responses));
|
||||||
let scale = DVec2::splat(MANIPULATOR_GROUP_MARKER_SIZE);
|
let scale = DVec2::splat(MANIPULATOR_GROUP_MARKER_SIZE);
|
||||||
@@ -228,11 +249,11 @@ impl OverlayRenderer {
|
|||||||
let [_, h1, h2] = &manipulator_group.points;
|
let [_, h1, h2] = &manipulator_group.points;
|
||||||
let [a, b, c, line1, line2] = overlays;
|
let [a, b, c, line1, line2] = overlays;
|
||||||
let markers = [a, b, c];
|
let markers = [a, b, c];
|
||||||
if let Some(handle) = &h1 {
|
if let (Some(handle), Some(line_source)) = (h1.as_ref(), line1.as_mut()) {
|
||||||
place_handle_and_line(handle, line1, markers[handle.manipulator_type as usize]);
|
place_handle_and_line(handle, line_source, markers[handle.manipulator_type as usize]);
|
||||||
}
|
}
|
||||||
if let Some(handle) = &h2 {
|
if let (Some(handle), Some(line_source)) = (h2.as_ref(), line2.as_mut()) {
|
||||||
place_handle_and_line(handle, line2, markers[handle.manipulator_type as usize]);
|
place_handle_and_line(handle, line_source, markers[handle.manipulator_type as usize]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place the anchor point overlay
|
// Place the anchor point overlay
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ impl ShapeEditor {
|
|||||||
if let Some(shape) = document.layer(layer_path).ok()?.as_subpath() {
|
if let Some(shape) = document.layer(layer_path).ok()?.as_subpath() {
|
||||||
let viewspace = document.generate_transform_relative_to_viewport(layer_path).ok()?;
|
let viewspace = document.generate_transform_relative_to_viewport(layer_path).ok()?;
|
||||||
for (manipulator_id, manipulator) in shape.manipulator_groups().enumerate() {
|
for (manipulator_id, manipulator) in shape.manipulator_groups().enumerate() {
|
||||||
let manipulator_point_index = manipulator.closest_point(&viewspace, pos);
|
let manipulator_point_index = manipulator.closest_point(&viewspace, pos, crate::consts::HIDE_HANDLE_DISTANCE);
|
||||||
if let Some(point) = &manipulator.points[manipulator_point_index] {
|
if let Some(point) = &manipulator.points[manipulator_point_index] {
|
||||||
if point.editor_state.can_be_selected {
|
if point.editor_state.can_be_selected {
|
||||||
let distance_squared = viewspace.transform_point2(point.position).distance_squared(pos);
|
let distance_squared = viewspace.transform_point2(point.position).distance_squared(pos);
|
||||||
|
|||||||
@@ -78,12 +78,23 @@ impl ManipulatorGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the closest [ManipulatorPoint] owned by this [ManipulatorGroup]. This may return the anchor or either handle.
|
/// Finds the closest [ManipulatorPoint] owned by this [ManipulatorGroup]. This may return the anchor or either handle.
|
||||||
pub fn closest_point(&self, transform_space: &DAffine2, target: glam::DVec2) -> usize {
|
pub fn closest_point(&self, transform_space: &DAffine2, target: glam::DVec2, hide_handle_distance: f64) -> usize {
|
||||||
let mut closest_index: usize = 0;
|
let mut closest_index: usize = 0;
|
||||||
let mut closest_distance_squared: f64 = f64::MAX; // Not ideal
|
let mut closest_distance_squared: f64 = f64::MAX; // Not ideal
|
||||||
for (index, point) in self.points.iter().enumerate() {
|
for (index, point) in self.points.iter().enumerate() {
|
||||||
if let Some(point) = point {
|
if let Some(point) = point {
|
||||||
let distance_squared = transform_space.transform_point2(point.position).distance_squared(target);
|
let transfomed_point = transform_space.transform_point2(point.position);
|
||||||
|
|
||||||
|
// Don't check handles under the anchor
|
||||||
|
if index != ManipulatorType::Anchor as usize {
|
||||||
|
if let Some(anchor) = &self.points[ManipulatorType::Anchor] {
|
||||||
|
if transfomed_point.distance_squared(transform_space.transform_point2(anchor.position)) < hide_handle_distance * hide_handle_distance {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let distance_squared = transfomed_point.distance_squared(target);
|
||||||
if distance_squared < closest_distance_squared {
|
if distance_squared < closest_distance_squared {
|
||||||
closest_distance_squared = distance_squared;
|
closest_distance_squared = distance_squared;
|
||||||
closest_index = index;
|
closest_index = index;
|
||||||
|
|||||||
Reference in New Issue
Block a user