Skip to content

Commit 40c6c61

Browse files
authored
Fix snapping when drawing an artboard (#3312)
* Fix snapping when drawing an artboard * Fix typo in test
1 parent e5f40a3 commit 40c6c61

File tree

2 files changed

+88
-35
lines changed

2 files changed

+88
-35
lines changed

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

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use crate::messages::tool::common_functionality::resize::Resize;
99
use crate::messages::tool::common_functionality::snapping;
1010
use crate::messages::tool::common_functionality::snapping::SnapCandidatePoint;
1111
use crate::messages::tool::common_functionality::snapping::SnapData;
12-
use crate::messages::tool::common_functionality::snapping::SnapManager;
1312
use crate::messages::tool::common_functionality::transformation_cage::*;
1413
use graph_craft::document::NodeId;
1514
use graphene_std::Artboard;
@@ -108,7 +107,6 @@ impl Default for ArtboardToolFsmState {
108107
struct ArtboardToolData {
109108
bounding_box_manager: Option<BoundingBoxManager>,
110109
selected_artboard: Option<LayerNodeIdentifier>,
111-
snap_manager: SnapManager,
112110
cursor: MouseCursorIcon,
113111
drag_start: DVec2,
114112
drag_current: DVec2,
@@ -188,7 +186,7 @@ impl ArtboardToolData {
188186
let center = from_center.then_some(bounds.center_of_transformation);
189187
let ignore = self.selected_artboard.map_or(Vec::new(), |layer| vec![layer]);
190188
let snap = Some(SizeSnapData {
191-
manager: &mut self.snap_manager,
189+
manager: &mut self.draw.snap_manager,
192190
points: &mut self.snap_candidates,
193191
snap_data: SnapData::ignore(document, input, &ignore),
194192
});
@@ -275,7 +273,7 @@ impl Fsm for ArtboardToolFsmState {
275273
}
276274
}
277275

278-
tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
276+
tool_data.draw.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context);
279277

280278
self
281279
}
@@ -322,7 +320,7 @@ impl Fsm for ArtboardToolFsmState {
322320
let snap_data = SnapData::ignore(document, input, &ignore);
323321
let document_to_viewport = document.metadata().document_to_viewport;
324322
let [start, current] = [tool_data.drag_start, tool_data.drag_current].map(|point| document_to_viewport.transform_point2(point));
325-
let mouse_delta = snap_drag(start, current, axis_align, Axis::None, snap_data, &mut tool_data.snap_manager, &tool_data.snap_candidates);
323+
let mouse_delta = snap_drag(start, current, axis_align, Axis::None, snap_data, &mut tool_data.draw.snap_manager, &tool_data.snap_candidates);
326324

327325
let size = bounds.bounds[1] - bounds.bounds[0];
328326
let position = bounds.bounds[0] + bounds.transform.inverse().transform_vector2(mouse_delta);
@@ -354,6 +352,8 @@ impl Fsm for ArtboardToolFsmState {
354352
ArtboardToolFsmState::Dragging
355353
}
356354
(ArtboardToolFsmState::Drawing, ArtboardToolMessage::PointerMove { constrain_axis_or_aspect, center }) => {
355+
// The draw.calculate_points_ignore_layer uses this value to avoid snapping to itself.
356+
tool_data.draw.layer = tool_data.selected_artboard;
357357
let [start, end] = tool_data.draw.calculate_points_ignore_layer(document, input, center, constrain_axis_or_aspect, true);
358358
let viewport_to_document = document.metadata().document_to_viewport.inverse();
359359
let [start, end] = [start, end].map(|point| viewport_to_document.transform_point2(point));
@@ -400,11 +400,11 @@ impl Fsm for ArtboardToolFsmState {
400400
.map_or(MouseCursorIcon::Default, |bounds| bounds.get_cursor(input, false, false, None));
401401

402402
if cursor == MouseCursorIcon::Default && !hovered {
403-
tool_data.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position);
403+
tool_data.draw.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position);
404404
responses.add(OverlaysMessage::Draw);
405405
cursor = MouseCursorIcon::Crosshair;
406406
} else {
407-
tool_data.snap_manager.cleanup(responses);
407+
tool_data.draw.cleanup(responses);
408408
}
409409

410410
if tool_data.cursor != cursor {
@@ -445,7 +445,7 @@ impl Fsm for ArtboardToolFsmState {
445445
(ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds | ArtboardToolFsmState::Dragging, ArtboardToolMessage::PointerUp) => {
446446
responses.add(DocumentMessage::EndTransaction);
447447

448-
tool_data.snap_manager.cleanup(responses);
448+
tool_data.draw.cleanup(responses);
449449

450450
if let Some(bounds) = &mut tool_data.bounding_box_manager {
451451
bounds.original_transforms.clear();
@@ -553,7 +553,7 @@ impl Fsm for ArtboardToolFsmState {
553553
(ArtboardToolFsmState::Dragging | ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds, ArtboardToolMessage::Abort) => {
554554
responses.add(DocumentMessage::AbortTransaction);
555555

556-
tool_data.snap_manager.cleanup(responses);
556+
tool_data.draw.cleanup(responses);
557557
responses.add(OverlaysMessage::Draw);
558558

559559
ArtboardToolFsmState::Ready { hovered }
@@ -611,29 +611,61 @@ mod test_artboard {
611611
.collect()
612612
}
613613

614+
#[derive(Debug, PartialEq)]
615+
struct ArtboardLayoutDocument {
616+
position: IVec2,
617+
dimensions: IVec2,
618+
}
619+
impl ArtboardLayoutDocument {
620+
pub fn new(position: impl Into<IVec2>, dimensions: impl Into<IVec2>) -> Self {
621+
Self {
622+
position: position.into(),
623+
dimensions: dimensions.into(),
624+
}
625+
}
626+
}
627+
628+
/// Check if all of the artboards exist in any ordering
629+
async fn has_artboards(editor: &mut EditorTestUtils, mut expected: Vec<ArtboardLayoutDocument>) {
630+
let artboards = get_artboards(editor)
631+
.await
632+
.iter()
633+
.map(|row| ArtboardLayoutDocument::new(row.element.location, row.element.dimensions))
634+
.collect::<Vec<_>>();
635+
assert_eq!(artboards.len(), expected.len(), "incorrect len: actual {:?}, expected {:?}", artboards, expected);
636+
637+
for artboard in artboards {
638+
let Some(index) = expected.iter().position(|expected| *expected == artboard) else {
639+
panic!("found {:?} that did not match any expected artboards\nexpected {:?}", artboard, expected);
640+
};
641+
expected.remove(index);
642+
}
643+
}
644+
614645
#[tokio::test]
615646
async fn artboard_draw_simple() {
616647
let mut editor = EditorTestUtils::create();
617648
editor.new_document().await;
618649
editor.drag_tool(ToolType::Artboard, 10.1, 10.8, 19.9, 0.2, ModifierKeys::empty()).await;
650+
has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((10, 0), (10, 11))]).await;
651+
}
619652

620-
let artboards = get_artboards(&mut editor).await;
621-
622-
assert_eq!(artboards.len(), 1);
623-
assert_eq!(artboards.get(0).unwrap().element.location, IVec2::new(10, 0));
624-
assert_eq!(artboards.get(0).unwrap().element.dimensions, IVec2::new(10, 11));
653+
#[tokio::test]
654+
async fn artboard_snapping() {
655+
let mut editor = EditorTestUtils::create();
656+
editor.new_document().await;
657+
editor.set_viewport_size(DVec2::splat(-1000.), DVec2::splat(1000.)).await; // Necessary for doing snapping since snaps outside of the viewport are discarded
658+
editor.drag_tool(ToolType::Artboard, 10., 10., 20., 20., ModifierKeys::empty()).await;
659+
editor.drag_tool(ToolType::Artboard, 11., 50., 19., 60., ModifierKeys::empty()).await;
660+
has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((10, 10), (10, 10)), ArtboardLayoutDocument::new((10, 50), (10, 10))]).await;
625661
}
626662

627663
#[tokio::test]
628664
async fn artboard_draw_square() {
629665
let mut editor = EditorTestUtils::create();
630666
editor.new_document().await;
631667
editor.drag_tool(ToolType::Artboard, 10., 10., -10., 11., ModifierKeys::SHIFT).await;
632-
633-
let artboards = get_artboards(&mut editor).await;
634-
assert_eq!(artboards.len(), 1);
635-
assert_eq!(artboards.get(0).unwrap().element.location, IVec2::new(-10, 10));
636-
assert_eq!(artboards.get(0).unwrap().element.dimensions, IVec2::new(20, 20));
668+
has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((-10, 10), (20, 20))]).await;
637669
}
638670

639671
#[tokio::test]
@@ -648,12 +680,9 @@ mod test_artboard {
648680
.await;
649681
// Viewport coordinates
650682
editor.drag_tool(ToolType::Artboard, 0., 0., 0., 10., ModifierKeys::SHIFT).await;
651-
652-
let artboards = get_artboards(&mut editor).await;
653-
assert_eq!(artboards.len(), 1);
654-
assert_eq!(artboards.get(0).unwrap().element.location, IVec2::new(0, 0));
655683
let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 10.);
656-
assert_eq!(artboards.get(0).unwrap().element.dimensions, desired_size.round().as_ivec2());
684+
685+
has_artboards(&mut editor, vec![ArtboardLayoutDocument::new(IVec2::new(0, 0), desired_size.round().as_ivec2())]).await;
657686
}
658687

659688
#[tokio::test]
@@ -669,12 +698,9 @@ mod test_artboard {
669698
.await;
670699
// Viewport coordinates
671700
editor.drag_tool(ToolType::Artboard, 0., 0., 0., 10., ModifierKeys::SHIFT | ModifierKeys::ALT).await;
672-
673-
let artboards = get_artboards(&mut editor).await;
674-
assert_eq!(artboards.len(), 1);
675-
assert_eq!(artboards.get(0).unwrap().element.location, DVec2::splat(f64::consts::FRAC_1_SQRT_2 * -10.).as_ivec2());
676-
let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 20.);
677-
assert_eq!(artboards.get(0).unwrap().element.dimensions, desired_size.round().as_ivec2());
701+
let desired_location = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * -10.).as_ivec2();
702+
let desired_size = DVec2::splat(f64::consts::FRAC_1_SQRT_2 * 20.).as_ivec2();
703+
has_artboards(&mut editor, vec![ArtboardLayoutDocument::new(desired_location, desired_size)]).await;
678704
}
679705

680706
#[tokio::test]
@@ -685,8 +711,7 @@ mod test_artboard {
685711
editor.drag_tool(ToolType::Artboard, 10.1, 10.8, 19.9, 0.2, ModifierKeys::default()).await;
686712
editor.press(Key::Delete, ModifierKeys::default()).await;
687713

688-
let artboards = get_artboards(&mut editor).await;
689-
assert_eq!(artboards.len(), 0);
714+
has_artboards(&mut editor, vec![]).await;
690715
}
691716

692717
#[tokio::test]
@@ -696,7 +721,27 @@ mod test_artboard {
696721
editor.new_document().await;
697722

698723
editor.drag_tool_cancel_rmb(ToolType::Artboard).await;
699-
let artboards = get_artboards(&mut editor).await;
700-
assert_eq!(artboards.len(), 0);
724+
has_artboards(&mut editor, vec![]).await;
725+
}
726+
727+
#[tokio::test]
728+
async fn artboard_move() {
729+
let mut editor = EditorTestUtils::create();
730+
editor.new_document().await;
731+
editor.drag_tool(ToolType::Artboard, 10., 10., 20., 22., ModifierKeys::empty()).await; // Artboard to drag
732+
editor.drag_tool(ToolType::Artboard, 15., 15., 65., 65., ModifierKeys::empty()).await; // Drag from the middle by (50,50)
733+
734+
has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((60, 60), (10, 12))]).await;
735+
}
736+
#[tokio::test]
737+
async fn artboard_move_snapping() {
738+
let mut editor = EditorTestUtils::create();
739+
editor.new_document().await;
740+
editor.set_viewport_size(DVec2::splat(-1000.), DVec2::splat(1000.)).await; // Necessary for doing snapping since snaps outside of the viewport are discarded
741+
editor.drag_tool(ToolType::Artboard, 10., 10., 20., 22., ModifierKeys::empty()).await; // Artboard to drag
742+
editor.drag_tool(ToolType::Artboard, 70., 0., 80., 100., ModifierKeys::empty()).await; // Artboard to snap to
743+
editor.drag_tool(ToolType::Artboard, 15., 15., 15. + 49., 15., ModifierKeys::empty()).await; // Drag the artboard so it should snap to the edge
744+
745+
has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((60, 10), (10, 12)), ArtboardLayoutDocument::new((70, 0), (10, 100))]).await;
701746
}
702747
}

editor/src/test_utils.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::application::Editor;
22
use crate::application::set_uuid_seed;
33
use crate::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
4+
use crate::messages::input_mapper::utility_types::input_mouse::ViewportBounds;
45
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition};
56
use crate::messages::portfolio::utility_types::Platform;
67
use crate::messages::prelude::*;
@@ -32,7 +33,6 @@ impl EditorTestUtils {
3233
let _ = GLOBAL_PLATFORM.set(Platform::Windows).is_ok();
3334

3435
editor.handle_message(PortfolioMessage::Init);
35-
3636
Self { editor, runtime }
3737
}
3838

@@ -293,6 +293,14 @@ impl EditorTestUtils {
293293
)
294294
.await;
295295
}
296+
297+
/// Necessary for doing snapping since snaps outside of the viewport are discarded
298+
pub async fn set_viewport_size(&mut self, top_left: DVec2, bottom_right: DVec2) {
299+
self.handle_message(InputPreprocessorMessage::BoundsOfViewports {
300+
bounds_of_viewports: vec![ViewportBounds { top_left, bottom_right }],
301+
})
302+
.await;
303+
}
296304
}
297305

298306
pub trait FrontendMessageTestUtils {

0 commit comments

Comments
 (0)