mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 18:28:12 +08:00
Fix the Path tool's segment insertion line overlay having hysteresis with distance away from the cursor (#2677)
* Fix hysteresis in segment insertion * Update editor/src/messages/tool/common_functionality/shape_editor.rs * Update editor/src/messages/tool/common_functionality/shape_editor.rs * Fix insert point overlay --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
04d7adb867
commit
33a31b4f17
@@ -102,7 +102,7 @@ pub const MANIPULATOR_GROUP_MARKER_SIZE: f64 = 6.;
|
|||||||
pub const SELECTION_THRESHOLD: f64 = 10.;
|
pub const SELECTION_THRESHOLD: f64 = 10.;
|
||||||
pub const HIDE_HANDLE_DISTANCE: f64 = 3.;
|
pub const HIDE_HANDLE_DISTANCE: f64 = 3.;
|
||||||
pub const HANDLE_ROTATE_SNAP_ANGLE: f64 = 15.;
|
pub const HANDLE_ROTATE_SNAP_ANGLE: f64 = 15.;
|
||||||
pub const SEGMENT_INSERTION_DISTANCE: f64 = 7.5;
|
pub const SEGMENT_INSERTION_DISTANCE: f64 = 8.;
|
||||||
pub const SEGMENT_OVERLAY_SIZE: f64 = 10.;
|
pub const SEGMENT_OVERLAY_SIZE: f64 = 10.;
|
||||||
pub const HANDLE_LENGTH_FACTOR: f64 = 0.5;
|
pub const HANDLE_LENGTH_FACTOR: f64 = 0.5;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::graph_modification_utils::{self, merge_layers};
|
use super::graph_modification_utils::merge_layers;
|
||||||
use super::snapping::{SnapCache, SnapCandidatePoint, SnapData, SnapManager, SnappedPoint};
|
use super::snapping::{SnapCache, SnapCandidatePoint, SnapData, SnapManager, SnappedPoint};
|
||||||
use super::utility_functions::calculate_segment_angle;
|
use super::utility_functions::calculate_segment_angle;
|
||||||
use crate::consts::HANDLE_LENGTH_FACTOR;
|
use crate::consts::HANDLE_LENGTH_FACTOR;
|
||||||
@@ -12,7 +12,6 @@ use crate::messages::tool::common_functionality::utility_functions::is_visible_p
|
|||||||
use crate::messages::tool::tool_messages::path_tool::{PathOverlayMode, PointSelectState};
|
use crate::messages::tool::tool_messages::path_tool::{PathOverlayMode, PointSelectState};
|
||||||
use bezier_rs::{Bezier, BezierHandles, Subpath, TValue};
|
use bezier_rs::{Bezier, BezierHandles, Subpath, TValue};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene_core::transform::Transform;
|
|
||||||
use graphene_core::vector::{ManipulatorPointId, PointId, VectorData, VectorModificationType};
|
use graphene_core::vector::{ManipulatorPointId, PointId, VectorData, VectorModificationType};
|
||||||
use graphene_std::vector::{HandleId, SegmentId};
|
use graphene_std::vector::{HandleId, SegmentId};
|
||||||
|
|
||||||
@@ -149,7 +148,6 @@ pub struct ClosestSegment {
|
|||||||
colinear: [Option<HandleId>; 2],
|
colinear: [Option<HandleId>; 2],
|
||||||
t: f64,
|
t: f64,
|
||||||
bezier_point_to_viewport: DVec2,
|
bezier_point_to_viewport: DVec2,
|
||||||
stroke_width: f64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClosestSegment {
|
impl ClosestSegment {
|
||||||
@@ -169,6 +167,12 @@ impl ClosestSegment {
|
|||||||
self.bezier_point_to_viewport
|
self.bezier_point_to_viewport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn closest_point(&self, document_metadata: &DocumentMetadata) -> DVec2 {
|
||||||
|
let transform = document_metadata.transform_to_viewport(self.layer);
|
||||||
|
let bezier_point = self.bezier.evaluate(TValue::Parametric(self.t));
|
||||||
|
transform.transform_point2(bezier_point)
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates this [`ClosestSegment`] with the viewport-space location of the closest point on the segment to the given mouse position.
|
/// Updates this [`ClosestSegment`] with the viewport-space location of the closest point on the segment to the given mouse position.
|
||||||
pub fn update_closest_point(&mut self, document_metadata: &DocumentMetadata, mouse_position: DVec2) {
|
pub fn update_closest_point(&mut self, document_metadata: &DocumentMetadata, mouse_position: DVec2) {
|
||||||
let transform = document_metadata.transform_to_viewport(self.layer);
|
let transform = document_metadata.transform_to_viewport(self.layer);
|
||||||
@@ -186,10 +190,8 @@ impl ClosestSegment {
|
|||||||
self.bezier_point_to_viewport.distance_squared(mouse_position)
|
self.bezier_point_to_viewport.distance_squared(mouse_position)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn too_far(&self, mouse_position: DVec2, tolerance: f64, document_metadata: &DocumentMetadata) -> bool {
|
pub fn too_far(&self, mouse_position: DVec2, tolerance: f64) -> bool {
|
||||||
let dist_sq = self.distance_squared(mouse_position);
|
tolerance.powi(2) < self.distance_squared(mouse_position)
|
||||||
let stroke_width = document_metadata.document_to_viewport.decompose_scale().x.max(1.) * self.stroke_width;
|
|
||||||
(stroke_width + tolerance).powi(2) < dist_sq
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_positions(&self, document_metadata: &DocumentMetadata) -> (Option<DVec2>, Option<DVec2>) {
|
pub fn handle_positions(&self, document_metadata: &DocumentMetadata) -> (Option<DVec2>, Option<DVec2>) {
|
||||||
@@ -1449,11 +1451,6 @@ impl ShapeState {
|
|||||||
if distance_squared < closest_distance_squared {
|
if distance_squared < closest_distance_squared {
|
||||||
closest_distance_squared = distance_squared;
|
closest_distance_squared = distance_squared;
|
||||||
|
|
||||||
// 0.5 is half the line (center to side) but it's convenient to allow targeting slightly more than half the line width
|
|
||||||
const STROKE_WIDTH_PERCENT: f64 = 0.7;
|
|
||||||
|
|
||||||
let stroke_width = graph_modification_utils::get_stroke_width(layer, network_interface).unwrap_or(1.) as f64 * STROKE_WIDTH_PERCENT;
|
|
||||||
|
|
||||||
// Convert to linear if handes are on top of control points
|
// Convert to linear if handes are on top of control points
|
||||||
if let bezier_rs::BezierHandles::Cubic { handle_start, handle_end } = bezier.handles {
|
if let bezier_rs::BezierHandles::Cubic { handle_start, handle_end } = bezier.handles {
|
||||||
if handle_start.abs_diff_eq(bezier.start(), f64::EPSILON * 100.) && handle_end.abs_diff_eq(bezier.end(), f64::EPSILON * 100.) {
|
if handle_start.abs_diff_eq(bezier.start(), f64::EPSILON * 100.) && handle_end.abs_diff_eq(bezier.end(), f64::EPSILON * 100.) {
|
||||||
@@ -1474,7 +1471,6 @@ impl ShapeState {
|
|||||||
t,
|
t,
|
||||||
bezier_point_to_viewport: screenspace,
|
bezier_point_to_viewport: screenspace,
|
||||||
layer,
|
layer,
|
||||||
stroke_width,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1129,7 +1129,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
Self::Ready => {
|
Self::Ready => {
|
||||||
if let Some(closest_segment) = &tool_data.segment {
|
if let Some(closest_segment) = &tool_data.segment {
|
||||||
let perp = closest_segment.calculate_perp(document);
|
let perp = closest_segment.calculate_perp(document);
|
||||||
let point = closest_segment.closest_point_to_viewport();
|
let point = closest_segment.closest_point(document.metadata());
|
||||||
|
|
||||||
// Draw an X on the segment
|
// Draw an X on the segment
|
||||||
if tool_data.delete_segment_pressed {
|
if tool_data.delete_segment_pressed {
|
||||||
@@ -1400,7 +1400,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
// If already hovering on a segment, then recalculate its closest point
|
// If already hovering on a segment, then recalculate its closest point
|
||||||
else if let Some(closest_segment) = &mut tool_data.segment {
|
else if let Some(closest_segment) = &mut tool_data.segment {
|
||||||
closest_segment.update_closest_point(document.metadata(), input.mouse.position);
|
closest_segment.update_closest_point(document.metadata(), input.mouse.position);
|
||||||
if closest_segment.too_far(input.mouse.position, SEGMENT_INSERTION_DISTANCE, document.metadata()) {
|
if closest_segment.too_far(input.mouse.position, SEGMENT_INSERTION_DISTANCE) {
|
||||||
tool_data.segment = None;
|
tool_data.segment = None;
|
||||||
}
|
}
|
||||||
responses.add(OverlaysMessage::Draw)
|
responses.add(OverlaysMessage::Draw)
|
||||||
|
|||||||
Reference in New Issue
Block a user