diff --git a/editor/src/messages/portfolio/document/utility_types/transformation.rs b/editor/src/messages/portfolio/document/utility_types/transformation.rs index 18e969e33c..e0149939a5 100644 --- a/editor/src/messages/portfolio/document/utility_types/transformation.rs +++ b/editor/src/messages/portfolio/document/utility_types/transformation.rs @@ -9,9 +9,7 @@ use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::utility_types::ToolType; use glam::{DAffine2, DMat2, DVec2}; use graphene_std::renderer::Quad; -use graphene_std::vector::VectorModificationType; -use graphene_std::vector::{HandleExt, ManipulatorPointId}; -use graphene_std::vector::{HandleId, PointId}; +use graphene_std::vector::{HandleExt, HandleId, ManipulatorPointId, PointId, VectorModificationType}; use std::collections::{HashMap, VecDeque}; use std::f64::consts::PI; @@ -616,7 +614,7 @@ impl<'a> Selected<'a> { responses.add(GraphOperationMessage::Vector { layer, modification_type }); } - if transform_operation.is_some_and(|transform_operation| matches!(transform_operation, TransformOperation::Scaling(_))) && initial_points.anchors.len() > 1 { + if transform_operation.is_some_and(|transform_operation| matches!(transform_operation, TransformOperation::Scaling(_))) && (initial_points.anchors.len() == 2) { return; } diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 24ce92a454..b491e0943d 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -899,6 +899,9 @@ impl ShapeState { let non_zero_handles = handles.iter().filter(|handle| handle.length(vector_data) > 1e-6).count(); let handle_segments = handles.iter().map(|handles| handles.segment).collect::>(); + // Check if the anchor is connected to linear segments and has no handles + let linear_segments = vector_data.connected_linear_segments(point_id) != 0; + // Grab the next and previous manipulator groups by simply looking at the next / previous index let points = handles.iter().map(|handle| vector_data.other_point(handle.segment, point_id)); let anchor_positions = points @@ -940,7 +943,7 @@ impl ShapeState { handle_direction *= -1.; } - if non_zero_handles != 0 { + if non_zero_handles != 0 && !linear_segments { let [a, b] = handles.as_slice() else { return }; let (non_zero_handle, zero_handle) = if a.length(vector_data) > 1e-6 { (a, b) } else { (b, a) }; let Some(direction) = non_zero_handle @@ -1772,10 +1775,13 @@ impl ShapeState { .filter(|&handle| anchor.abs_diff_eq(handle, 1e-5)) .count(); + // Check if the anchor is connected to linear segments. + let one_or_more_segment_linear = vector_data.connected_linear_segments(id) != 0; + // Check by comparing the handle positions to the anchor if this manipulator group is a point for point in self.selected_points() { let Some(point_id) = point.as_anchor() else { continue }; - if positions != 0 { + if positions != 0 || one_or_more_segment_linear { self.convert_manipulator_handles_to_colinear(&vector_data, point_id, responses, layer); } else { for handle in vector_data.all_connected(point_id) { diff --git a/node-graph/gcore/src/vector/vector_data.rs b/node-graph/gcore/src/vector/vector_data.rs index ed7c32db3b..c9888984c7 100644 --- a/node-graph/gcore/src/vector/vector_data.rs +++ b/node-graph/gcore/src/vector/vector_data.rs @@ -11,7 +11,7 @@ use crate::transform::Transform; use crate::vector::click_target::{ClickTargetType, FreePoint}; use crate::{AlphaBlending, Color, GraphicGroupTable}; pub use attributes::*; -use bezier_rs::ManipulatorGroup; +use bezier_rs::{BezierHandles, ManipulatorGroup}; use core::borrow::Borrow; use core::hash::Hash; use dyn_any::DynAny; @@ -334,6 +334,13 @@ impl VectorData { index.flat_map(|index| self.segment_domain.connected_points(index).map(|index| self.point_domain.ids()[index])) } + /// Returns the number of linear segments connected to the given point. + pub fn connected_linear_segments(&self, point_id: PointId) -> usize { + self.segment_bezier_iter() + .filter(|(_, bez, start, end)| ((*start == point_id || *end == point_id) && matches!(bez.handles, BezierHandles::Linear))) + .count() + } + /// Get an array slice of all segment IDs. pub fn segment_ids(&self) -> &[SegmentId] { self.segment_domain.ids()