From 898b0bb582808a3f093479f68c61bb464b0b4e8e Mon Sep 17 00:00:00 2001 From: 0HyperCube <78500760+0HyperCube@users.noreply.github.com> Date: Fri, 27 Jan 2023 20:28:12 +0000 Subject: [PATCH] Cull snapping of points outside viewport (#961) Do not snap to points outside viewport Co-authored-by: Keavon Chambers --- .../tool/common_functionality/resize.rs | 8 ++--- .../tool/common_functionality/snapping.rs | 34 +++++++++++++++---- .../tool/tool_messages/artboard_tool.rs | 14 ++++---- .../tool/tool_messages/ellipse_tool.rs | 2 +- .../tool/tool_messages/gradient_tool.rs | 10 +++--- .../tool/tool_messages/imaginate_tool.rs | 2 +- .../messages/tool/tool_messages/line_tool.rs | 4 +-- .../tool_messages/node_graph_frame_tool.rs | 2 +- .../messages/tool/tool_messages/path_tool.rs | 4 +-- .../messages/tool/tool_messages/pen_tool.rs | 4 +-- .../tool/tool_messages/rectangle_tool.rs | 2 +- .../tool/tool_messages/select_tool.rs | 14 ++++---- .../messages/tool/tool_messages/shape_tool.rs | 2 +- .../tool/tool_messages/spline_tool.rs | 4 +-- 14 files changed, 66 insertions(+), 40 deletions(-) diff --git a/editor/src/messages/tool/common_functionality/resize.rs b/editor/src/messages/tool/common_functionality/resize.rs index 8261a527..af3d869b 100644 --- a/editor/src/messages/tool/common_functionality/resize.rs +++ b/editor/src/messages/tool/common_functionality/resize.rs @@ -18,11 +18,11 @@ pub struct Resize { impl Resize { /// Starts a resize, assigning the snap targets and snapping the starting position. - pub fn start(&mut self, responses: &mut VecDeque, document: &DocumentMessageHandler, mouse_position: DVec2, font_cache: &FontCache) { - self.snap_manager.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); - self.snap_manager.add_all_document_handles(document, &[], &[], &[]); + pub fn start(&mut self, responses: &mut VecDeque, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, font_cache: &FontCache) { + self.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true); + self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); let root_transform = document.document_legacy.root.transform; - self.drag_start = root_transform.inverse().transform_point2(self.snap_manager.snap_position(responses, document, mouse_position)); + self.drag_start = root_transform.inverse().transform_point2(self.snap_manager.snap_position(responses, document, input.mouse.position)); } /// Calculate the drag start position in viewport space. diff --git a/editor/src/messages/tool/common_functionality/snapping.rs b/editor/src/messages/tool/common_functionality/snapping.rs index 12a4ec8a..9f3ce2ec 100644 --- a/editor/src/messages/tool/common_functionality/snapping.rs +++ b/editor/src/messages/tool/common_functionality/snapping.rs @@ -210,13 +210,25 @@ impl SnapManager { /// Gets a list of snap targets for the X and Y axes (if specified) in Viewport coords for the target layers (usually all layers or all non-selected layers.) /// This should be called at the start of a drag. - pub fn start_snap(&mut self, document_message_handler: &DocumentMessageHandler, bounding_boxes: impl Iterator, snap_x: bool, snap_y: bool) { + pub fn start_snap( + &mut self, + document_message_handler: &DocumentMessageHandler, + input: &InputPreprocessorMessageHandler, + bounding_boxes: impl Iterator, + snap_x: bool, + snap_y: bool, + ) { if document_message_handler.snapping_enabled { self.snap_x = snap_x; self.snap_y = snap_y; // Could be made into sorted Vec or a HashSet for more performant lookups. - self.bound_targets = Some(bounding_boxes.flat_map(expand_bounds).collect()); + self.bound_targets = Some( + bounding_boxes + .flat_map(expand_bounds) + .filter(|&pos| pos.x >= 0. && pos.y >= 0. && pos.x < input.viewport_bounds.size().x && pos.y <= input.viewport_bounds.size().y) + .collect(), + ); self.point_targets = None; } } @@ -224,8 +236,9 @@ impl SnapManager { /// Add arbitrary snapping points /// /// This should be called after start_snap - pub fn add_snap_points(&mut self, document_message_handler: &DocumentMessageHandler, snap_points: impl Iterator) { + pub fn add_snap_points(&mut self, document_message_handler: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, snap_points: impl Iterator) { if document_message_handler.snapping_enabled { + let snap_points = snap_points.filter(|&pos| pos.x >= 0. && pos.y >= 0. && pos.x < input.viewport_bounds.size().x && pos.y <= input.viewport_bounds.size().y); if let Some(targets) = &mut self.point_targets { targets.extend(snap_points); } else { @@ -237,7 +250,15 @@ impl SnapManager { /// Add the [ManipulatorGroup]s (optionally including handles) of the specified shape layer to the snapping points /// /// This should be called after start_snap - pub fn add_snap_path(&mut self, document_message_handler: &DocumentMessageHandler, layer: &Layer, path: &[LayerId], include_handles: bool, ignore_points: &[(&[LayerId], u64, ManipulatorType)]) { + pub fn add_snap_path( + &mut self, + document_message_handler: &DocumentMessageHandler, + input: &InputPreprocessorMessageHandler, + layer: &Layer, + path: &[LayerId], + include_handles: bool, + ignore_points: &[(&[LayerId], u64, ManipulatorType)], + ) { if let LayerDataType::Shape(shape_layer) = &layer.data { let transform = document_message_handler.document_legacy.multiply_transforms(path).unwrap(); let snap_points = shape_layer @@ -259,7 +280,7 @@ impl SnapManager { .filter(|(id, point)| !ignore_points.contains(&(path, *id, point.manipulator_type))) .map(|(_id, point)| DVec2::new(point.position.x, point.position.y)) .map(|pos| transform.transform_point2(pos)); - self.add_snap_points(document_message_handler, snap_points); + self.add_snap_points(document_message_handler, input, snap_points); } } @@ -267,6 +288,7 @@ impl SnapManager { pub fn add_all_document_handles( &mut self, document_message_handler: &DocumentMessageHandler, + input: &InputPreprocessorMessageHandler, include_handles: &[&[LayerId]], exclude: &[&[LayerId]], ignore_points: &[(&[LayerId], u64, ManipulatorType)], @@ -274,7 +296,7 @@ impl SnapManager { for path in document_message_handler.all_layers() { if !exclude.contains(&path) { let layer = document_message_handler.document_legacy.layer(path).expect("Could not get layer for snapping"); - self.add_snap_path(document_message_handler, layer, path, include_handles.contains(&path), ignore_points); + self.add_snap_path(document_message_handler, input, layer, path, include_handles.contains(&path), ignore_points); } } } diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index 98da5d6b..5b503aa2 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -196,8 +196,10 @@ impl Fsm for ArtboardToolFsmState { let snap_y = selected_edges.0 || selected_edges.1; let artboard = tool_data.selected_artboard.unwrap(); - tool_data.snap_manager.start_snap(document, document.bounding_boxes(None, Some(artboard), font_cache), snap_x, snap_y); - tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]); + tool_data + .snap_manager + .start_snap(document, input, document.bounding_boxes(None, Some(artboard), font_cache), snap_x, snap_y); + tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); if let Some(bounds) = &mut tool_data.bounding_box_overlays { let pivot = document.artboard_message_handler.artboards_document.pivot(&[artboard], font_cache).unwrap_or_default(); @@ -219,8 +221,8 @@ impl Fsm for ArtboardToolFsmState { tool_data .snap_manager - .start_snap(document, document.bounding_boxes(None, Some(intersection[0]), font_cache), true, true); - tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]); + .start_snap(document, input, document.bounding_boxes(None, Some(intersection[0]), font_cache), true, true); + tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); responses.push_back( PropertiesPanelMessage::SetActiveLayers { @@ -327,8 +329,8 @@ impl Fsm for ArtboardToolFsmState { let id = generate_uuid(); tool_data.selected_artboard = Some(id); - tool_data.snap_manager.start_snap(document, document.bounding_boxes(None, Some(id), font_cache), true, true); - tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]); + tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, Some(id), font_cache), true, true); + tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); responses.push_back( ArtboardMessage::AddArtboard { diff --git a/editor/src/messages/tool/tool_messages/ellipse_tool.rs b/editor/src/messages/tool/tool_messages/ellipse_tool.rs index dc52d6ed..e26b1865 100644 --- a/editor/src/messages/tool/tool_messages/ellipse_tool.rs +++ b/editor/src/messages/tool/tool_messages/ellipse_tool.rs @@ -134,7 +134,7 @@ impl Fsm for EllipseToolFsmState { if let ToolMessage::Ellipse(event) = event { match (self, event) { (Ready, DragStart) => { - shape_data.start(responses, document, input.mouse.position, font_cache); + shape_data.start(responses, document, input, font_cache); responses.push_back(DocumentMessage::StartTransaction.into()); shape_data.path = Some(document.get_path_for_new_layer()); responses.push_back(DocumentMessage::DeselectAllLayers.into()); diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index 144544ec..30a5c688 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -416,9 +416,9 @@ struct GradientToolData { drag_start: DVec2, } -pub fn start_snap(snap_manager: &mut SnapManager, document: &DocumentMessageHandler, font_cache: &FontCache) { - snap_manager.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); - snap_manager.add_all_document_handles(document, &[], &[], &[]); +pub fn start_snap(snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, font_cache: &FontCache) { + snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true); + snap_manager.add_all_document_handles(document, input, &[], &[], &[]); } impl Fsm for GradientToolFsmState { @@ -573,7 +573,7 @@ impl Fsm for GradientToolFsmState { ] { if pos.distance_squared(mouse) < tolerance { dragging = true; - start_snap(&mut tool_data.snap_manager, document, font_cache); + start_snap(&mut tool_data.snap_manager, document, input, font_cache); tool_data.selected_gradient = Some(SelectedGradient { path: overlay.path.clone(), transform: overlay.transform, @@ -621,7 +621,7 @@ impl Fsm for GradientToolFsmState { tool_data.selected_gradient = Some(selected_gradient); - start_snap(&mut tool_data.snap_manager, document, font_cache); + start_snap(&mut tool_data.snap_manager, document, input, font_cache); GradientToolFsmState::Drawing } else { diff --git a/editor/src/messages/tool/tool_messages/imaginate_tool.rs b/editor/src/messages/tool/tool_messages/imaginate_tool.rs index 66c639fd..0b12dfc1 100644 --- a/editor/src/messages/tool/tool_messages/imaginate_tool.rs +++ b/editor/src/messages/tool/tool_messages/imaginate_tool.rs @@ -133,7 +133,7 @@ impl Fsm for ImaginateToolFsmState { if let ToolMessage::Imaginate(event) = event { match (self, event) { (Ready, DragStart) => { - shape_data.start(responses, document, input.mouse.position, font_cache); + shape_data.start(responses, document, input, font_cache); responses.push_back(DocumentMessage::StartTransaction.into()); responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: true }.into()); shape_data.path = Some(document.get_path_for_new_layer()); diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index b6011384..984eeaaf 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -180,8 +180,8 @@ impl Fsm for LineToolFsmState { if let ToolMessage::Line(event) = event { match (self, event) { (Ready, DragStart) => { - tool_data.snap_manager.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); - tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]); + tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true); + tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); tool_data.drag_start = tool_data.snap_manager.snap_position(responses, document, input.mouse.position); responses.push_back(DocumentMessage::StartTransaction.into()); diff --git a/editor/src/messages/tool/tool_messages/node_graph_frame_tool.rs b/editor/src/messages/tool/tool_messages/node_graph_frame_tool.rs index 03e25495..ef0fef7a 100644 --- a/editor/src/messages/tool/tool_messages/node_graph_frame_tool.rs +++ b/editor/src/messages/tool/tool_messages/node_graph_frame_tool.rs @@ -132,7 +132,7 @@ impl Fsm for NodeGraphToolFsmState { if let ToolMessage::NodeGraphFrame(event) = event { match (self, event) { (Ready, DragStart) => { - shape_data.start(responses, document, input.mouse.position, font_cache); + shape_data.start(responses, document, input, font_cache); responses.push_back(DocumentMessage::StartTransaction.into()); responses.push_back(NodeGraphMessage::SetDrawing { new_drawing: true }.into()); shape_data.path = Some(document.get_path_for_new_layer()); diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 3f63a249..7b0a4fe1 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -188,7 +188,7 @@ impl Fsm for PathToolFsmState { let ignore_document = tool_data.shape_editor.selected_layers().clone(); tool_data .snap_manager - .start_snap(document, document.bounding_boxes(Some(&ignore_document), None, font_cache), true, true); + .start_snap(document, input, document.bounding_boxes(Some(&ignore_document), None, font_cache), true, true); // Do not snap against handles when anchor is selected let mut extension = Vec::new(); @@ -201,7 +201,7 @@ impl Fsm for PathToolFsmState { new_selected.extend(extension); let include_handles = tool_data.shape_editor.selected_layers_ref(); - tool_data.snap_manager.add_all_document_handles(document, &include_handles, &[], &new_selected); + tool_data.snap_manager.add_all_document_handles(document, input, &include_handles, &[], &new_selected); tool_data.drag_start_pos = input.mouse.position - offset; PathToolFsmState::Dragging diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index e2b9c0b0..32bf2af3 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -214,8 +214,8 @@ impl Fsm for PenToolFsmState { responses.push_back(DocumentMessage::StartTransaction.into()); // Initialize snapping - tool_data.snap_manager.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); - tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]); + tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true); + tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); // Disable this tool's mirroring tool_data.should_mirror = false; diff --git a/editor/src/messages/tool/tool_messages/rectangle_tool.rs b/editor/src/messages/tool/tool_messages/rectangle_tool.rs index 61046f7a..6e4039f4 100644 --- a/editor/src/messages/tool/tool_messages/rectangle_tool.rs +++ b/editor/src/messages/tool/tool_messages/rectangle_tool.rs @@ -133,7 +133,7 @@ impl Fsm for RectangleToolFsmState { if let ToolMessage::Rectangle(event) = event { match (self, event) { (Ready, DragStart) => { - shape_data.start(responses, document, input.mouse.position, font_cache); + shape_data.start(responses, document, input, font_cache); responses.push_back(DocumentMessage::StartTransaction.into()); shape_data.path = Some(document.get_path_for_new_layer()); responses.push_back(DocumentMessage::DeselectAllLayers.into()); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index a54be1ae..aeeb41b6 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -475,8 +475,8 @@ impl Fsm for SelectToolFsmState { let state = if tool_data.pivot.is_over(input.mouse.position) { responses.push_back(DocumentMessage::StartTransaction.into()); - tool_data.snap_manager.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); - tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]); + tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true); + tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); DraggingPivot } else if let Some(selected_edges) = dragging_bounds { @@ -485,10 +485,12 @@ impl Fsm for SelectToolFsmState { let snap_x = selected_edges.2 || selected_edges.3; let snap_y = selected_edges.0 || selected_edges.1; - tool_data.snap_manager.start_snap(document, document.bounding_boxes(Some(&selected), None, font_cache), snap_x, snap_y); tool_data .snap_manager - .add_all_document_handles(document, &[], &selected.iter().map(|x| x.as_slice()).collect::>(), &[]); + .start_snap(document, input, document.bounding_boxes(Some(&selected), None, font_cache), snap_x, snap_y); + tool_data + .snap_manager + .add_all_document_handles(document, input, &[], &selected.iter().map(|x| x.as_slice()).collect::>(), &[]); tool_data.layers_dragging = selected; @@ -521,7 +523,7 @@ impl Fsm for SelectToolFsmState { tool_data .snap_manager - .start_snap(document, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true); + .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true); Dragging } else { @@ -537,7 +539,7 @@ impl Fsm for SelectToolFsmState { tool_data.layers_dragging.append(&mut selected); tool_data .snap_manager - .start_snap(document, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true); + .start_snap(document, input, document.bounding_boxes(Some(&tool_data.layers_dragging), None, font_cache), true, true); Dragging } else { diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index 65f48e73..e1a6e0e2 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -174,7 +174,7 @@ impl Fsm for ShapeToolFsmState { if let ToolMessage::Shape(event) = event { match (self, event) { (Ready, DragStart) => { - shape_data.start(responses, document, input.mouse.position, font_cache); + shape_data.start(responses, document, input, font_cache); responses.push_back(DocumentMessage::StartTransaction.into()); shape_data.path = Some(document.get_path_for_new_layer()); responses.push_back(DocumentMessage::DeselectAllLayers.into()); diff --git a/editor/src/messages/tool/tool_messages/spline_tool.rs b/editor/src/messages/tool/tool_messages/spline_tool.rs index cd439c97..3b54799b 100644 --- a/editor/src/messages/tool/tool_messages/spline_tool.rs +++ b/editor/src/messages/tool/tool_messages/spline_tool.rs @@ -186,8 +186,8 @@ impl Fsm for SplineToolFsmState { responses.push_back(DocumentMessage::DeselectAllLayers.into()); tool_data.path = Some(document.get_path_for_new_layer()); - tool_data.snap_manager.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true); - tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]); + tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(None, None, font_cache), true, true); + tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]); let snapped_position = tool_data.snap_manager.snap_position(responses, document, input.mouse.position); let pos = transform.inverse().transform_point2(snapped_position);