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:
0HyperCube
2023-01-08 19:32:26 -08:00
committed by Keavon Chambers
co-authored by Keavon Chambers
parent 5a34b3a966
commit 68a1ab5297
4 changed files with 66 additions and 33 deletions
+1
View File
@@ -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;