From 5aedda0ce880a665a1411e1c88845cd876ba1ec4 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 <142654792+0SlowPoke0@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:09:41 +0530 Subject: [PATCH] Add handle visualization during point insertion in the Path tool (#2197) * added_handle_overlays * changed color to yellow * Rename color parameter * Change the color to blue --------- Co-authored-by: Keavon Chambers --- .../document/overlays/utility_functions.rs | 6 +++--- .../portfolio/document/overlays/utility_types.rs | 4 ++-- .../tool/common_functionality/shape_editor.rs | 14 ++++++++++++++ .../messages/tool/common_functionality/snapping.rs | 4 ++-- .../messages/tool/tool_messages/gradient_tool.rs | 6 +++--- .../src/messages/tool/tool_messages/path_tool.rs | 12 ++++++++++-- editor/src/messages/tool/tool_messages/pen_tool.rs | 6 +++--- 7 files changed, 37 insertions(+), 15 deletions(-) diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index c99d83e3..b71d3e38 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -41,16 +41,16 @@ pub fn path_overlays(document: &DocumentMessageHandler, shape_editor: &mut Shape bezier_rs::BezierHandles::Quadratic { handle } if not_under_anchor(handle, bezier.start) && not_under_anchor(handle, bezier.end) => { overlay_context.line(handle, bezier.start, None); overlay_context.line(handle, bezier.end, None); - overlay_context.manipulator_handle(handle, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id))); + overlay_context.manipulator_handle(handle, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)), None); } bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => { if not_under_anchor(handle_start, bezier.start) { overlay_context.line(handle_start, bezier.start, None); - overlay_context.manipulator_handle(handle_start, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id))); + overlay_context.manipulator_handle(handle_start, is_selected(selected, ManipulatorPointId::PrimaryHandle(segment_id)), None); } if not_under_anchor(handle_end, bezier.end) { overlay_context.line(handle_end, bezier.end, None); - overlay_context.manipulator_handle(handle_end, is_selected(selected, ManipulatorPointId::EndHandle(segment_id))); + overlay_context.manipulator_handle(handle_end, is_selected(selected, ManipulatorPointId::EndHandle(segment_id)), None); } } _ => {} diff --git a/editor/src/messages/portfolio/document/overlays/utility_types.rs b/editor/src/messages/portfolio/document/overlays/utility_types.rs index f35f8418..edf903bd 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_types.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_types.rs @@ -127,7 +127,7 @@ impl OverlayContext { } } - pub fn manipulator_handle(&mut self, position: DVec2, selected: bool) { + pub fn manipulator_handle(&mut self, position: DVec2, selected: bool, color: Option<&str>) { let position = position.round() - DVec2::splat(0.5); self.render_context.begin_path(); @@ -137,7 +137,7 @@ impl OverlayContext { let fill = if selected { COLOR_OVERLAY_BLUE } else { COLOR_OVERLAY_WHITE }; self.render_context.set_fill_style_str(fill); - self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE); + self.render_context.set_stroke_style_str(color.unwrap_or(COLOR_OVERLAY_BLUE)); self.render_context.fill(); self.render_context.stroke(); } diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index d53b9043..6ecdbcea 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -116,6 +116,20 @@ impl ClosestSegment { (stroke_width_sq + tolerance_sq) < dist_sq } + pub fn handle_positions(&self, document_metadata: &DocumentMetadata) -> (Option, Option) { + // Transform to viewport space + let transform = document_metadata.transform_to_viewport(self.layer); + + // Split the Bezier at the parameter `t` + let [first, second] = self.bezier.split(TValue::Parametric(self.t)); + + // Transform the handle positions to viewport space + let first_handle = first.handle_end().map(|handle| transform.transform_point2(handle)); + let second_handle = second.handle_start().map(|handle| transform.transform_point2(handle)); + + (first_handle, second_handle) + } + pub fn adjusted_insert(&self, responses: &mut VecDeque) -> PointId { let layer = self.layer; let [first, second] = self.bezier.split(TValue::Parametric(self.t)); diff --git a/editor/src/messages/tool/common_functionality/snapping.rs b/editor/src/messages/tool/common_functionality/snapping.rs index d23b2c54..9e402d41 100644 --- a/editor/src/messages/tool/common_functionality/snapping.rs +++ b/editor/src/messages/tool/common_functionality/snapping.rs @@ -461,10 +461,10 @@ impl SnapManager { overlay_context.line(viewport, target, None); } for &target in align.iter().flatten() { - overlay_context.manipulator_handle(target, false); + overlay_context.manipulator_handle(target, false, None); } if any_align { - overlay_context.manipulator_handle(viewport, false); + overlay_context.manipulator_handle(viewport, false, None); } if !any_align && ind.distribution_equal_distance_x.is_none() && ind.distribution_equal_distance_y.is_none() { diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index 9e45a30c..a6676fb7 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -257,15 +257,15 @@ impl Fsm for GradientToolFsmState { let (start, end) = (transform.transform_point2(start), transform.transform_point2(end)); overlay_context.line(start, end, None); - overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start)); - overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End)); + overlay_context.manipulator_handle(start, dragging == Some(GradientDragTarget::Start), None); + overlay_context.manipulator_handle(end, dragging == Some(GradientDragTarget::End), None); for (index, (position, _)) in stops.0.into_iter().enumerate() { if position.abs() < f64::EPSILON * 1000. || (1. - position).abs() < f64::EPSILON * 1000. { continue; } - overlay_context.manipulator_handle(start.lerp(end, position), dragging == Some(GradientDragTarget::Step(index))); + overlay_context.manipulator_handle(start.lerp(end, position), dragging == Some(GradientDragTarget::Step(index)), None); } } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index e883a87f..7f585883 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1,5 +1,5 @@ use super::tool_prelude::*; -use crate::consts::{COLOR_OVERLAY_YELLOW, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, SELECTION_THRESHOLD, SELECTION_TOLERANCE}; +use crate::consts::{COLOR_OVERLAY_BLUE, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE, INSERT_POINT_ON_SEGMENT_TOO_FAR_DISTANCE, SELECTION_THRESHOLD, SELECTION_TOLERANCE}; use crate::messages::portfolio::document::overlays::utility_functions::path_overlays; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; @@ -520,6 +520,7 @@ impl PathToolData { handle_angle } + #[allow(clippy::too_many_arguments)] fn apply_snapping( &mut self, handle_direction: DVec2, @@ -549,6 +550,7 @@ impl PathToolData { document.metadata().document_to_viewport.transform_vector2(snap_result.snapped_point_document - handle_position) } + #[allow(clippy::too_many_arguments)] fn drag( &mut self, equidistant: bool, @@ -622,7 +624,13 @@ impl Fsm for PathToolFsmState { let state = tool_data.update_insertion(shape_editor, document, responses, input); if let Some(closest_segment) = &tool_data.segment { - overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_YELLOW)); + overlay_context.manipulator_anchor(closest_segment.closest_point_to_viewport(), false, Some(COLOR_OVERLAY_BLUE)); + if let (Some(handle1), Some(handle2)) = closest_segment.handle_positions(document.metadata()) { + overlay_context.line(closest_segment.closest_point_to_viewport(), handle1, Some(COLOR_OVERLAY_BLUE)); + overlay_context.line(closest_segment.closest_point_to_viewport(), handle2, Some(COLOR_OVERLAY_BLUE)); + overlay_context.manipulator_handle(handle1, false, Some(COLOR_OVERLAY_BLUE)); + overlay_context.manipulator_handle(handle2, false, Some(COLOR_OVERLAY_BLUE)); + } } responses.add(PathToolMessage::SelectedPointUpdated); diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 1d2169ca..845276c0 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -602,12 +602,12 @@ impl Fsm for PenToolFsmState { if self == PenToolFsmState::DraggingHandle && valid(next_anchor, handle_end) { // Draw the handle circle for the currently-being-dragged-out incoming handle (opposite the one currently being dragged out) - overlay_context.manipulator_handle(handle_end, false); + overlay_context.manipulator_handle(handle_end, false, None); } if valid(anchor_start, handle_start) { // Draw the handle circle for the most recently placed anchor's outgoing handle (which is currently influencing the currently-being-placed segment) - overlay_context.manipulator_handle(handle_start, false); + overlay_context.manipulator_handle(handle_start, false, None); } } else { // Draw the whole path and its manipulators when the user is clicking-and-dragging out from the most recently placed anchor to set its outgoing handle, during which it would otherwise not have its overlays drawn @@ -616,7 +616,7 @@ impl Fsm for PenToolFsmState { if self == PenToolFsmState::DraggingHandle && valid(next_anchor, next_handle_start) { // Draw the handle circle for the currently-being-dragged-out outgoing handle (the one currently being dragged out, under the user's cursor) - overlay_context.manipulator_handle(next_handle_start, false); + overlay_context.manipulator_handle(next_handle_start, false, None); } if self == PenToolFsmState::DraggingHandle {