Make the Path tool only allow selecting points that are visible (#2668)

* Fix only visible points selection in point selection

* Fix comments

* Remove bug from box selection and lasso

* Code review

* Fix comment

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Adesh Gupta
2025-05-28 02:24:23 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent f6e592da5b
commit b564579362
4 changed files with 216 additions and 28 deletions
@@ -379,6 +379,7 @@ struct PathToolData {
alt_dragging_from_anchor: bool,
angle_locked: bool,
temporary_colinear_handles: bool,
frontier_handles_info: Option<HashMap<SegmentId, Vec<PointId>>>,
adjacent_anchor_offset: Option<DVec2>,
}
@@ -451,6 +452,7 @@ impl PathToolData {
lasso_select: bool,
handle_drag_from_anchor: bool,
drag_zero_handle: bool,
path_overlay_mode: PathOverlayMode,
) -> PathToolFsmState {
self.double_click_handled = false;
self.opposing_handle_lengths = None;
@@ -466,8 +468,14 @@ impl PathToolData {
let old_selection = shape_editor.selected_points().cloned().collect::<Vec<_>>();
// Check if the point is already selected; if not, select the first point within the threshold (in pixels)
if let Some((already_selected, mut selection_info)) = shape_editor.get_point_selection_state(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) {
log::info!("entered the part where tool identifies a near point");
// Don't select the points which are not shown currently in PathOverlayMode
if let Some((already_selected, mut selection_info)) = shape_editor.get_point_selection_state(
&document.network_interface,
input.mouse.position,
SELECTION_THRESHOLD,
path_overlay_mode,
self.frontier_handles_info.clone(),
) {
responses.add(DocumentMessage::StartTransaction);
self.last_clicked_point_was_selected = already_selected;
@@ -475,7 +483,14 @@ impl PathToolData {
// If the point is already selected and shift (`extend_selection`) is used, keep the selection unchanged.
// Otherwise, select the first point within the threshold.
if !(already_selected && extend_selection) {
if let Some(updated_selection_info) = shape_editor.change_point_selection(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD, extend_selection) {
if let Some(updated_selection_info) = shape_editor.change_point_selection(
&document.network_interface,
input.mouse.position,
SELECTION_THRESHOLD,
extend_selection,
path_overlay_mode,
self.frontier_handles_info.clone(),
) {
selection_info = updated_selection_info;
}
}
@@ -1055,14 +1070,16 @@ impl Fsm for PathToolFsmState {
match tool_options.path_overlay_mode {
PathOverlayMode::AllHandles => {
path_overlays(document, DrawHandles::All, shape_editor, &mut overlay_context);
tool_data.frontier_handles_info = None;
}
PathOverlayMode::SelectedPointHandles => {
let selected_segments = selected_segments(document, shape_editor);
let selected_segments = selected_segments(&document.network_interface, shape_editor);
path_overlays(document, DrawHandles::SelectedAnchors(selected_segments), shape_editor, &mut overlay_context);
tool_data.frontier_handles_info = None;
}
PathOverlayMode::FrontierHandles => {
let selected_segments = selected_segments(document, shape_editor);
let selected_segments = selected_segments(&document.network_interface, shape_editor);
let selected_points = shape_editor.selected_points();
let selected_anchors = selected_points
.filter_map(|point_id| if let ManipulatorPointId::Anchor(p) = point_id { Some(*p) } else { None })
@@ -1090,13 +1107,18 @@ impl Fsm for PathToolFsmState {
for (point, attached_segments) in selected_segments_by_point {
if attached_segments.len() == 1 {
segment_endpoints.entry(attached_segments[0]).or_default().push(point);
} else if !selected_anchors.contains(&point) {
}
// Handle the edge case where a point, although not explicitly selected, is shared by two segments.
else if !selected_anchors.contains(&point) {
segment_endpoints.entry(attached_segments[0]).or_default().push(point);
segment_endpoints.entry(attached_segments[1]).or_default().push(point);
}
}
}
// Caching segment endpoints for use in point selection logic
tool_data.frontier_handles_info = Some(segment_endpoints.clone());
// Now frontier anchors can be sent for rendering overlays
path_overlays(document, DrawHandles::FrontierHandles(segment_endpoints), shape_editor, &mut overlay_context);
}
@@ -1198,7 +1220,17 @@ impl Fsm for PathToolFsmState {
tool_data.selection_mode = None;
tool_data.lasso_polygon.clear();
tool_data.mouse_down(shape_editor, document, input, responses, extend_selection, lasso_select, handle_drag_from_anchor, drag_zero_handle)
tool_data.mouse_down(
shape_editor,
document,
input,
responses,
extend_selection,
lasso_select,
handle_drag_from_anchor,
drag_zero_handle,
tool_options.path_overlay_mode,
)
}
(
PathToolFsmState::Drawing { selection_shape },
@@ -1353,7 +1385,13 @@ impl Fsm for PathToolFsmState {
// If there is a point nearby, then remove the overlay
if shape_editor
.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD)
.find_nearest_visible_point_indices(
&document.network_interface,
input.mouse.position,
SELECTION_THRESHOLD,
tool_options.path_overlay_mode,
tool_data.frontier_handles_info.clone(),
)
.is_some()
{
tool_data.segment = None;
@@ -1447,9 +1485,21 @@ impl Fsm for PathToolFsmState {
match selection_shape {
SelectionShapeType::Box => {
let bbox = [tool_data.drag_start_pos, previous_mouse];
shape_editor.select_all_in_shape(&document.network_interface, SelectionShape::Box(bbox), selection_change);
shape_editor.select_all_in_shape(
&document.network_interface,
SelectionShape::Box(bbox),
selection_change,
tool_options.path_overlay_mode,
tool_data.frontier_handles_info.clone(),
);
}
SelectionShapeType::Lasso => shape_editor.select_all_in_shape(&document.network_interface, SelectionShape::Lasso(&tool_data.lasso_polygon), selection_change),
SelectionShapeType::Lasso => shape_editor.select_all_in_shape(
&document.network_interface,
SelectionShape::Lasso(&tool_data.lasso_polygon),
selection_change,
tool_options.path_overlay_mode,
tool_data.frontier_handles_info.clone(),
),
}
}
@@ -1495,9 +1545,21 @@ impl Fsm for PathToolFsmState {
match selection_shape {
SelectionShapeType::Box => {
let bbox = [tool_data.drag_start_pos, previous_mouse];
shape_editor.select_all_in_shape(&document.network_interface, SelectionShape::Box(bbox), select_kind);
shape_editor.select_all_in_shape(
&document.network_interface,
SelectionShape::Box(bbox),
select_kind,
tool_options.path_overlay_mode,
tool_data.frontier_handles_info.clone(),
);
}
SelectionShapeType::Lasso => shape_editor.select_all_in_shape(&document.network_interface, SelectionShape::Lasso(&tool_data.lasso_polygon), select_kind),
SelectionShapeType::Lasso => shape_editor.select_all_in_shape(
&document.network_interface,
SelectionShape::Lasso(&tool_data.lasso_polygon),
select_kind,
tool_options.path_overlay_mode,
tool_data.frontier_handles_info.clone(),
),
}
}
responses.add(OverlaysMessage::Draw);
@@ -1508,7 +1570,14 @@ impl Fsm for PathToolFsmState {
(_, PathToolMessage::DragStop { extend_selection, .. }) => {
let extend_selection = input.keyboard.get(extend_selection as usize);
let drag_occurred = tool_data.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD;
let nearest_point = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD);
// TODO: Here we want only visible points to be considered
let nearest_point = shape_editor.find_nearest_visible_point_indices(
&document.network_interface,
input.mouse.position,
SELECTION_THRESHOLD,
tool_options.path_overlay_mode,
tool_data.frontier_handles_info.clone(),
);
if let Some((layer, nearest_point)) = nearest_point {
if !drag_occurred && extend_selection {