diff --git a/lightningbeam-ui/lightningbeam-core/src/hit_test.rs b/lightningbeam-ui/lightningbeam-core/src/hit_test.rs index 85eceba..7a9bc99 100644 --- a/lightningbeam-ui/lightningbeam-core/src/hit_test.rs +++ b/lightningbeam-ui/lightningbeam-core/src/hit_test.rs @@ -449,15 +449,25 @@ pub fn hit_test_vector_editing( let inverse_transform = combined_transform.inverse(); 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 let editable = extract_editable_curves(shape.path()); // Priority 1: Control points (only in BezierEdit mode) if show_control_points { + let local_cp_tolerance = tolerance.control_point * local_tolerance_factor; for (i, curve) in editable.curves.iter().enumerate() { // Test p1 (first control point) let dist_p1 = (curve.p1 - local_point).hypot(); - if dist_p1 < tolerance.control_point { + if dist_p1 < local_cp_tolerance { return Some(VectorEditHit::ControlPoint { shape_instance_id: object.id, curve_index: i, @@ -467,7 +477,7 @@ pub fn hit_test_vector_editing( // Test p2 (second control point) let dist_p2 = (curve.p2 - local_point).hypot(); - if dist_p2 < tolerance.control_point { + if dist_p2 < local_cp_tolerance { return Some(VectorEditHit::ControlPoint { shape_instance_id: object.id, curve_index: i, @@ -478,9 +488,10 @@ pub fn hit_test_vector_editing( } // Priority 2: Vertices (anchor points) + let local_vertex_tolerance = tolerance.vertex * local_tolerance_factor; for (i, vertex) in editable.vertices.iter().enumerate() { let dist = (vertex.point - local_point).hypot(); - if dist < tolerance.vertex { + if dist < local_vertex_tolerance { return Some(VectorEditHit::Vertex { shape_instance_id: object.id, vertex_index: i, @@ -489,11 +500,12 @@ pub fn hit_test_vector_editing( } // Priority 3: Curves + let local_curve_tolerance = tolerance.curve * local_tolerance_factor; for (i, curve) in editable.curves.iter().enumerate() { let nearest = curve.nearest(local_point, 1e-6); let nearest_point = curve.eval(nearest.t); let dist = (nearest_point - local_point).hypot(); - if dist < tolerance.curve { + if dist < local_curve_tolerance { return Some(VectorEditHit::Curve { shape_instance_id: object.id, curve_index: i, diff --git a/lightningbeam-ui/lightningbeam-editor/src/panes/stage.rs b/lightningbeam-ui/lightningbeam-editor/src/panes/stage.rs index 3a53c68..5b5ed60 100644 --- a/lightningbeam-ui/lightningbeam-editor/src/panes/stage.rs +++ b/lightningbeam-ui/lightningbeam-editor/src/panes/stage.rs @@ -2063,9 +2063,10 @@ impl StagePane { 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 - 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) let tolerance = EditingHitTolerance::scaled_by_zoom(self.zoom as f64); let vector_hit = hit_test_vector_editing( @@ -2746,8 +2747,9 @@ impl StagePane { true, // BezierEdit tool shows control points ); - // Mouse down: start interaction - if response.drag_started() || response.clicked() { + // Mouse down: start interaction (check on initial press, not after drag starts) + let mouse_pressed = ui.input(|i| i.pointer.primary_pressed()); + if mouse_pressed { // Priority 1: Vector editing (control points, vertices, and curves) if let Some(hit) = vector_hit { match hit {