@@ -15,6 +15,7 @@ use crate::messages::tool::common_functionality::shape_editor::{
15
15
ClosestSegment , ManipulatorAngle , OpposingHandleLengths , SelectedPointsInfo , SelectionChange , SelectionShape , SelectionShapeType , ShapeState ,
16
16
} ;
17
17
use crate :: messages:: tool:: common_functionality:: snapping:: { SnapCache , SnapCandidatePoint , SnapConstraint , SnapData , SnapManager } ;
18
+ use crate :: messages:: tool:: common_functionality:: utility_functions:: calculate_segment_angle;
18
19
use graphene_core:: renderer:: Quad ;
19
20
use graphene_core:: vector:: { ManipulatorPointId , PointId , VectorModificationType } ;
20
21
use graphene_std:: vector:: { HandleId , NoHashBuilder , SegmentId , VectorData } ;
@@ -380,6 +381,8 @@ struct PathToolData {
380
381
snapping_axis : Option < Axis > ,
381
382
alt_clicked_on_anchor : bool ,
382
383
alt_dragging_from_anchor : bool ,
384
+ angle_locked : bool ,
385
+ temporary_colinear_handles : bool ,
383
386
}
384
387
385
388
impl PathToolData {
@@ -727,11 +730,38 @@ impl PathToolData {
727
730
Some ( ( handle_position_document, anchor_position_document, handle_id) )
728
731
}
729
732
730
- fn calculate_handle_angle ( & mut self , handle_vector : DVec2 , handle_id : ManipulatorPointId , lock_angle : bool , snap_angle : bool ) -> f64 {
733
+ #[ allow( clippy:: too_many_arguments) ]
734
+ fn calculate_handle_angle (
735
+ & mut self ,
736
+ shape_editor : & mut ShapeState ,
737
+ document : & DocumentMessageHandler ,
738
+ responses : & mut VecDeque < Message > ,
739
+ relative_vector : DVec2 ,
740
+ handle_vector : DVec2 ,
741
+ handle_id : ManipulatorPointId ,
742
+ lock_angle : bool ,
743
+ snap_angle : bool ,
744
+ ) -> f64 {
731
745
let current_angle = -handle_vector. angle_to ( DVec2 :: X ) ;
732
746
747
+ if let Some ( vector_data) = shape_editor
748
+ . selected_shape_state
749
+ . iter ( )
750
+ . next ( )
751
+ . and_then ( |( layer, _) | document. network_interface . compute_modified_vector ( * layer) )
752
+ {
753
+ if relative_vector. length ( ) < 25. && lock_angle && !self . angle_locked {
754
+ if let Some ( angle) = calculate_lock_angle ( self , shape_editor, responses, document, & vector_data, handle_id) {
755
+ self . angle = angle;
756
+ return angle;
757
+ }
758
+ }
759
+ }
760
+
733
761
// When the angle is locked we use the old angle
762
+
734
763
if self . current_selected_handle_id == Some ( handle_id) && lock_angle {
764
+ self . angle_locked = true ;
735
765
return self . angle ;
736
766
}
737
767
@@ -785,7 +815,7 @@ impl PathToolData {
785
815
let drag_start = self . drag_start_pos ;
786
816
let opposite_delta = drag_start - current_mouse;
787
817
788
- shape_editor. move_selected_points ( None , document, opposite_delta, false , true , false , None , responses) ;
818
+ shape_editor. move_selected_points ( None , document, opposite_delta, false , true , false , None , false , responses) ;
789
819
790
820
// Calculate the projected delta and shift the points along that delta
791
821
let delta = current_mouse - drag_start;
@@ -797,7 +827,7 @@ impl PathToolData {
797
827
_ => DVec2 :: new ( delta. x , 0. ) ,
798
828
} ;
799
829
800
- shape_editor. move_selected_points ( None , document, projected_delta, false , true , false , None , responses) ;
830
+ shape_editor. move_selected_points ( None , document, projected_delta, false , true , false , None , false , responses) ;
801
831
}
802
832
803
833
fn stop_snap_along_axis ( & mut self , shape_editor : & mut ShapeState , document : & DocumentMessageHandler , input : & InputPreprocessorMessageHandler , responses : & mut VecDeque < Message > ) {
@@ -813,12 +843,12 @@ impl PathToolData {
813
843
_ => DVec2 :: new ( opposite_delta. x , 0. ) ,
814
844
} ;
815
845
816
- shape_editor. move_selected_points ( None , document, opposite_projected_delta, false , true , false , None , responses) ;
846
+ shape_editor. move_selected_points ( None , document, opposite_projected_delta, false , true , false , None , false , responses) ;
817
847
818
848
// Calculate what actually would have been the original delta for the point, and apply that
819
849
let delta = current_mouse - drag_start;
820
850
821
- shape_editor. move_selected_points ( None , document, delta, false , true , false , None , responses) ;
851
+ shape_editor. move_selected_points ( None , document, delta, false , true , false , None , false , responses) ;
822
852
823
853
self . snapping_axis = None ;
824
854
}
@@ -872,7 +902,7 @@ impl PathToolData {
872
902
let snapped_delta = if let Some ( ( handle_pos, anchor_pos, handle_id) ) = self . try_get_selected_handle_and_anchor ( shape_editor, document) {
873
903
let cursor_pos = handle_pos + raw_delta;
874
904
875
- let handle_angle = self . calculate_handle_angle ( cursor_pos - anchor_pos, handle_id, lock_angle, snap_angle) ;
905
+ let handle_angle = self . calculate_handle_angle ( shape_editor , document , responses , handle_pos - anchor_pos , cursor_pos - anchor_pos, handle_id, lock_angle, snap_angle) ;
876
906
877
907
let constrained_direction = DVec2 :: new ( handle_angle. cos ( ) , handle_angle. sin ( ) ) ;
878
908
let projected_length = ( cursor_pos - anchor_pos) . dot ( constrained_direction) ;
@@ -931,7 +961,14 @@ impl PathToolData {
931
961
self . alt_dragging_from_anchor = false ;
932
962
self . alt_clicked_on_anchor = false ;
933
963
}
934
- shape_editor. move_selected_points ( handle_lengths, document, snapped_delta, equidistant, true , was_alt_dragging, opposite, responses) ;
964
+
965
+ let mut skip_opposite = false ;
966
+ if self . temporary_colinear_handles && !lock_angle {
967
+ shape_editor. disable_colinear_handles_state_on_selected ( & document. network_interface , responses) ;
968
+ self . temporary_colinear_handles = false ;
969
+ skip_opposite = true ;
970
+ }
971
+ shape_editor. move_selected_points ( handle_lengths, document, snapped_delta, equidistant, true , was_alt_dragging, opposite, skip_opposite, responses) ;
935
972
self . previous_mouse_position += document_to_viewport. inverse ( ) . transform_vector2 ( snapped_delta) ;
936
973
} else {
937
974
let Some ( axis) = self . snapping_axis else { return } ;
@@ -940,7 +977,7 @@ impl PathToolData {
940
977
Axis :: Y => DVec2 :: new ( 0. , unsnapped_delta. y ) ,
941
978
_ => DVec2 :: new ( unsnapped_delta. x , 0. ) ,
942
979
} ;
943
- shape_editor. move_selected_points ( handle_lengths, document, projected_delta, equidistant, true , false , opposite, responses) ;
980
+ shape_editor. move_selected_points ( handle_lengths, document, projected_delta, equidistant, true , false , opposite, false , responses) ;
944
981
self . previous_mouse_position += document_to_viewport. inverse ( ) . transform_vector2 ( unsnapped_delta) ;
945
982
}
946
983
@@ -1238,6 +1275,10 @@ impl Fsm for PathToolFsmState {
1238
1275
let lock_angle_state = input. keyboard . get ( lock_angle as usize ) ;
1239
1276
let snap_angle_state = input. keyboard . get ( snap_angle as usize ) ;
1240
1277
1278
+ if !lock_angle_state {
1279
+ tool_data. angle_locked = false ;
1280
+ }
1281
+
1241
1282
if !tool_data. update_colinear ( equidistant_state, toggle_colinear_state, tool_action_data. shape_editor , tool_action_data. document , responses) {
1242
1283
tool_data. drag (
1243
1284
equidistant_state,
@@ -1412,6 +1453,10 @@ impl Fsm for PathToolFsmState {
1412
1453
}
1413
1454
}
1414
1455
1456
+ if tool_data. temporary_colinear_handles {
1457
+ tool_data. temporary_colinear_handles = false ;
1458
+ }
1459
+
1415
1460
if tool_data. handle_drag_toggle && drag_occurred {
1416
1461
shape_editor. deselect_all_points ( ) ;
1417
1462
shape_editor. select_points_by_manipulator_id ( & tool_data. saved_points_before_handle_drag ) ;
@@ -1511,6 +1556,7 @@ impl Fsm for PathToolFsmState {
1511
1556
false ,
1512
1557
false ,
1513
1558
tool_data. opposite_handle_position ,
1559
+ false ,
1514
1560
responses,
1515
1561
) ;
1516
1562
@@ -1749,3 +1795,52 @@ fn get_selection_status(network_interface: &NodeNetworkInterface, shape_state: &
1749
1795
1750
1796
SelectionStatus :: None
1751
1797
}
1798
+
1799
+ fn calculate_lock_angle (
1800
+ tool_data : & mut PathToolData ,
1801
+ shape_state : & mut ShapeState ,
1802
+ responses : & mut VecDeque < Message > ,
1803
+ document : & DocumentMessageHandler ,
1804
+ vector_data : & VectorData ,
1805
+ handle_id : ManipulatorPointId ,
1806
+ ) -> Option < f64 > {
1807
+ let anchor = handle_id. get_anchor ( vector_data) ?;
1808
+ let anchor_position = vector_data. point_domain . position_from_id ( anchor) ;
1809
+ let current_segment = handle_id. get_segment ( ) ;
1810
+ let points_connected = vector_data. connected_count ( anchor) ;
1811
+
1812
+ let ( anchor_position, segment) = anchor_position. zip ( current_segment) ?;
1813
+ if points_connected == 1 {
1814
+ calculate_segment_angle ( anchor, segment, vector_data, false )
1815
+ } else {
1816
+ let opposite_handle = handle_id
1817
+ . get_handle_pair ( vector_data)
1818
+ . iter ( )
1819
+ . flatten ( )
1820
+ . find ( |& h| h. to_manipulator_point ( ) != handle_id)
1821
+ . copied ( )
1822
+ . map ( |h| h. to_manipulator_point ( ) ) ;
1823
+ let opposite_handle_position = opposite_handle. and_then ( |h| h. get_position ( vector_data) ) . filter ( |pos| ( pos - anchor_position) . length ( ) > 1e-6 ) ;
1824
+
1825
+ if let Some ( opposite_pos) = opposite_handle_position {
1826
+ if !vector_data. colinear_manipulators . iter ( ) . flatten ( ) . map ( |h| h. to_manipulator_point ( ) ) . any ( |h| h == handle_id) {
1827
+ shape_state. convert_selected_manipulators_to_colinear_handles ( responses, document) ;
1828
+ tool_data. temporary_colinear_handles = true ;
1829
+ }
1830
+ Some ( -( opposite_pos - anchor_position) . angle_to ( DVec2 :: X ) )
1831
+ } else {
1832
+ let angle_1 = vector_data
1833
+ . adjacent_segment ( & handle_id)
1834
+ . and_then ( |( _, adjacent_segment) | calculate_segment_angle ( anchor, adjacent_segment, vector_data, false ) ) ;
1835
+
1836
+ let angle_2 = calculate_segment_angle ( anchor, segment, vector_data, false ) ;
1837
+
1838
+ match ( angle_1, angle_2) {
1839
+ ( Some ( angle_1) , Some ( angle_2) ) => Some ( ( angle_1 + angle_2) / 2.0 ) ,
1840
+ ( Some ( angle_1) , None ) => Some ( angle_1) ,
1841
+ ( None , Some ( angle_2) ) => Some ( angle_2) ,
1842
+ ( None , None ) => None ,
1843
+ }
1844
+ }
1845
+ }
1846
+ }
0 commit comments