Files
vectorcraft/crates/engine/src/cmd/addons/select/tests.rs
T
ruchirguitarandClaude Opus 5.5 7e016a0bcc Select by Artboards: select objects overlapping, inside or outside several artboards (#5525)
addon.select.byArtboards {artboards, relation} in a new Selection Tools
add-on. Overlapping is All on Active Artboard's test, now one shared
helper (select::on_artboard); Fully inside is within one artboard, edges
included; Fully outside is on none. Objects are those Select All takes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 22:57:57 +07:00

110 lines
4.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Select by Artboards.
use serde_json::{Value, json};
use vectorcraft_doc::NodeId;
use crate::Session;
const C: &str = "addon.select.byArtboards";
/// A document with three 100 × 100 artboards in a row, 50 pt apart: 0 at x 0, 1 at x 150, 2 at x 300.
fn three_boards() -> Session {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 100, "height": 100})).unwrap();
for x in [150, 300] {
s.execute("artboard.new", &json!({"x": x, "y": 0, "width": 100, "height": 100})).unwrap();
}
s
}
fn rect(s: &mut Session, x: f64, y: f64, w: f64, h: f64) -> NodeId {
NodeId(s.execute("shape.rectangle", &json!({"x": x, "y": y, "width": w, "height": h})).unwrap()["id"].as_u64().unwrap())
}
fn pick(s: &mut Session, p: Value) -> Vec<NodeId> {
s.execute("select.none", &json!({})).unwrap();
let r = s.execute(C, &p).unwrap();
let mut ids = s.doc().unwrap().selection.objects.clone();
assert_eq!(r["count"], json!(ids.len()));
ids.sort();
ids
}
#[test]
fn selects_by_overlap_inside_and_outside_several_artboards() {
let mut s = three_boards();
let a = rect(&mut s, 10.0, 10.0, 20.0, 20.0); // inside 0
let ab = rect(&mut s, 90.0, 40.0, 80.0, 20.0); // across 0 and 1
let b = rect(&mut s, 160.0, 10.0, 20.0, 20.0); // inside 1
let c = rect(&mut s, 310.0, 10.0, 20.0, 20.0); // inside 2
let off = rect(&mut s, 0.0, 200.0, 20.0, 20.0); // on no board
let edge = rect(&mut s, 0.0, 0.0, 100.0, 100.0); // flush with board 0
let want = |mut v: Vec<NodeId>| {
v.sort();
v
};
assert_eq!(pick(&mut s, json!({"artboards": [0, 1]})), want(vec![a, ab, b, edge]));
assert_eq!(pick(&mut s, json!({"artboards": [0, 1], "relation": "overlap"})), want(vec![a, ab, b, edge]));
// Wholly within one board, edges included; straddling two boards is not inside either.
assert_eq!(pick(&mut s, json!({"artboards": [0, 1], "relation": "inside"})), want(vec![a, b, edge]));
assert_eq!(pick(&mut s, json!({"artboards": [0, 1], "relation": "outside"})), want(vec![c, off]));
assert_eq!(pick(&mut s, json!({"artboards": [2], "relation": "outside"})), want(vec![a, ab, b, off, edge]));
// One board does what All on Active Artboard does.
for i in 0..3u64 {
s.execute("select.allOnArtboard", &json!({"artboard": i})).unwrap();
let mut on = s.doc().unwrap().selection.objects.clone();
on.sort();
assert_eq!(pick(&mut s, json!({"artboards": [i]})), on, "board {i}");
}
// A board listed twice counts once.
assert_eq!(pick(&mut s, json!({"artboards": [2, 2]})), vec![c]);
}
#[test]
fn hidden_locked_and_isolated_art_follow_select_all() {
let mut s = three_boards();
let a = rect(&mut s, 10.0, 10.0, 20.0, 20.0);
let hidden = rect(&mut s, 40.0, 10.0, 20.0, 20.0);
let locked = rect(&mut s, 70.0, 10.0, 20.0, 20.0);
let far = rect(&mut s, 0.0, 300.0, 20.0, 20.0);
s.execute("object.setProps", &json!({"ids": [hidden.0], "visible": false})).unwrap();
s.execute("object.setProps", &json!({"ids": [locked.0], "locked": true})).unwrap();
assert_eq!(pick(&mut s, json!({"artboards": [0]})), vec![a]);
assert_eq!(pick(&mut s, json!({"artboards": [0], "relation": "outside"})), vec![far]);
// Art held by a locked artboard is left out.
s.execute("artboard.setProps", &json!({"index": 0, "locked": true})).unwrap();
assert_eq!(pick(&mut s, json!({"artboards": [0]})), vec![]);
s.execute("artboard.setProps", &json!({"index": 0, "locked": false})).unwrap();
// In isolation mode only the isolated group's objects are looked at.
let b = rect(&mut s, 20.0, 50.0, 10.0, 10.0);
s.execute("select.none", &json!({})).unwrap();
s.execute("select.set", &json!({"ids": [a.0, b.0]})).unwrap();
let g = s.execute("object.group", &json!({})).unwrap()["id"].as_u64().unwrap();
s.execute("object.isolate", &json!({"id": g})).unwrap();
let mut both = vec![a, b];
both.sort();
assert_eq!(pick(&mut s, json!({"artboards": [0], "relation": "inside"})), both);
assert_eq!(pick(&mut s, json!({"artboards": [1]})), vec![]);
}
#[test]
fn bad_params_are_errors_and_leave_the_selection() {
let mut s = three_boards();
let a = rect(&mut s, 10.0, 10.0, 20.0, 20.0);
for p in [
json!({}),
json!({"artboards": []}),
json!({"artboards": [3]}),
json!({"artboards": [-1]}),
json!({"artboards": [0.5]}),
json!({"artboards": "0"}),
json!({"artboards": [0], "relation": "near"}),
json!({"artboards": [0], "relation": 1}),
json!({"artboards": [0], "relatoin": "inside"}),
] {
s.execute("select.set", &json!({"ids": [a.0]})).unwrap();
assert!(s.execute(C, &p).is_err(), "{p}");
assert_eq!(s.doc().unwrap().selection.objects, vec![a], "{p}");
}
}