Add Path tool feature for angle locking upon pressing Ctrl while dragging handle over anchor (#2612)

* almost_fixed

* fix need to refactor

* fixed issed need to refactor

* refactor-done fixed issue

* move function to common_functionality

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0SlowPoke0
2025-04-23 20:39:14 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent 3d37ef79ac
commit d39308c048
5 changed files with 150 additions and 38 deletions
@@ -729,7 +729,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 {
@@ -775,6 +777,7 @@ impl ShapeState {
in_viewport_space: bool,
was_alt_dragging: bool,
opposite_handle_position: Option<DVec2>,
skip_opposite_handle: bool,
responses: &mut VecDeque<Message>,
) {
for (&layer, state) in &self.selected_shape_state {
@@ -816,6 +819,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];
@@ -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<f64> {
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))
}