From e022fbb4900853080fbf6268139abef1cb02b058 Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Fri, 25 Jul 2025 09:45:35 +0530 Subject: [PATCH 1/3] Fix colinear switch behaviour --- .../tool/common_functionality/shape_editor.rs | 63 ++++++++++++++++++- .../messages/tool/tool_messages/path_tool.rs | 8 ++- node-graph/gcore/src/vector/vector_data.rs | 17 +++++ 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index f98e50f079..363bdba293 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -1027,6 +1027,9 @@ impl ShapeState { /// If only one handle is selected, the other handle will be moved to match the angle of the selected handle. /// If both or neither handles are selected, the angle of both handles will be averaged from their current angles, weighted by their lengths. /// Assumes all selected manipulators have handles that are already not colinear. + /// + /// For vector meshes, the handle which is nearest in the direction of 180° angle separation, becomes colinear with current handle, + /// and if that handle was colinear with some other handle that constraint is removed from the vector data pub fn convert_selected_manipulators_to_colinear_handles(&self, responses: &mut VecDeque, document: &DocumentMessageHandler) { let mut skip_set = HashSet::new(); @@ -1036,8 +1039,64 @@ impl ShapeState { }; let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface); + log::info!("reaching hereeeeee"); + + // TODO: A point which has for &point in layer_state.selected_points.iter() { - let Some(handles) = point.get_handle_pair(&vector_data) else { continue }; + // Skip a point which has more than 2 segments connected (vector meshes) + if let ManipulatorPointId::Anchor(anchor) = point { + if vector_data.all_connected(anchor).count() > 2 { + continue; + } + } + + // let Some(handles) = point.get_handle_pair(&vector_data) else { continue }; + + // Here we take handles as the current handle and the most opposite non-colinear-handle + + let is_handle_non_colinear = |handle: HandleId| -> bool { !(vector_data.colinear_manipulators.iter().any(|&handles| handles[0] == handle || handles[1] == handle)) }; + + log::info!("reaching here"); + let other_handles = match point { + ManipulatorPointId::Anchor(_) => point.get_handle_pair(&vector_data), + _ => { + // something + point.get_all_connected_handles(&vector_data).and_then(|handles| { + let mut non_colinear_handles = handles.iter().filter(|&handle| is_handle_non_colinear(*handle)).clone().collect::>(); + + // Now sort these by angle from the current handle + non_colinear_handles.sort_by(|&handle_a, &handle_b| { + let anchor = point.get_anchor_position(&vector_data).expect("No anchor position for handle"); + let orig_handle_pos = point.get_position(&vector_data).expect("No handle position"); + + let a_pos = handle_a.to_manipulator_point().get_position(&vector_data).expect("No handle position"); + let b_pos = handle_b.to_manipulator_point().get_position(&vector_data).expect("No handle position"); + + let v_orig = (orig_handle_pos - anchor).normalize_or_zero(); + + let v_a = (a_pos - anchor).normalize_or_zero(); + let v_b = (b_pos - anchor).normalize_or_zero(); + + let angle_a = v_orig.angle_to(v_a).abs(); + let angle_b = v_orig.angle_to(v_b).abs(); + + // Sort by descending angle (180° is farthest) + angle_b.partial_cmp(&angle_a).unwrap_or(std::cmp::Ordering::Equal) + }); + + let current = match point { + ManipulatorPointId::EndHandle(segment) => HandleId::end(segment), + ManipulatorPointId::PrimaryHandle(segment) => HandleId::primary(segment), + ManipulatorPointId::Anchor(_) => unreachable!(), + }; + + if let Some(other) = non_colinear_handles.iter().next() { Some([current, **other]) } else { None } + }) + } + }; + + let Some(handles) = other_handles else { continue }; + if skip_set.contains(&handles) || skip_set.contains(&[handles[1], handles[0]]) { continue; }; @@ -1317,7 +1376,7 @@ impl ShapeState { match point { ManipulatorPointId::Anchor(anchor) => { if let Some(handles) = Self::dissolve_anchor(anchor, responses, layer, &vector_data) { - if !vector_data.all_connected(anchor).any(|a| selected_segments.contains(&a.segment)) { + if !vector_data.all_connected(anchor).any(|a| selected_segments.contains(&a.segment)) && vector_data.all_connected(anchor).count() <= 2 { missing_anchors.insert(anchor, handles); } } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index f5ebc33189..c0cc43de6b 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -583,7 +583,12 @@ impl PathToolData { SelectionStatus::None => false, SelectionStatus::One(single_selected_point) => { let vector_data = document.network_interface.compute_modified_vector(single_selected_point.layer).unwrap(); - single_selected_point.id.get_handle_pair(&vector_data).is_some() + if single_selected_point.id.get_handle_pair(&vector_data).is_some() { + let anchor = single_selected_point.id.get_anchor(&vector_data).expect("Cannot find connected anchor"); + if vector_data.all_connected(anchor).count() > 2 { false } else { true } + } else { + false + } } SelectionStatus::Multiple(_) => true, }; @@ -886,6 +891,7 @@ impl PathToolData { if is_colinear { shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); } else { + // Convert to colinear only if shape_editor.convert_selected_manipulators_to_colinear_handles(responses, document); } self.toggle_colinear_debounce = true; diff --git a/node-graph/gcore/src/vector/vector_data.rs b/node-graph/gcore/src/vector/vector_data.rs index 3523784545..b5c4abead6 100644 --- a/node-graph/gcore/src/vector/vector_data.rs +++ b/node-graph/gcore/src/vector/vector_data.rs @@ -561,6 +561,23 @@ impl ManipulatorPointId { } } + /// Finds all the connected handles of a point. For an anchor it is all the connected handles. For a handle it is all the handles connected to its corresponding anchor other than current handle + pub fn get_all_connected_handles(self, vector_data: &VectorData) -> Option> { + match self { + ManipulatorPointId::Anchor(point) => Some(vector_data.all_connected(point).collect::>()), + ManipulatorPointId::PrimaryHandle(segment) => { + let point = vector_data.segment_domain.segment_start_from_id(segment)?; + let current = HandleId::primary(segment); + Some(vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::>()) + } + ManipulatorPointId::EndHandle(segment) => { + let point = vector_data.segment_domain.segment_end_from_id(segment)?; + let current = HandleId::primary(segment); + Some(vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::>()) + } + } + } + /// Attempt to find the closest anchor. If self is already an anchor then it is just self. If it is a start or end handle, then the start or end point is chosen. #[must_use] pub fn get_anchor(self, vector_data: &VectorData) -> Option { From 1dc87e4c900f5170f0f4c44ba06a6ff5fd2af0cd Mon Sep 17 00:00:00 2001 From: Adesh Gupta Date: Sat, 26 Jul 2025 18:47:54 +0530 Subject: [PATCH 2/3] Fix Tab to switch handles --- .../tool/common_functionality/shape_editor.rs | 76 +++++++++++++------ .../messages/tool/tool_messages/path_tool.rs | 1 - node-graph/gcore/src/vector/vector_data.rs | 2 +- 3 files changed, 55 insertions(+), 24 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 363bdba293..820ed385a6 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -1,3 +1,5 @@ +use std::f64::consts::PI; + use super::graph_modification_utils::merge_layers; use super::snapping::{SnapCache, SnapCandidatePoint, SnapData, SnapManager, SnappedPoint}; use super::utility_functions::{adjust_handle_colinearity, calculate_bezier_bbox, calculate_segment_angle, restore_g1_continuity, restore_previous_handle_position}; @@ -892,16 +894,20 @@ impl ShapeState { ManipulatorPointId::Anchor(point) => self.move_anchor(point, &vector_data, delta, layer, None, responses), ManipulatorPointId::PrimaryHandle(segment) => { self.move_primary(segment, delta, layer, responses); - if let Some(handles) = point.get_handle_pair(&vector_data) { - let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false }; - responses.add(GraphOperationMessage::Vector { layer, modification_type }); + if let Some(handle) = point.as_handle() { + if let Some(handles) = vector_data.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) { + let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } } } ManipulatorPointId::EndHandle(segment) => { self.move_end(segment, delta, layer, responses); - if let Some(handles) = point.get_handle_pair(&vector_data) { - let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false }; - responses.add(GraphOperationMessage::Vector { layer, modification_type }); + if let Some(handle) = point.as_handle() { + if let Some(handles) = vector_data.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) { + let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } } } } @@ -1028,8 +1034,8 @@ impl ShapeState { /// If both or neither handles are selected, the angle of both handles will be averaged from their current angles, weighted by their lengths. /// Assumes all selected manipulators have handles that are already not colinear. /// - /// For vector meshes, the handle which is nearest in the direction of 180° angle separation, becomes colinear with current handle, - /// and if that handle was colinear with some other handle that constraint is removed from the vector data + /// For vector meshes, non colinear handle which is nearest in the direction of 180° angle separation, becomes colinear with current handle, + /// if there is no such handle, nothing happens pub fn convert_selected_manipulators_to_colinear_handles(&self, responses: &mut VecDeque, document: &DocumentMessageHandler) { let mut skip_set = HashSet::new(); @@ -1039,9 +1045,6 @@ impl ShapeState { }; let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface); - log::info!("reaching hereeeeee"); - - // TODO: A point which has for &point in layer_state.selected_points.iter() { // Skip a point which has more than 2 segments connected (vector meshes) if let ManipulatorPointId::Anchor(anchor) = point { @@ -1050,21 +1053,17 @@ impl ShapeState { } } - // let Some(handles) = point.get_handle_pair(&vector_data) else { continue }; - // Here we take handles as the current handle and the most opposite non-colinear-handle let is_handle_non_colinear = |handle: HandleId| -> bool { !(vector_data.colinear_manipulators.iter().any(|&handles| handles[0] == handle || handles[1] == handle)) }; - log::info!("reaching here"); let other_handles = match point { ManipulatorPointId::Anchor(_) => point.get_handle_pair(&vector_data), _ => { - // something point.get_all_connected_handles(&vector_data).and_then(|handles| { let mut non_colinear_handles = handles.iter().filter(|&handle| is_handle_non_colinear(*handle)).clone().collect::>(); - // Now sort these by angle from the current handle + // Sort these by angle from the current handle non_colinear_handles.sort_by(|&handle_a, &handle_b| { let anchor = point.get_anchor_position(&vector_data).expect("No anchor position for handle"); let orig_handle_pos = point.get_position(&vector_data).expect("No handle position"); @@ -1567,9 +1566,11 @@ impl ShapeState { responses.add(GraphOperationMessage::Vector { layer, modification_type }); } } - } else if let Some(handles) = point.get_handle_pair(&vector_data) { - let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false }; - responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } else if let Some(handle) = point.as_handle() { + if let Some(handles) = vector_data.colinear_manipulators.iter().find(|handles| handles[0] == handle || handles[1] == handle) { + let modification_type = VectorModificationType::SetG1Continuous { handles: *handles, enabled: false }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } } } } @@ -1779,9 +1780,40 @@ impl ShapeState { if point.as_anchor().is_some() { continue; } - if let Some(handles) = point.get_handle_pair(&vector_data) { - // handle[0] is selected, handle[1] is opposite / mirror handle - handles_to_update.push((layer, handles[0].to_manipulator_point(), handles[1].to_manipulator_point())); + + if let Some(other_handles) = point.get_all_connected_handles(&vector_data) { + // Find the next closest handle in the clockwise sense + let mut candidates = other_handles.clone(); + candidates.sort_by(|&handle_a, &handle_b| { + let anchor = point.get_anchor_position(&vector_data).expect("No anchor position for handle"); + let orig_handle_pos = point.get_position(&vector_data).expect("No handle position"); + + let a_pos = handle_a.to_manipulator_point().get_position(&vector_data).expect("No handle position"); + let b_pos = handle_b.to_manipulator_point().get_position(&vector_data).expect("No handle position"); + + let v_orig = (orig_handle_pos - anchor).normalize_or_zero(); + + let v_a = (a_pos - anchor).normalize_or_zero(); + let v_b = (b_pos - anchor).normalize_or_zero(); + + let signed_angle = |base: DVec2, to: DVec2| -> f64 { + let angle = base.angle_to(to); + let cross = base.perp_dot(to); + + if cross < 0.0 { 2.0 * PI - angle } else { angle } + }; + + let angle_a = signed_angle(v_orig, v_a); + let angle_b = signed_angle(v_orig, v_b); + + angle_a.partial_cmp(&angle_b).unwrap_or(std::cmp::Ordering::Equal) + }); + + if candidates.is_empty() { + continue; + } + + handles_to_update.push((layer, *point, candidates[0].to_manipulator_point())); } } } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index c0cc43de6b..3643ae1f16 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -891,7 +891,6 @@ impl PathToolData { if is_colinear { shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses); } else { - // Convert to colinear only if shape_editor.convert_selected_manipulators_to_colinear_handles(responses, document); } self.toggle_colinear_debounce = true; diff --git a/node-graph/gcore/src/vector/vector_data.rs b/node-graph/gcore/src/vector/vector_data.rs index b5c4abead6..2718a4482d 100644 --- a/node-graph/gcore/src/vector/vector_data.rs +++ b/node-graph/gcore/src/vector/vector_data.rs @@ -572,7 +572,7 @@ impl ManipulatorPointId { } ManipulatorPointId::EndHandle(segment) => { let point = vector_data.segment_domain.segment_end_from_id(segment)?; - let current = HandleId::primary(segment); + let current = HandleId::end(segment); Some(vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::>()) } } From 9ec4cad3bad17acbee0ca1a9f6562f995ee6cdbf Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sat, 26 Jul 2025 17:05:37 -0700 Subject: [PATCH 3/3] Code review --- .../tool/common_functionality/shape_editor.rs | 70 +++++++++---------- .../messages/tool/tool_messages/path_tool.rs | 12 ++-- node-graph/gcore/src/vector/vector_data.rs | 15 ++-- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 8bad9e5f24..b45c5fcd11 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -1,5 +1,3 @@ -use std::f64::consts::PI; - use super::graph_modification_utils::merge_layers; use super::snapping::{SnapCache, SnapCandidatePoint, SnapData, SnapManager, SnappedPoint}; use super::utility_functions::{adjust_handle_colinearity, calculate_bezier_bbox, calculate_segment_angle, restore_g1_continuity, restore_previous_handle_position}; @@ -17,6 +15,7 @@ use bezier_rs::{Bezier, BezierHandles, Subpath, TValue}; use glam::{DAffine2, DVec2}; use graphene_std::vector::{HandleExt, HandleId, SegmentId}; use graphene_std::vector::{ManipulatorPointId, PointId, VectorData, VectorModificationType}; +use std::f64::consts::TAU; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum SelectionChange { @@ -1034,8 +1033,8 @@ impl ShapeState { /// If both or neither handles are selected, the angle of both handles will be averaged from their current angles, weighted by their lengths. /// Assumes all selected manipulators have handles that are already not colinear. /// - /// For vector meshes, non colinear handle which is nearest in the direction of 180° angle separation, becomes colinear with current handle, - /// if there is no such handle, nothing happens + /// For vector meshes, the non-colinear handle which is nearest in the direction of 180° angle separation becomes colinear with current handle. + /// If there is no such handle, nothing happens. pub fn convert_selected_manipulators_to_colinear_handles(&self, responses: &mut VecDeque, document: &DocumentMessageHandler) { let mut skip_set = HashSet::new(); @@ -1055,43 +1054,42 @@ impl ShapeState { // Here we take handles as the current handle and the most opposite non-colinear-handle - let is_handle_non_colinear = |handle: HandleId| -> bool { !(vector_data.colinear_manipulators.iter().any(|&handles| handles[0] == handle || handles[1] == handle)) }; + let is_handle_colinear = |handle: HandleId| -> bool { vector_data.colinear_manipulators.iter().any(|&handles| handles[0] == handle || handles[1] == handle) }; - let other_handles = match point { - ManipulatorPointId::Anchor(_) => point.get_handle_pair(&vector_data), - _ => { - point.get_all_connected_handles(&vector_data).and_then(|handles| { - let mut non_colinear_handles = handles.iter().filter(|&handle| is_handle_non_colinear(*handle)).clone().collect::>(); + let other_handles = if matches!(point, ManipulatorPointId::Anchor(_)) { + point.get_handle_pair(&vector_data) + } else { + point.get_all_connected_handles(&vector_data).and_then(|handles| { + let mut non_colinear_handles = handles.iter().filter(|&handle| !is_handle_colinear(*handle)).clone().collect::>(); - // Sort these by angle from the current handle - non_colinear_handles.sort_by(|&handle_a, &handle_b| { - let anchor = point.get_anchor_position(&vector_data).expect("No anchor position for handle"); - let orig_handle_pos = point.get_position(&vector_data).expect("No handle position"); + // Sort these by angle from the current handle + non_colinear_handles.sort_by(|&handle_a, &handle_b| { + let anchor = point.get_anchor_position(&vector_data).expect("No anchor position for handle"); + let orig_handle_pos = point.get_position(&vector_data).expect("No handle position"); - let a_pos = handle_a.to_manipulator_point().get_position(&vector_data).expect("No handle position"); - let b_pos = handle_b.to_manipulator_point().get_position(&vector_data).expect("No handle position"); + let a_pos = handle_a.to_manipulator_point().get_position(&vector_data).expect("No handle position"); + let b_pos = handle_b.to_manipulator_point().get_position(&vector_data).expect("No handle position"); - let v_orig = (orig_handle_pos - anchor).normalize_or_zero(); + let v_orig = (orig_handle_pos - anchor).normalize_or_zero(); - let v_a = (a_pos - anchor).normalize_or_zero(); - let v_b = (b_pos - anchor).normalize_or_zero(); + let v_a = (a_pos - anchor).normalize_or_zero(); + let v_b = (b_pos - anchor).normalize_or_zero(); - let angle_a = v_orig.angle_to(v_a).abs(); - let angle_b = v_orig.angle_to(v_b).abs(); + let angle_a = v_orig.angle_to(v_a).abs(); + let angle_b = v_orig.angle_to(v_b).abs(); - // Sort by descending angle (180° is farthest) - angle_b.partial_cmp(&angle_a).unwrap_or(std::cmp::Ordering::Equal) - }); + // Sort by descending angle (180° is furthest) + angle_b.partial_cmp(&angle_a).unwrap_or(std::cmp::Ordering::Equal) + }); - let current = match point { - ManipulatorPointId::EndHandle(segment) => HandleId::end(segment), - ManipulatorPointId::PrimaryHandle(segment) => HandleId::primary(segment), - ManipulatorPointId::Anchor(_) => unreachable!(), - }; + let current = match point { + ManipulatorPointId::EndHandle(segment) => HandleId::end(segment), + ManipulatorPointId::PrimaryHandle(segment) => HandleId::primary(segment), + ManipulatorPointId::Anchor(_) => unreachable!(), + }; - if let Some(other) = non_colinear_handles.iter().next() { Some([current, **other]) } else { None } - }) - } + non_colinear_handles.first().map(|other| [current, **other]) + }) }; let Some(handles) = other_handles else { continue }; @@ -1554,14 +1552,12 @@ impl ShapeState { /// Disable colinear handles colinear. pub fn disable_colinear_handles_state_on_selected(&self, network_interface: &NodeNetworkInterface, responses: &mut VecDeque) { for (&layer, state) in &self.selected_shape_state { - let Some(vector_data) = network_interface.compute_modified_vector(layer) else { - continue; - }; + let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue }; for &point in &state.selected_points { if let ManipulatorPointId::Anchor(point) = point { for connected in vector_data.all_connected(point) { - if let Some(&handles) = vector_data.colinear_manipulators.iter().find(|target| target.iter().any(|&target| target == connected)) { + if let Some(&handles) = vector_data.colinear_manipulators.iter().find(|target| target.contains(&connected)) { let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false }; responses.add(GraphOperationMessage::Vector { layer, modification_type }); } @@ -1800,7 +1796,7 @@ impl ShapeState { let angle = base.angle_to(to); let cross = base.perp_dot(to); - if cross < 0.0 { 2.0 * PI - angle } else { angle } + if cross < 0. { TAU - angle } else { angle } }; let angle_a = signed_angle(v_orig, v_a); diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 49347bd106..e04b75d3f4 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -585,7 +585,7 @@ impl PathToolData { let vector_data = document.network_interface.compute_modified_vector(single_selected_point.layer).unwrap(); if single_selected_point.id.get_handle_pair(&vector_data).is_some() { let anchor = single_selected_point.id.get_anchor(&vector_data).expect("Cannot find connected anchor"); - if vector_data.all_connected(anchor).count() > 2 { false } else { true } + vector_data.all_connected(anchor).count() <= 2 } else { false } @@ -605,7 +605,7 @@ impl PathToolData { } fn next_drill_through_cycle(&mut self, position: DVec2) -> usize { - if self.last_drill_through_click_position.map_or(true, |last_pos| last_pos.distance(position) > DRILL_THROUGH_THRESHOLD) { + if self.last_drill_through_click_position.is_none_or(|last_pos| last_pos.distance(position) > DRILL_THROUGH_THRESHOLD) { // New position, reset cycle self.drill_through_cycle_index = 0; } else { @@ -625,7 +625,7 @@ impl PathToolData { } fn has_drill_through_mouse_moved(&self, position: DVec2) -> bool { - self.last_drill_through_click_position.map_or(true, |last_pos| last_pos.distance(position) > DRILL_THROUGH_THRESHOLD) + self.last_drill_through_click_position.is_none_or(|last_pos| last_pos.distance(position) > DRILL_THROUGH_THRESHOLD) } fn set_ghost_outline(&mut self, shape_editor: &ShapeState, document: &DocumentMessageHandler) { @@ -2411,15 +2411,15 @@ impl Fsm for PathToolFsmState { let compatible_type = first_layer.and_then(|layer| { let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &document.network_interface); - graph_layer.horizontal_layer_flow().nth(1).and_then(|node_id| { + graph_layer.horizontal_layer_flow().nth(1).map(|node_id| { let (output_type, _) = document.network_interface.output_type(&node_id, 0, &[]); - Some(format!("type:{}", output_type.nested_type())) + format!("type:{}", output_type.nested_type()) }) }); let is_compatible = compatible_type.as_deref() == Some("type:Instances"); - let is_modifiable = first_layer.map_or(false, |layer| { + let is_modifiable = first_layer.is_some_and(|layer| { let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &document.network_interface); matches!(graph_layer.find_input("Path", 1), Some(TaggedValue::VectorModification(_))) }); diff --git a/node-graph/gcore/src/vector/vector_data.rs b/node-graph/gcore/src/vector/vector_data.rs index 91de74d82a..7f015a6adb 100644 --- a/node-graph/gcore/src/vector/vector_data.rs +++ b/node-graph/gcore/src/vector/vector_data.rs @@ -561,19 +561,26 @@ impl ManipulatorPointId { } } - /// Finds all the connected handles of a point. For an anchor it is all the connected handles. For a handle it is all the handles connected to its corresponding anchor other than current handle + /// Finds all the connected handles of a point. + /// For an anchor it is all the connected handles. + /// For a handle it is all the handles connected to its corresponding anchor other than the current handle. pub fn get_all_connected_handles(self, vector_data: &VectorData) -> Option> { match self { - ManipulatorPointId::Anchor(point) => Some(vector_data.all_connected(point).collect::>()), + ManipulatorPointId::Anchor(point) => { + let connected = vector_data.all_connected(point).collect::>(); + Some(connected) + } ManipulatorPointId::PrimaryHandle(segment) => { let point = vector_data.segment_domain.segment_start_from_id(segment)?; let current = HandleId::primary(segment); - Some(vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::>()) + let connected = vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::>(); + Some(connected) } ManipulatorPointId::EndHandle(segment) => { let point = vector_data.segment_domain.segment_end_from_id(segment)?; let current = HandleId::end(segment); - Some(vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::>()) + let connected = vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::>(); + Some(connected) } } }