fix quick drags of control points

This commit is contained in:
Skyler Lehmkuhl 2025-12-22 18:54:43 -05:00
parent ffb53884b0
commit f1df85baa2
2 changed files with 22 additions and 8 deletions

View File

@ -449,15 +449,25 @@ pub fn hit_test_vector_editing(
let inverse_transform = combined_transform.inverse(); let inverse_transform = combined_transform.inverse();
let local_point = inverse_transform * point; let local_point = inverse_transform * point;
// Calculate the scale factor to transform screen-space tolerances to local space
// We need the inverse scale because we're in local space
// Affine coefficients are [a, b, c, d, e, f] representing matrix [[a, c, e], [b, d, f]]
let coeffs = combined_transform.as_coeffs();
let scale_x = (coeffs[0].powi(2) + coeffs[1].powi(2)).sqrt();
let scale_y = (coeffs[2].powi(2) + coeffs[3].powi(2)).sqrt();
let avg_scale = (scale_x + scale_y) / 2.0;
let local_tolerance_factor = 1.0 / avg_scale.max(0.001); // Avoid division by zero
// Extract editable curves and vertices from the shape's path // Extract editable curves and vertices from the shape's path
let editable = extract_editable_curves(shape.path()); let editable = extract_editable_curves(shape.path());
// Priority 1: Control points (only in BezierEdit mode) // Priority 1: Control points (only in BezierEdit mode)
if show_control_points { if show_control_points {
let local_cp_tolerance = tolerance.control_point * local_tolerance_factor;
for (i, curve) in editable.curves.iter().enumerate() { for (i, curve) in editable.curves.iter().enumerate() {
// Test p1 (first control point) // Test p1 (first control point)
let dist_p1 = (curve.p1 - local_point).hypot(); let dist_p1 = (curve.p1 - local_point).hypot();
if dist_p1 < tolerance.control_point { if dist_p1 < local_cp_tolerance {
return Some(VectorEditHit::ControlPoint { return Some(VectorEditHit::ControlPoint {
shape_instance_id: object.id, shape_instance_id: object.id,
curve_index: i, curve_index: i,
@ -467,7 +477,7 @@ pub fn hit_test_vector_editing(
// Test p2 (second control point) // Test p2 (second control point)
let dist_p2 = (curve.p2 - local_point).hypot(); let dist_p2 = (curve.p2 - local_point).hypot();
if dist_p2 < tolerance.control_point { if dist_p2 < local_cp_tolerance {
return Some(VectorEditHit::ControlPoint { return Some(VectorEditHit::ControlPoint {
shape_instance_id: object.id, shape_instance_id: object.id,
curve_index: i, curve_index: i,
@ -478,9 +488,10 @@ pub fn hit_test_vector_editing(
} }
// Priority 2: Vertices (anchor points) // Priority 2: Vertices (anchor points)
let local_vertex_tolerance = tolerance.vertex * local_tolerance_factor;
for (i, vertex) in editable.vertices.iter().enumerate() { for (i, vertex) in editable.vertices.iter().enumerate() {
let dist = (vertex.point - local_point).hypot(); let dist = (vertex.point - local_point).hypot();
if dist < tolerance.vertex { if dist < local_vertex_tolerance {
return Some(VectorEditHit::Vertex { return Some(VectorEditHit::Vertex {
shape_instance_id: object.id, shape_instance_id: object.id,
vertex_index: i, vertex_index: i,
@ -489,11 +500,12 @@ pub fn hit_test_vector_editing(
} }
// Priority 3: Curves // Priority 3: Curves
let local_curve_tolerance = tolerance.curve * local_tolerance_factor;
for (i, curve) in editable.curves.iter().enumerate() { for (i, curve) in editable.curves.iter().enumerate() {
let nearest = curve.nearest(local_point, 1e-6); let nearest = curve.nearest(local_point, 1e-6);
let nearest_point = curve.eval(nearest.t); let nearest_point = curve.eval(nearest.t);
let dist = (nearest_point - local_point).hypot(); let dist = (nearest_point - local_point).hypot();
if dist < tolerance.curve { if dist < local_curve_tolerance {
return Some(VectorEditHit::Curve { return Some(VectorEditHit::Curve {
shape_instance_id: object.id, shape_instance_id: object.id,
curve_index: i, curve_index: i,

View File

@ -2063,9 +2063,10 @@ impl StagePane {
let point = Point::new(world_pos.x as f64, world_pos.y as f64); let point = Point::new(world_pos.x as f64, world_pos.y as f64);
// Mouse down: start interaction (use drag_started for immediate feedback) // Mouse down: start interaction (check on initial press, not after drag starts)
// Scope this section to drop vector_layer borrow before drag handling // Scope this section to drop vector_layer borrow before drag handling
if response.drag_started() || response.clicked() { let mouse_pressed = ui.input(|i| i.pointer.primary_pressed());
if mouse_pressed {
// VECTOR EDITING: Check for vertex/curve editing first (higher priority than selection) // VECTOR EDITING: Check for vertex/curve editing first (higher priority than selection)
let tolerance = EditingHitTolerance::scaled_by_zoom(self.zoom as f64); let tolerance = EditingHitTolerance::scaled_by_zoom(self.zoom as f64);
let vector_hit = hit_test_vector_editing( let vector_hit = hit_test_vector_editing(
@ -2746,8 +2747,9 @@ impl StagePane {
true, // BezierEdit tool shows control points true, // BezierEdit tool shows control points
); );
// Mouse down: start interaction // Mouse down: start interaction (check on initial press, not after drag starts)
if response.drag_started() || response.clicked() { let mouse_pressed = ui.input(|i| i.pointer.primary_pressed());
if mouse_pressed {
// Priority 1: Vector editing (control points, vertices, and curves) // Priority 1: Vector editing (control points, vertices, and curves)
if let Some(hit) = vector_hit { if let Some(hit) = vector_hit {
match hit { match hit {