mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-31 13:17:19 -06:00
Perspective Selection: Cmd/Ctrl+Alt-drag copies objects in perspective
With the Perspective Selection tool, Ctrl+Alt-drag (Command+Option-drag on macOS) now copies objects on their perspective plane, as Alt/Option-drag does. Before, Cmd/Ctrl lent the last selection tool even with Alt held, so the drag made a flat copy. Cmd/Ctrl alone still lends the last selection tool, and the cursor follows the same rule.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
use serde_json::json;
|
||||
use vectorcraft_geom::{Point, Rect, Vec2};
|
||||
use vectorcraft_tools::distort::perspective::{self as persp, PerspectiveGrid, Plane};
|
||||
use vectorcraft_tools::{Mods, PointerEvent, PointerKind, ToolKey};
|
||||
use vectorcraft_tools::{Cursor, Mods, PointerEvent, PointerKind, ToolKey};
|
||||
|
||||
use crate::*;
|
||||
|
||||
@@ -111,6 +111,65 @@ fn alt_drag_copies_and_the_copy_stays_attached() {
|
||||
assert!(s.doc().unwrap().doc.node(copy).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmd_alt_drag_copies_on_the_perspective_plane() {
|
||||
let (mut s, id) = persp_session();
|
||||
let v = ViewInfo::default();
|
||||
s.select_tool("directSelection", v).unwrap();
|
||||
s.select_tool("perspectiveSelection", v).unwrap();
|
||||
let before = plane_pts(&s, id, Plane::Right, 0.0);
|
||||
let count = s.doc().unwrap().doc.node_count();
|
||||
let c = bounds_of(&before).center();
|
||||
let h = grid(&s).homography(Plane::Right).unwrap();
|
||||
let (from, to) = (h.apply(c).unwrap(), h.apply(c + Vec2::new(80.0, 0.0)).unwrap());
|
||||
let n = undo_len(&s);
|
||||
drag(&mut s, &[from, to, to], Mods { cmd: true, alt: true, ..Default::default() });
|
||||
assert_eq!(undo_len(&s), n + 1, "one undo step");
|
||||
assert_eq!(s.doc().unwrap().doc.node_count(), count + 1, "one copy");
|
||||
assert_eq!(s.doc().unwrap().selection.objects.len(), 1);
|
||||
let copy = s.doc().unwrap().selection.objects[0];
|
||||
assert_ne!(copy, id, "the copy is selected");
|
||||
assert_eq!(plane_pts(&s, id, Plane::Right, 0.0), before, "the original stays");
|
||||
assert_eq!(attached(&s, copy), Some((Plane::Right, 0.0)), "the copy is attached");
|
||||
for (a, b) in before.iter().zip(plane_pts(&s, copy, Plane::Right, 0.0)) {
|
||||
assert!((b - *a - Vec2::new(80.0, 0.0)).hypot() < 1e-6);
|
||||
}
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().doc.node_count(), count);
|
||||
assert_eq!(plane_pts(&s, id, Plane::Right, 0.0), before);
|
||||
assert_eq!(s.doc().unwrap().selection.objects, vec![id]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmd_drag_still_lends_selection_from_perspective_selection() {
|
||||
let (mut s, id) = persp_session();
|
||||
let v = ViewInfo::default();
|
||||
s.select_tool("directSelection", v).unwrap();
|
||||
s.select_tool("perspectiveSelection", v).unwrap();
|
||||
let p = s.doc().unwrap().doc.node(id).unwrap().geometric_bounds().unwrap().center();
|
||||
let cmd = Mods { cmd: true, ..Default::default() };
|
||||
s.pointer(&PointerEvent::new(PointerKind::Down, p.x, p.y).with_mods(cmd), v).unwrap();
|
||||
assert_eq!(s.tool_id(), "directSelection");
|
||||
s.pointer(&PointerEvent::new(PointerKind::Up, p.x, p.y).with_mods(cmd), v).unwrap();
|
||||
assert_eq!(s.tool_id(), "perspectiveSelection");
|
||||
let cmd_alt = Mods { cmd: true, alt: true, ..Default::default() };
|
||||
s.pointer(&PointerEvent::new(PointerKind::Down, p.x, p.y).with_mods(cmd_alt), v).unwrap();
|
||||
assert_eq!(s.tool_id(), "perspectiveSelection");
|
||||
s.pointer(&PointerEvent::new(PointerKind::Up, p.x, p.y).with_mods(cmd_alt), v).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmd_alt_cursor_stays_with_perspective_selection() {
|
||||
let (mut s, id) = persp_session();
|
||||
let v = ViewInfo::default();
|
||||
s.select_tool("directSelection", v).unwrap();
|
||||
s.select_tool("perspectiveSelection", v).unwrap();
|
||||
let p = s.doc().unwrap().doc.node(id).unwrap().geometric_bounds().unwrap().center();
|
||||
let cmd_alt = Mods { cmd: true, alt: true, ..Default::default() };
|
||||
assert_eq!(s.cursor(p, cmd_alt, v), Cursor::Arrow);
|
||||
assert_eq!(s.tool_id(), "perspectiveSelection");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pressing_5_while_dragging_moves_perpendicular_to_the_plane() {
|
||||
let (mut s, id) = persp_session();
|
||||
|
||||
@@ -147,6 +147,7 @@ impl Session {
|
||||
/// Direct Selection to the tools that edit anchors (the Pen) and Selection to the others.
|
||||
/// Cmd+Alt lends Group Selection while Direct Selection is active. Cmd still lends the last
|
||||
/// selection tool from other tools (Alt on the lent Direct Selection gesture stays copy/subtract).
|
||||
/// Perspective Selection keeps Cmd+Alt drags to copy objects on their perspective plane.
|
||||
/// None for the other selection tools and the view tools.
|
||||
fn lent_tool(&self, id: &str, mods: &Mods) -> Option<&'static str> {
|
||||
use vectorcraft_tools::catalog::{edits_anchors, is_selection_tool};
|
||||
@@ -156,6 +157,9 @@ impl Session {
|
||||
if id == "directSelection" && mods.cmd && mods.alt {
|
||||
return Some("groupSelection");
|
||||
}
|
||||
if id == "perspectiveSelection" && mods.cmd && mods.alt {
|
||||
return None;
|
||||
}
|
||||
if is_selection_tool(id) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -1089,6 +1089,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
In the web build, while a pan, zoom or rotation changes the view of a document whose last canvas render took more than 8 ms, the canvas draws the last frame moved with the view, and it renders the art again once the view has been still for 0.2 s.
|
||||
An Alt-drag with the Selection, Direct Selection, Group Selection, Rotate, Scale, Reflect or Shear tool copies the selection on its first move, and the moves after it transform those copies; the previews of the Move, Rotate, Scale, Reflect and Shear dialogs with Copy and Preview on copy the selection once in the same way (#1429).
|
||||
An Alt-drag of objects in perspective with the Perspective Selection tool, and an Alt-drag of a plane widget with that tool or the Perspective Grid tool, copy the objects on the first move, and the moves after it move those copies (#1429).
|
||||
With the Perspective Selection tool, Ctrl+Alt-drag (Command+Option-drag on macOS) also copies objects on their perspective plane; Alt/Option-drag continues to do so.
|
||||
When the desktop app opens a document on a thread of its own (#1245), that thread also reads the document's linked files and lays out its type, and the UI thread only adds the finished document: in release builds, the step left on the UI thread takes 0.02 ms instead of 1.04 s for a document with 12 linked 3000 × 2000 TIFFs, and it takes 0.1 ms instead of 0.85 s for one with 300 Auto Size area type objects, every second one in three columns.
|
||||
The Layers panel's Delete Selection, Duplicate Selection, Collect in New Layer, Merge Selected, Flatten Artwork, Release to Layers (Sequence) and row drags also take time in proportion to the size of the document, and so do Cut Artboard and Duplicate Artboard with their art and each move of an Alt-drag of one artboard with the Artboard tool while Move/Copy Artwork with Artboard is on (#1429).
|
||||
In Outline view the renderer draws paths and compound paths from the geometry it keeps for them between frames, and it culls the members of clip groups by the bounds it keeps for them. Layers with Preview off and the outlines of Image Trace objects are drawn the same way.
|
||||
|
||||
Reference in New Issue
Block a user