Skip to content

Commit e022fbb

Browse files
committed
Fix colinear switch behaviour
1 parent 30e5567 commit e022fbb

3 files changed

Lines changed: 85 additions & 3 deletions

File tree

editor/src/messages/tool/common_functionality/shape_editor.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,9 @@ impl ShapeState {
10271027
/// If only one handle is selected, the other handle will be moved to match the angle of the selected handle.
10281028
/// If both or neither handles are selected, the angle of both handles will be averaged from their current angles, weighted by their lengths.
10291029
/// Assumes all selected manipulators have handles that are already not colinear.
1030+
///
1031+
/// For vector meshes, the handle which is nearest in the direction of 180° angle separation, becomes colinear with current handle,
1032+
/// and if that handle was colinear with some other handle that constraint is removed from the vector data
10301033
pub fn convert_selected_manipulators_to_colinear_handles(&self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler) {
10311034
let mut skip_set = HashSet::new();
10321035

@@ -1036,8 +1039,64 @@ impl ShapeState {
10361039
};
10371040
let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
10381041

1042+
log::info!("reaching hereeeeee");
1043+
1044+
// TODO: A point which has
10391045
for &point in layer_state.selected_points.iter() {
1040-
let Some(handles) = point.get_handle_pair(&vector_data) else { continue };
1046+
// Skip a point which has more than 2 segments connected (vector meshes)
1047+
if let ManipulatorPointId::Anchor(anchor) = point {
1048+
if vector_data.all_connected(anchor).count() > 2 {
1049+
continue;
1050+
}
1051+
}
1052+
1053+
// let Some(handles) = point.get_handle_pair(&vector_data) else { continue };
1054+
1055+
// Here we take handles as the current handle and the most opposite non-colinear-handle
1056+
1057+
let is_handle_non_colinear = |handle: HandleId| -> bool { !(vector_data.colinear_manipulators.iter().any(|&handles| handles[0] == handle || handles[1] == handle)) };
1058+
1059+
log::info!("reaching here");
1060+
let other_handles = match point {
1061+
ManipulatorPointId::Anchor(_) => point.get_handle_pair(&vector_data),
1062+
_ => {
1063+
// something
1064+
point.get_all_connected_handles(&vector_data).and_then(|handles| {
1065+
let mut non_colinear_handles = handles.iter().filter(|&handle| is_handle_non_colinear(*handle)).clone().collect::<Vec<_>>();
1066+
1067+
// Now sort these by angle from the current handle
1068+
non_colinear_handles.sort_by(|&handle_a, &handle_b| {
1069+
let anchor = point.get_anchor_position(&vector_data).expect("No anchor position for handle");
1070+
let orig_handle_pos = point.get_position(&vector_data).expect("No handle position");
1071+
1072+
let a_pos = handle_a.to_manipulator_point().get_position(&vector_data).expect("No handle position");
1073+
let b_pos = handle_b.to_manipulator_point().get_position(&vector_data).expect("No handle position");
1074+
1075+
let v_orig = (orig_handle_pos - anchor).normalize_or_zero();
1076+
1077+
let v_a = (a_pos - anchor).normalize_or_zero();
1078+
let v_b = (b_pos - anchor).normalize_or_zero();
1079+
1080+
let angle_a = v_orig.angle_to(v_a).abs();
1081+
let angle_b = v_orig.angle_to(v_b).abs();
1082+
1083+
// Sort by descending angle (180° is farthest)
1084+
angle_b.partial_cmp(&angle_a).unwrap_or(std::cmp::Ordering::Equal)
1085+
});
1086+
1087+
let current = match point {
1088+
ManipulatorPointId::EndHandle(segment) => HandleId::end(segment),
1089+
ManipulatorPointId::PrimaryHandle(segment) => HandleId::primary(segment),
1090+
ManipulatorPointId::Anchor(_) => unreachable!(),
1091+
};
1092+
1093+
if let Some(other) = non_colinear_handles.iter().next() { Some([current, **other]) } else { None }
1094+
})
1095+
}
1096+
};
1097+
1098+
let Some(handles) = other_handles else { continue };
1099+
10411100
if skip_set.contains(&handles) || skip_set.contains(&[handles[1], handles[0]]) {
10421101
continue;
10431102
};
@@ -1317,7 +1376,7 @@ impl ShapeState {
13171376
match point {
13181377
ManipulatorPointId::Anchor(anchor) => {
13191378
if let Some(handles) = Self::dissolve_anchor(anchor, responses, layer, &vector_data) {
1320-
if !vector_data.all_connected(anchor).any(|a| selected_segments.contains(&a.segment)) {
1379+
if !vector_data.all_connected(anchor).any(|a| selected_segments.contains(&a.segment)) && vector_data.all_connected(anchor).count() <= 2 {
13211380
missing_anchors.insert(anchor, handles);
13221381
}
13231382
}

editor/src/messages/tool/tool_messages/path_tool.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,12 @@ impl PathToolData {
583583
SelectionStatus::None => false,
584584
SelectionStatus::One(single_selected_point) => {
585585
let vector_data = document.network_interface.compute_modified_vector(single_selected_point.layer).unwrap();
586-
single_selected_point.id.get_handle_pair(&vector_data).is_some()
586+
if single_selected_point.id.get_handle_pair(&vector_data).is_some() {
587+
let anchor = single_selected_point.id.get_anchor(&vector_data).expect("Cannot find connected anchor");
588+
if vector_data.all_connected(anchor).count() > 2 { false } else { true }
589+
} else {
590+
false
591+
}
587592
}
588593
SelectionStatus::Multiple(_) => true,
589594
};
@@ -886,6 +891,7 @@ impl PathToolData {
886891
if is_colinear {
887892
shape_editor.disable_colinear_handles_state_on_selected(&document.network_interface, responses);
888893
} else {
894+
// Convert to colinear only if
889895
shape_editor.convert_selected_manipulators_to_colinear_handles(responses, document);
890896
}
891897
self.toggle_colinear_debounce = true;

node-graph/gcore/src/vector/vector_data.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,23 @@ impl ManipulatorPointId {
561561
}
562562
}
563563

564+
/// Finds all the connected handles of a point. For an anchor it is all the connected handles. For a handle it is all the handles connected to its corresponding anchor other than current handle
565+
pub fn get_all_connected_handles(self, vector_data: &VectorData) -> Option<Vec<HandleId>> {
566+
match self {
567+
ManipulatorPointId::Anchor(point) => Some(vector_data.all_connected(point).collect::<Vec<_>>()),
568+
ManipulatorPointId::PrimaryHandle(segment) => {
569+
let point = vector_data.segment_domain.segment_start_from_id(segment)?;
570+
let current = HandleId::primary(segment);
571+
Some(vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::<Vec<_>>())
572+
}
573+
ManipulatorPointId::EndHandle(segment) => {
574+
let point = vector_data.segment_domain.segment_end_from_id(segment)?;
575+
let current = HandleId::primary(segment);
576+
Some(vector_data.segment_domain.all_connected(point).filter(|&value| value != current).collect::<Vec<_>>())
577+
}
578+
}
579+
}
580+
564581
/// Attempt to find the closest anchor. If self is already an anchor then it is just self. If it is a start or end handle, then the start or end point is chosen.
565582
#[must_use]
566583
pub fn get_anchor(self, vector_data: &VectorData) -> Option<PointId> {

0 commit comments

Comments
 (0)