From 01add76671bea0ab038044ab30a8775689b37714 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Wed, 16 Apr 2025 17:50:09 +0530 Subject: [PATCH 1/5] almost_fixed --- .../tool/common_functionality/shape_editor.rs | 4 +- .../messages/tool/tool_messages/path_tool.rs | 168 +++++++++++++++++- node-graph/gcore/src/vector/vector_data.rs | 7 + 3 files changed, 175 insertions(+), 4 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index f434cdc079..31ce6ebc15 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -698,7 +698,9 @@ impl ShapeState { let length = transform.transform_vector2(unselected_position - anchor).length(); let position = transform.inverse().transform_vector2(direction * length); let modification_type = unselected_handle.set_relative_position(position); - responses.add(GraphOperationMessage::Vector { layer, modification_type }); + if (anchor - selected_position).length() > 1e-6 { + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } } // If both handles are selected, average the angles of the handles else { diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index c31855fae8..65d2772fd0 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -16,7 +16,7 @@ use crate::messages::tool::common_functionality::shape_editor::{ use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager}; use graphene_core::renderer::Quad; use graphene_core::vector::{ManipulatorPointId, PointId}; -use graphene_std::vector::{NoHashBuilder, SegmentId}; +use graphene_std::vector::{NoHashBuilder, SegmentId, VectorData}; use std::vec; #[derive(Default)] @@ -375,6 +375,8 @@ struct PathToolData { angle: f64, opposite_handle_position: Option, snapping_axis: Option, + angle_locked: bool, + temporary_colinear_handles: bool, } impl PathToolData { @@ -686,11 +688,40 @@ impl PathToolData { Some((handle_position_document, anchor_position_document, handle_id)) } - fn calculate_handle_angle(&mut self, handle_vector: DVec2, handle_id: ManipulatorPointId, lock_angle: bool, snap_angle: bool) -> f64 { + #[allow(clippy::too_many_arguments)] + fn calculate_handle_angle( + &mut self, + shape_editor: &mut ShapeState, + document: &DocumentMessageHandler, + responses: &mut VecDeque, + relative_vector: DVec2, + handle_vector: DVec2, + handle_id: ManipulatorPointId, + lock_angle: bool, + snap_angle: bool, + ) -> f64 { let current_angle = -handle_vector.angle_to(DVec2::X); + if let Some(vector_data) = shape_editor + .selected_shape_state + .iter() + .next() + .and_then(|(layer, _)| document.network_interface.compute_modified_vector(*layer)) + { + if relative_vector.length() < 25. && lock_angle && !self.angle_locked { + log::info!("reaching here"); + + if let Some(angle) = calculate_lock_angle(self, shape_editor, responses, document, &vector_data, handle_id) { + self.angle = angle; + return angle; + } + } + } + // When the angle is locked we use the old angle + if self.current_selected_handle_id == Some(handle_id) && lock_angle { + self.angle_locked = true; return self.angle; } @@ -814,7 +845,7 @@ impl PathToolData { let snapped_delta = if let Some((handle_pos, anchor_pos, handle_id)) = self.try_get_selected_handle_and_anchor(shape_editor, document) { let cursor_pos = handle_pos + raw_delta; - let handle_angle = self.calculate_handle_angle(cursor_pos - anchor_pos, handle_id, lock_angle, snap_angle); + let handle_angle = self.calculate_handle_angle(shape_editor, document, responses, handle_pos - anchor_pos, cursor_pos - anchor_pos, handle_id, lock_angle, snap_angle); let constrained_direction = DVec2::new(handle_angle.cos(), handle_angle.sin()); let projected_length = (cursor_pos - anchor_pos).dot(constrained_direction); @@ -826,11 +857,18 @@ impl PathToolData { shape_editor.snap(&mut self.snap_manager, &self.snap_cache, document, input, previous_mouse) }; + log::info!("{:?}", self.temporary_colinear_handles); + log::info!("reaching here"); + let handle_lengths = if equidistant { None } else { self.opposing_handle_lengths.take() }; let opposite = if lock_angle { None } else { self.opposite_handle_position }; let unsnapped_delta = current_mouse - previous_mouse; if self.snapping_axis.is_none() { + if self.temporary_colinear_handles && !lock_angle { + self.temporary_colinear_handles = false; + shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); + } shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, true, opposite, responses); self.previous_mouse_position += document_to_viewport.inverse().transform_vector2(snapped_delta); } else { @@ -1129,6 +1167,15 @@ impl Fsm for PathToolFsmState { let lock_angle_state = input.keyboard.get(lock_angle as usize); let snap_angle_state = input.keyboard.get(snap_angle as usize); + if !lock_angle_state { + tool_data.angle_locked = false; + } + + if !lock_angle_state && tool_data.temporary_colinear_handles { + shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); + tool_data.temporary_colinear_handles = false; + } + if !tool_data.update_colinear(equidistant_state, toggle_colinear_state, tool_action_data.shape_editor, tool_action_data.document, responses) { tool_data.drag( equidistant_state, @@ -1620,3 +1667,118 @@ fn get_selection_status(network_interface: &NodeNetworkInterface, shape_state: & SelectionStatus::None } + +fn calculate_lock_angle( + tool_data: &mut PathToolData, + shape_state: &mut ShapeState, + responses: &mut VecDeque, + document: &DocumentMessageHandler, + vector_data: &VectorData, + handle_id: ManipulatorPointId, +) -> Option { + let anchor = handle_id.get_anchor(vector_data)?; + let anchor_position = vector_data.point_domain.position_from_id(anchor); + let current_segment = handle_id.get_segment(); + let points_connected = vector_data.connected_count(anchor); + log::info!("points_connected: {points_connected:?}"); + + let (anchor_position, segment) = anchor_position.zip(current_segment)?; + if points_connected == 1 { + calculate_segment_angle(anchor, segment, vector_data) + } else { + let opposite_handle = handle_id + .get_handle_pair(vector_data) + .iter() + .flatten() + .copied() + .find(|&h| h.to_manipulator_point() != handle_id) + .map(|h| h.to_manipulator_point()); + let opposite_handle_position = opposite_handle.and_then(|h| h.get_position(vector_data)).filter(|pos| (pos - anchor_position).length() > 1e-6); + + let is_start = |point: PointId, segment: SegmentId| vector_data.segment_start_from_id(segment) == Some(point); + if let Some(opposite_pos) = opposite_handle_position { + if !vector_data.colinear_manipulators.iter().flatten().map(|h| h.to_manipulator_point()).any(|h| h == handle_id) { + shape_state.convert_selected_manipulators_to_colinear_handles(responses, document); + tool_data.temporary_colinear_handles = true; + } + Some(-(opposite_pos - anchor_position).angle_to(DVec2::X)) + } else { + // This is the segment which is adjacent to the handle + let angle_1 = if let Some((_, adjacent_segment)) = vector_data.adjacent_segment(&handle_id) { + let end_point = if is_start(anchor, adjacent_segment) { + vector_data.segment_end_from_id(adjacent_segment).and_then(|id| vector_data.point_domain.position_from_id(id)) + } else { + vector_data.segment_start_from_id(adjacent_segment).and_then(|id| vector_data.point_domain.position_from_id(id)) + }; + + let required_handle = if is_start(anchor, adjacent_segment) { + ManipulatorPointId::EndHandle(adjacent_segment) + .get_position(vector_data) + .filter(|handle: &DVec2| Some(*handle) != end_point) + .or(end_point) + } else { + ManipulatorPointId::PrimaryHandle(adjacent_segment) + .get_position(vector_data) + .filter(|handle| Some(*handle) != end_point) + .or(end_point) + }; + + required_handle.map(|handle| -(handle - anchor_position).angle_to(DVec2::X)) + } else { + None + }; + + let angle_2 = { + let endpoint = if is_start(anchor, segment) { + vector_data.segment_end_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) + } else { + vector_data.segment_start_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) + }; + let opposite_handle = vector_data + .all_connected(anchor) + .filter(|handle| handle.to_manipulator_point() == handle_id) + .map(|handle| handle.opposite()) + .next() + .map(|handle_id| handle_id.to_manipulator_point()); + let required_handle = opposite_handle.and_then(|h| h.get_position(vector_data)).filter(|pos| Some(pos) != endpoint.as_ref()).or(endpoint); + + required_handle.map(|handle| -(handle - anchor_position).angle_to(DVec2::X)) + }; + + match (angle_1, angle_2) { + (Some(angle_1), Some(angle_2)) => Some((angle_1 + angle_2) / 2.0), + (Some(angle_1), None) => Some(angle_1), + (None, Some(angle_2)) => Some(angle_2), + (None, None) => None, + } + } + } +} + +fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data: &VectorData) -> Option { + let anchor_position = vector_data.point_domain.position_from_id(anchor)?; + let is_start = |point: PointId, segment: SegmentId| vector_data.segment_start_from_id(segment) == Some(point); + + let end_handle = ManipulatorPointId::EndHandle(segment).get_position(vector_data); + let start_handle = ManipulatorPointId::PrimaryHandle(segment).get_position(vector_data); + + let start_point = if is_start(anchor, segment) { + vector_data.segment_end_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) + } else { + vector_data.segment_start_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) + }; + + let required_handle = if is_start(anchor, segment) { + start_handle + .filter(|&handle| handle != anchor_position) + .or(end_handle.filter(|&handle| Some(handle) != start_point)) + .or(start_point) + } else { + end_handle + .filter(|&handle| handle != anchor_position) + .or(start_handle.filter(|&handle| Some(handle) != start_point)) + .or(start_point) + }; + + required_handle.map(|required_handle| -(required_handle - anchor_position).angle_to(DVec2::X)) +} diff --git a/node-graph/gcore/src/vector/vector_data.rs b/node-graph/gcore/src/vector/vector_data.rs index e2a701fba6..6e0ca86294 100644 --- a/node-graph/gcore/src/vector/vector_data.rs +++ b/node-graph/gcore/src/vector/vector_data.rs @@ -496,6 +496,13 @@ impl ManipulatorPointId { _ => None, } } + + pub fn get_segment(self) -> Option { + match self { + ManipulatorPointId::PrimaryHandle(segment) | ManipulatorPointId::EndHandle(segment) => Some(segment), + _ => None, + } + } } /// The type of handle found on a bézier curve. From 5e089f8003a71403d3904bdb0d7c6f028456cc6c Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Wed, 23 Apr 2025 03:52:38 +0530 Subject: [PATCH 2/5] fix need to refactor --- .../tool/common_functionality/shape_editor.rs | 6 ++++ .../messages/tool/tool_messages/path_tool.rs | 33 +++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 31ce6ebc15..d093847117 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -745,6 +745,7 @@ impl ShapeState { equidistant: bool, in_viewport_space: bool, opposite_handle_position: Option, + skip_opposite_handle: bool, responses: &mut VecDeque, ) { for (&layer, state) in &self.selected_shape_state { @@ -786,6 +787,11 @@ impl ShapeState { responses.add(GraphOperationMessage::Vector { layer, modification_type }); let Some(other) = vector_data.other_colinear_handle(handle) else { continue }; + + if skip_opposite_handle { + continue; + }; + if state.is_selected(other.to_manipulator_point()) { // If two colinear handles are being dragged at the same time but not the anchor, it is necessary to break the colinear state. let handles = [handle, other]; diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 65d2772fd0..56f2c1c10e 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -709,8 +709,6 @@ impl PathToolData { .and_then(|(layer, _)| document.network_interface.compute_modified_vector(*layer)) { if relative_vector.length() < 25. && lock_angle && !self.angle_locked { - log::info!("reaching here"); - if let Some(angle) = calculate_lock_angle(self, shape_editor, responses, document, &vector_data, handle_id) { self.angle = angle; return angle; @@ -775,7 +773,7 @@ impl PathToolData { let drag_start = self.drag_start_pos; let opposite_delta = drag_start - current_mouse; - shape_editor.move_selected_points(None, document, opposite_delta, false, true, None, responses); + shape_editor.move_selected_points(None, document, opposite_delta, false, true, None, false, responses); // Calculate the projected delta and shift the points along that delta let delta = current_mouse - drag_start; @@ -787,7 +785,7 @@ impl PathToolData { _ => DVec2::new(delta.x, 0.), }; - shape_editor.move_selected_points(None, document, projected_delta, false, true, None, responses); + shape_editor.move_selected_points(None, document, projected_delta, false, true, None, false, responses); } fn stop_snap_along_axis(&mut self, shape_editor: &mut ShapeState, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { @@ -803,12 +801,12 @@ impl PathToolData { _ => DVec2::new(opposite_delta.x, 0.), }; - shape_editor.move_selected_points(None, document, opposite_projected_delta, false, true, None, responses); + shape_editor.move_selected_points(None, document, opposite_projected_delta, false, true, None, false, responses); // Calculate what actually would have been the original delta for the point, and apply that let delta = current_mouse - drag_start; - shape_editor.move_selected_points(None, document, delta, false, true, None, responses); + shape_editor.move_selected_points(None, document, delta, false, true, None, false, responses); self.snapping_axis = None; } @@ -857,19 +855,18 @@ impl PathToolData { shape_editor.snap(&mut self.snap_manager, &self.snap_cache, document, input, previous_mouse) }; - log::info!("{:?}", self.temporary_colinear_handles); - log::info!("reaching here"); - let handle_lengths = if equidistant { None } else { self.opposing_handle_lengths.take() }; let opposite = if lock_angle { None } else { self.opposite_handle_position }; let unsnapped_delta = current_mouse - previous_mouse; if self.snapping_axis.is_none() { + let mut skip_opposite = false; if self.temporary_colinear_handles && !lock_angle { - self.temporary_colinear_handles = false; shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); + self.temporary_colinear_handles = false; + skip_opposite = true; } - shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, true, opposite, responses); + shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, true, opposite, skip_opposite, responses); self.previous_mouse_position += document_to_viewport.inverse().transform_vector2(snapped_delta); } else { let Some(axis) = self.snapping_axis else { return }; @@ -878,7 +875,7 @@ impl PathToolData { Axis::Y => DVec2::new(0., unsnapped_delta.y), _ => DVec2::new(unsnapped_delta.x, 0.), }; - shape_editor.move_selected_points(handle_lengths, document, projected_delta, equidistant, true, opposite, responses); + shape_editor.move_selected_points(handle_lengths, document, projected_delta, equidistant, true, opposite, false, responses); self.previous_mouse_position += document_to_viewport.inverse().transform_vector2(unsnapped_delta); } @@ -1171,11 +1168,6 @@ impl Fsm for PathToolFsmState { tool_data.angle_locked = false; } - if !lock_angle_state && tool_data.temporary_colinear_handles { - shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); - tool_data.temporary_colinear_handles = false; - } - if !tool_data.update_colinear(equidistant_state, toggle_colinear_state, tool_action_data.shape_editor, tool_action_data.document, responses) { tool_data.drag( equidistant_state, @@ -1335,6 +1327,11 @@ impl Fsm for PathToolFsmState { } (_, PathToolMessage::DragStop { extend_selection, .. }) => { if tool_data.handle_drag_toggle && tool_data.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD { + // the handles which are temporarily made colinear when ctrl-dragging from the anchor need to revert back + if tool_data.temporary_colinear_handles { + shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); + tool_data.temporary_colinear_handles = false; + } shape_editor.deselect_all_points(); shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_handle_drag); @@ -1433,6 +1430,7 @@ impl Fsm for PathToolFsmState { true, false, tool_data.opposite_handle_position, + false, responses, ); @@ -1680,7 +1678,6 @@ fn calculate_lock_angle( let anchor_position = vector_data.point_domain.position_from_id(anchor); let current_segment = handle_id.get_segment(); let points_connected = vector_data.connected_count(anchor); - log::info!("points_connected: {points_connected:?}"); let (anchor_position, segment) = anchor_position.zip(current_segment)?; if points_connected == 1 { From 417e80531e15508c653be4796c4bb3bb9e217e0b Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Wed, 23 Apr 2025 04:41:53 +0530 Subject: [PATCH 3/5] fixed issed need to refactor --- .../messages/tool/tool_messages/path_tool.rs | 82 +++++++++---------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index c72f36fecc..53c23657a3 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -17,7 +17,7 @@ use crate::messages::tool::common_functionality::shape_editor::{ use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager}; use graphene_core::renderer::Quad; use graphene_core::vector::{ManipulatorPointId, PointId, VectorModificationType}; -use graphene_std::vector::{HandleId, NoHashBuilder, SegmentId, VectorData, VectorData}; +use graphene_std::vector::{HandleId, NoHashBuilder, SegmentId, VectorData}; use std::vec; #[derive(Default)] @@ -967,7 +967,7 @@ impl PathToolData { self.temporary_colinear_handles = false; skip_opposite = true; } - shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, true, opposite, opposite, skip_opposite, responses); + shape_editor.move_selected_points(handle_lengths, document, snapped_delta, equidistant, true, was_alt_dragging, opposite, skip_opposite, responses); self.previous_mouse_position += document_to_viewport.inverse().transform_vector2(snapped_delta); } else { let Some(axis) = self.snapping_axis else { return }; @@ -1453,55 +1453,53 @@ impl Fsm for PathToolFsmState { } if tool_data.handle_drag_toggle && drag_occurred { - if tool_data.handle_drag_toggle && tool_data.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD { - // the handles which are temporarily made colinear when ctrl-dragging from the anchor need to revert back - if tool_data.temporary_colinear_handles { - shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); - tool_data.temporary_colinear_handles = false; - } - shape_editor.deselect_all_points(); - shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_handle_drag); - - tool_data.saved_points_before_handle_drag.clear(); - tool_data.handle_drag_toggle = false; + // the handles which are temporarily made colinear when ctrl-dragging from the anchor need to revert back + if tool_data.temporary_colinear_handles { + shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); + tool_data.temporary_colinear_handles = false; } + shape_editor.deselect_all_points(); + shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_handle_drag); - tool_data.alt_dragging_from_anchor = false; - tool_data.alt_clicked_on_anchor = false; + tool_data.saved_points_before_handle_drag.clear(); + tool_data.handle_drag_toggle = false; + } - if tool_data.select_anchor_toggled { - shape_editor.deselect_all_points(); - shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_anchor_select_toggle); - tool_data.remove_saved_points(); - tool_data.select_anchor_toggled = false; - } + tool_data.alt_dragging_from_anchor = false; + tool_data.alt_clicked_on_anchor = false; - if let Some((layer, nearest_point)) = nearest_point { - if !drag_occurred && !extend_selection { - let clicked_selected = shape_editor.selected_points().any(|&point| nearest_point == point); - if clicked_selected { - shape_editor.deselect_all_points(); - shape_editor.selected_shape_state.entry(layer).or_default().select_point(nearest_point); - responses.add(OverlaysMessage::Draw); - } + if tool_data.select_anchor_toggled { + shape_editor.deselect_all_points(); + shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_anchor_select_toggle); + tool_data.remove_saved_points(); + tool_data.select_anchor_toggled = false; + } + + if let Some((layer, nearest_point)) = nearest_point { + if !drag_occurred && !extend_selection { + let clicked_selected = shape_editor.selected_points().any(|&point| nearest_point == point); + if clicked_selected { + shape_editor.deselect_all_points(); + shape_editor.selected_shape_state.entry(layer).or_default().select_point(nearest_point); + responses.add(OverlaysMessage::Draw); } } - // Deselect all points if the user clicks the filled region of the shape - else if tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD { - shape_editor.deselect_all_points(); - } + } + // Deselect all points if the user clicks the filled region of the shape + else if tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD { + shape_editor.deselect_all_points(); + } - if tool_data.snapping_axis.is_some() { - tool_data.snapping_axis = None; - } + if tool_data.snapping_axis.is_some() { + tool_data.snapping_axis = None; + } - responses.add(DocumentMessage::EndTransaction); - responses.add(PathToolMessage::SelectedPointUpdated); - tool_data.snap_manager.cleanup(responses); - tool_data.opposite_handle_position = None; + responses.add(DocumentMessage::EndTransaction); + responses.add(PathToolMessage::SelectedPointUpdated); + tool_data.snap_manager.cleanup(responses); + tool_data.opposite_handle_position = None; - PathToolFsmState::Ready - } + PathToolFsmState::Ready } // Delete key From 2aa0ea77a1f84b684cba9205116717875ecd0155 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Wed, 23 Apr 2025 13:48:05 +0530 Subject: [PATCH 4/5] refactor-done fixed issue --- .../messages/tool/tool_messages/path_tool.rs | 66 ++++--------------- 1 file changed, 11 insertions(+), 55 deletions(-) diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 53c23657a3..cba594ab6f 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -1452,12 +1452,11 @@ impl Fsm for PathToolFsmState { } } + if tool_data.temporary_colinear_handles { + tool_data.temporary_colinear_handles = false; + } + if tool_data.handle_drag_toggle && drag_occurred { - // the handles which are temporarily made colinear when ctrl-dragging from the anchor need to revert back - if tool_data.temporary_colinear_handles { - shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); - tool_data.temporary_colinear_handles = false; - } shape_editor.deselect_all_points(); shape_editor.select_points_by_manipulator_id(&tool_data.saved_points_before_handle_drag); @@ -1817,12 +1816,11 @@ fn calculate_lock_angle( .get_handle_pair(vector_data) .iter() .flatten() - .copied() .find(|&h| h.to_manipulator_point() != handle_id) + .copied() .map(|h| h.to_manipulator_point()); let opposite_handle_position = opposite_handle.and_then(|h| h.get_position(vector_data)).filter(|pos| (pos - anchor_position).length() > 1e-6); - let is_start = |point: PointId, segment: SegmentId| vector_data.segment_start_from_id(segment) == Some(point); if let Some(opposite_pos) = opposite_handle_position { if !vector_data.colinear_manipulators.iter().flatten().map(|h| h.to_manipulator_point()).any(|h| h == handle_id) { shape_state.convert_selected_manipulators_to_colinear_handles(responses, document); @@ -1830,47 +1828,11 @@ fn calculate_lock_angle( } Some(-(opposite_pos - anchor_position).angle_to(DVec2::X)) } else { - // This is the segment which is adjacent to the handle - let angle_1 = if let Some((_, adjacent_segment)) = vector_data.adjacent_segment(&handle_id) { - let end_point = if is_start(anchor, adjacent_segment) { - vector_data.segment_end_from_id(adjacent_segment).and_then(|id| vector_data.point_domain.position_from_id(id)) - } else { - vector_data.segment_start_from_id(adjacent_segment).and_then(|id| vector_data.point_domain.position_from_id(id)) - }; - - let required_handle = if is_start(anchor, adjacent_segment) { - ManipulatorPointId::EndHandle(adjacent_segment) - .get_position(vector_data) - .filter(|handle: &DVec2| Some(*handle) != end_point) - .or(end_point) - } else { - ManipulatorPointId::PrimaryHandle(adjacent_segment) - .get_position(vector_data) - .filter(|handle| Some(*handle) != end_point) - .or(end_point) - }; - - required_handle.map(|handle| -(handle - anchor_position).angle_to(DVec2::X)) - } else { - None - }; + let angle_1 = vector_data + .adjacent_segment(&handle_id) + .and_then(|(_, adjacent_segment)| calculate_segment_angle(anchor, adjacent_segment, vector_data)); - let angle_2 = { - let endpoint = if is_start(anchor, segment) { - vector_data.segment_end_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) - } else { - vector_data.segment_start_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) - }; - let opposite_handle = vector_data - .all_connected(anchor) - .filter(|handle| handle.to_manipulator_point() == handle_id) - .map(|handle| handle.opposite()) - .next() - .map(|handle_id| handle_id.to_manipulator_point()); - let required_handle = opposite_handle.and_then(|h| h.get_position(vector_data)).filter(|pos| Some(pos) != endpoint.as_ref()).or(endpoint); - - required_handle.map(|handle| -(handle - anchor_position).angle_to(DVec2::X)) - }; + let angle_2 = calculate_segment_angle(anchor, segment, vector_data); match (angle_1, angle_2) { (Some(angle_1), Some(angle_2)) => Some((angle_1 + angle_2) / 2.0), @@ -1896,15 +1858,9 @@ fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data: &Ve }; let required_handle = if is_start(anchor, segment) { - start_handle - .filter(|&handle| handle != anchor_position) - .or(end_handle.filter(|&handle| Some(handle) != start_point)) - .or(start_point) + end_handle.filter(|&handle| Some(handle) != start_point).or(start_point) } else { - end_handle - .filter(|&handle| handle != anchor_position) - .or(start_handle.filter(|&handle| Some(handle) != start_point)) - .or(start_point) + start_handle.filter(|&handle| Some(handle) != start_point).or(start_point) }; required_handle.map(|required_handle| -(required_handle - anchor_position).angle_to(DVec2::X)) From 557271200423251bd21134709daab9166e5291d3 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Thu, 24 Apr 2025 02:32:28 +0530 Subject: [PATCH 5/5] move function to common_functionality --- .../tool/common_functionality/shape_editor.rs | 2 +- .../common_functionality/utility_functions.rs | 29 ++++++++++++++++- .../messages/tool/tool_messages/path_tool.rs | 29 +++-------------- .../messages/tool/tool_messages/pen_tool.rs | 31 ++----------------- 4 files changed, 36 insertions(+), 55 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 4d8eae88ff..db61db5221 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -822,7 +822,7 @@ impl ShapeState { if skip_opposite_handle { continue; - }; + } if state.is_selected(other.to_manipulator_point()) { // If two colinear handles are being dragged at the same time but not the anchor, it is necessary to break the colinear state. diff --git a/editor/src/messages/tool/common_functionality/utility_functions.rs b/editor/src/messages/tool/common_functionality/utility_functions.rs index 9e0b2700b4..47800a26b7 100644 --- a/editor/src/messages/tool/common_functionality/utility_functions.rs +++ b/editor/src/messages/tool/common_functionality/utility_functions.rs @@ -4,7 +4,7 @@ use crate::messages::tool::common_functionality::graph_modification_utils::get_t use glam::DVec2; use graphene_core::renderer::Quad; use graphene_core::text::{FontCache, load_face}; -use graphene_std::vector::PointId; +use graphene_std::vector::{ManipulatorPointId, PointId, SegmentId, VectorData}; /// Determines if a path should be extended. Goal in viewport space. Returns the path and if it is extending from the start, if applicable. pub fn should_extend( @@ -66,3 +66,30 @@ pub fn text_bounding_box(layer: LayerNodeIdentifier, document: &DocumentMessageH Quad::from_box([DVec2::ZERO, far]) } + +pub fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data: &VectorData, pen_tool: bool) -> Option { + let is_start = |point: PointId, segment: SegmentId| vector_data.segment_start_from_id(segment) == Some(point); + let anchor_position = vector_data.point_domain.position_from_id(anchor)?; + let end_handle = ManipulatorPointId::EndHandle(segment).get_position(vector_data); + let start_handle = ManipulatorPointId::PrimaryHandle(segment).get_position(vector_data); + + let start_point = if is_start(anchor, segment) { + vector_data.segment_end_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) + } else { + vector_data.segment_start_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) + }; + + let required_handle = if is_start(anchor, segment) { + start_handle + .filter(|&handle| pen_tool && handle != anchor_position) + .or(end_handle.filter(|&handle| Some(handle) != start_point)) + .or(start_point) + } else { + end_handle + .filter(|&handle| pen_tool && handle != anchor_position) + .or(start_handle.filter(|&handle| Some(handle) != start_point)) + .or(start_point) + }; + + required_handle.map(|handle| -(handle - anchor_position).angle_to(DVec2::X)) +} diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index cba594ab6f..ae94679d45 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -15,6 +15,7 @@ use crate::messages::tool::common_functionality::shape_editor::{ ClosestSegment, ManipulatorAngle, OpposingHandleLengths, SelectedPointsInfo, SelectionChange, SelectionShape, SelectionShapeType, ShapeState, }; use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager}; +use crate::messages::tool::common_functionality::utility_functions::calculate_segment_angle; use graphene_core::renderer::Quad; use graphene_core::vector::{ManipulatorPointId, PointId, VectorModificationType}; use graphene_std::vector::{HandleId, NoHashBuilder, SegmentId, VectorData}; @@ -1810,7 +1811,7 @@ fn calculate_lock_angle( let (anchor_position, segment) = anchor_position.zip(current_segment)?; if points_connected == 1 { - calculate_segment_angle(anchor, segment, vector_data) + calculate_segment_angle(anchor, segment, vector_data, false) } else { let opposite_handle = handle_id .get_handle_pair(vector_data) @@ -1830,9 +1831,9 @@ fn calculate_lock_angle( } else { let angle_1 = vector_data .adjacent_segment(&handle_id) - .and_then(|(_, adjacent_segment)| calculate_segment_angle(anchor, adjacent_segment, vector_data)); + .and_then(|(_, adjacent_segment)| calculate_segment_angle(anchor, adjacent_segment, vector_data, false)); - let angle_2 = calculate_segment_angle(anchor, segment, vector_data); + let angle_2 = calculate_segment_angle(anchor, segment, vector_data, false); match (angle_1, angle_2) { (Some(angle_1), Some(angle_2)) => Some((angle_1 + angle_2) / 2.0), @@ -1843,25 +1844,3 @@ fn calculate_lock_angle( } } } - -fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data: &VectorData) -> Option { - let anchor_position = vector_data.point_domain.position_from_id(anchor)?; - let is_start = |point: PointId, segment: SegmentId| vector_data.segment_start_from_id(segment) == Some(point); - - let end_handle = ManipulatorPointId::EndHandle(segment).get_position(vector_data); - let start_handle = ManipulatorPointId::PrimaryHandle(segment).get_position(vector_data); - - let start_point = if is_start(anchor, segment) { - vector_data.segment_end_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) - } else { - vector_data.segment_start_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) - }; - - let required_handle = if is_start(anchor, segment) { - end_handle.filter(|&handle| Some(handle) != start_point).or(start_point) - } else { - start_handle.filter(|&handle| Some(handle) != start_point).or(start_point) - }; - - required_handle.map(|required_handle| -(required_handle - anchor_position).angle_to(DVec2::X)) -} diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 3d14812187..f0edffe5ef 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -10,7 +10,7 @@ use crate::messages::tool::common_functionality::color_selector::{ToolColorOptio use crate::messages::tool::common_functionality::graph_modification_utils::{self, merge_layers}; use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration}; -use crate::messages::tool::common_functionality::utility_functions::{closest_point, should_extend}; +use crate::messages::tool::common_functionality::utility_functions::{calculate_segment_angle, closest_point, should_extend}; use bezier_rs::{Bezier, BezierHandles}; use graph_craft::document::NodeId; use graphene_core::Color; @@ -1291,31 +1291,8 @@ impl PenToolData { match (self.handle_type, self.path_closed) { (TargetHandle::FuturePreviewOutHandle, _) | (TargetHandle::PreviewInHandle, true) => { - let is_start = |point: PointId, segment: SegmentId| vector_data.segment_start_from_id(segment) == Some(point); - - let end_handle = ManipulatorPointId::EndHandle(segment).get_position(vector_data); - let start_handle = ManipulatorPointId::PrimaryHandle(segment).get_position(vector_data); - - let start_point = if is_start(anchor, segment) { - vector_data.segment_end_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) - } else { - vector_data.segment_start_from_id(segment).and_then(|id| vector_data.point_domain.position_from_id(id)) - }; - - let required_handle = if is_start(anchor, segment) { - start_handle - .filter(|&handle| handle != anchor_position) - .or(end_handle.filter(|&handle| Some(handle) != start_point)) - .or(start_point) - } else { - end_handle - .filter(|&handle| handle != anchor_position) - .or(start_handle.filter(|&handle| Some(handle) != start_point)) - .or(start_point) - }; - - if let Some(required_handle) = required_handle { - self.angle = -(required_handle - anchor_position).angle_to(DVec2::X); + if let Some(required_handle) = calculate_segment_angle(anchor, segment, vector_data, true) { + self.angle = required_handle; self.handle_mode = HandleMode::ColinearEquidistant; } } @@ -1328,8 +1305,6 @@ impl PenToolData { self.handle_mode = HandleMode::ColinearEquidistant; } } - - // Closure to check if a point is the start or end of a segment } fn add_point_layer_position(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque, layer: LayerNodeIdentifier, viewport: DVec2) {