@@ -301,6 +301,7 @@ struct PathToolData {
301
301
saved_points_before_anchor_select_toggle : Vec < ManipulatorPointId > ,
302
302
select_anchor_toggled : bool ,
303
303
dragging_state : DraggingState ,
304
+ current_selected_handle_id : Option < ManipulatorPointId > ,
304
305
angle : f64 ,
305
306
}
306
307
@@ -450,15 +451,20 @@ impl PathToolData {
450
451
}
451
452
452
453
fn update_colinear ( & mut self , equidistant : bool , toggle_colinear : bool , shape_editor : & mut ShapeState , document : & DocumentMessageHandler , responses : & mut VecDeque < Message > ) -> bool {
454
+ // Check handle colinear state
455
+ let is_colinear = self
456
+ . selection_status
457
+ . angle ( )
458
+ . map ( |angle| match angle {
459
+ ManipulatorAngle :: Colinear => true ,
460
+ ManipulatorAngle :: Free | ManipulatorAngle :: Mixed => false ,
461
+ } )
462
+ . unwrap_or ( false ) ;
463
+
453
464
// Check if the toggle_colinear key has just been pressed
454
465
if toggle_colinear && !self . toggle_colinear_debounce {
455
466
self . opposing_handle_lengths = None ;
456
- let colinear = self . selection_status . angle ( ) . is_some_and ( |angle| match angle {
457
- ManipulatorAngle :: Colinear => true ,
458
- ManipulatorAngle :: Free => false ,
459
- ManipulatorAngle :: Mixed => false ,
460
- } ) ;
461
- if colinear {
467
+ if is_colinear {
462
468
shape_editor. disable_colinear_handles_state_on_selected ( & document. network_interface , responses) ;
463
469
} else {
464
470
shape_editor. convert_selected_manipulators_to_colinear_handles ( responses, document) ;
@@ -469,13 +475,46 @@ impl PathToolData {
469
475
self . toggle_colinear_debounce = toggle_colinear;
470
476
471
477
if equidistant && self . opposing_handle_lengths . is_none ( ) {
478
+ if !is_colinear {
479
+ // Try to get selected handle info
480
+ let Some ( ( _, _, selected_handle_id) ) = self . try_get_selected_handle_and_anchor ( shape_editor, document) else {
481
+ self . opposing_handle_lengths = Some ( shape_editor. opposing_handle_lengths ( document) ) ;
482
+ return false ;
483
+ } ;
484
+
485
+ let Some ( ( layer, _) ) = shape_editor. selected_shape_state . iter ( ) . next ( ) else {
486
+ self . opposing_handle_lengths = Some ( shape_editor. opposing_handle_lengths ( document) ) ;
487
+ return false ;
488
+ } ;
489
+
490
+ let Some ( vector_data) = document. network_interface . compute_modified_vector ( * layer) else {
491
+ self . opposing_handle_lengths = Some ( shape_editor. opposing_handle_lengths ( document) ) ;
492
+ return false ;
493
+ } ;
494
+
495
+ // Check if handle has a pair (to ignore handles of edges of open paths)
496
+ if let Some ( handle_pair) = selected_handle_id. get_handle_pair ( & vector_data) {
497
+ let opposite_handle_length = handle_pair. iter ( ) . filter ( |& & h| h. to_manipulator_point ( ) != selected_handle_id) . find_map ( |& h| {
498
+ let opp_handle_pos = h. to_manipulator_point ( ) . get_position ( & vector_data) ?;
499
+ let opp_anchor_id = h. to_manipulator_point ( ) . get_anchor ( & vector_data) ?;
500
+ let opp_anchor_pos = vector_data. point_domain . position_from_id ( opp_anchor_id) ?;
501
+ Some ( ( opp_handle_pos - opp_anchor_pos) . length ( ) )
502
+ } ) ;
503
+
504
+ // Make handles colinear if opposite handle is zero length
505
+ if opposite_handle_length. map_or ( false , |l| l == 0. ) {
506
+ shape_editor. convert_selected_manipulators_to_colinear_handles ( responses, document) ;
507
+ return true ;
508
+ }
509
+ }
510
+ }
472
511
self . opposing_handle_lengths = Some ( shape_editor. opposing_handle_lengths ( document) ) ;
473
512
}
474
513
false
475
514
}
476
515
477
516
/// Attempts to get a single selected handle. Also retrieves the position of the anchor it is connected to. Used for the purpose of snapping the angle.
478
- fn try_get_selected_handle_and_anchor ( & self , shape_editor : & ShapeState , document : & DocumentMessageHandler ) -> Option < ( DVec2 , DVec2 ) > {
517
+ fn try_get_selected_handle_and_anchor ( & self , shape_editor : & ShapeState , document : & DocumentMessageHandler ) -> Option < ( DVec2 , DVec2 , ManipulatorPointId ) > {
479
518
// Only count selections of a single layer
480
519
let ( layer, selection) = shape_editor. selected_shape_state . iter ( ) . next ( ) ?;
481
520
@@ -486,6 +525,7 @@ impl PathToolData {
486
525
487
526
// Only count selected handles
488
527
let selected_handle = selection. selected ( ) . next ( ) ?. as_handle ( ) ?;
528
+ let handle_id = selected_handle. to_manipulator_point ( ) ;
489
529
490
530
let layer_to_document = document. metadata ( ) . transform_to_document ( * layer) ;
491
531
let vector_data = document. network_interface . compute_modified_vector ( * layer) ?;
@@ -497,24 +537,26 @@ impl PathToolData {
497
537
let handle_position_document = layer_to_document. transform_point2 ( handle_position_local) ;
498
538
let anchor_position_document = layer_to_document. transform_point2 ( anchor_position_local) ;
499
539
500
- Some ( ( handle_position_document, anchor_position_document) )
540
+ Some ( ( handle_position_document, anchor_position_document, handle_id ) )
501
541
}
502
542
503
- fn calculate_handle_angle ( & mut self , handle_vector : DVec2 , lock_angle : bool , snap_angle : bool ) -> f64 {
504
- let mut handle_angle = -handle_vector. angle_to ( DVec2 :: X ) ;
543
+ fn calculate_handle_angle ( & mut self , handle_vector : DVec2 , handle_id : ManipulatorPointId , lock_angle : bool , snap_angle : bool ) -> f64 {
544
+ let current_angle = -handle_vector. angle_to ( DVec2 :: X ) ;
505
545
506
546
// When the angle is locked we use the old angle
507
- if lock_angle {
508
- handle_angle = self . angle
547
+ if self . current_selected_handle_id == Some ( handle_id ) && lock_angle {
548
+ return self . angle ;
509
549
}
510
550
511
551
// Round the angle to the closest increment
512
- if snap_angle {
552
+ let mut handle_angle = current_angle;
553
+ if snap_angle && !lock_angle {
513
554
let snap_resolution = HANDLE_ROTATE_SNAP_ANGLE . to_radians ( ) ;
514
555
handle_angle = ( handle_angle / snap_resolution) . round ( ) * snap_resolution;
515
556
}
516
557
517
- // Cache the old handle angle for the lock angle.
558
+ // Cache the angle and handle id for lock angle
559
+ self . current_selected_handle_id = Some ( handle_id) ;
518
560
self . angle = handle_angle;
519
561
520
562
handle_angle
@@ -566,10 +608,10 @@ impl PathToolData {
566
608
let current_mouse = input. mouse . position ;
567
609
let raw_delta = document_to_viewport. inverse ( ) . transform_vector2 ( current_mouse - previous_mouse) ;
568
610
569
- let snapped_delta = if let Some ( ( handle_pos, anchor_pos) ) = self . try_get_selected_handle_and_anchor ( shape_editor, document) {
611
+ let snapped_delta = if let Some ( ( handle_pos, anchor_pos, handle_id ) ) = self . try_get_selected_handle_and_anchor ( shape_editor, document) {
570
612
let cursor_pos = handle_pos + raw_delta;
571
613
572
- let handle_angle = self . calculate_handle_angle ( cursor_pos - anchor_pos, lock_angle, snap_angle) ;
614
+ let handle_angle = self . calculate_handle_angle ( cursor_pos - anchor_pos, handle_id , lock_angle, snap_angle) ;
573
615
574
616
let constrained_direction = DVec2 :: new ( handle_angle. cos ( ) , handle_angle. sin ( ) ) ;
575
617
let projected_length = ( cursor_pos - anchor_pos) . dot ( constrained_direction) ;
0 commit comments