mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 13:47:21 -06:00
Merge pull request #4859 from dotCooCoo/fix/select-same-fill-live-paint
Select Same: Fill Color from a Live Paint face stays in its Live Paint group
This commit is contained in:
@@ -5,6 +5,7 @@ use std::collections::BTreeSet;
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_color::Paint;
|
||||
use vectorcraft_doc::{Document, Node, NodeId, NodeKind};
|
||||
use vectorcraft_tools::builder::is_live_paint;
|
||||
|
||||
use super::wand::{fill_of, stroke_of};
|
||||
use super::{
|
||||
@@ -106,11 +107,11 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Fill Color",
|
||||
["Select", "Same"],
|
||||
None,
|
||||
"{} the objects filled with the first selected object's fill colour (any tint of its global or spot swatch, the same tint with prefs selectSameTintPercent); a member path of a compound path selected first compares as its compound path, except in isolation mode inside that compound path",
|
||||
"{} the objects filled with the first selected object's fill colour (any tint of its global or spot swatch, the same tint with prefs selectSameTintPercent); a member path of a compound path selected first compares as its compound path, except in isolation mode inside that compound path; from a Live Paint face, the faces of its Live Paint group only, and from any other object, no Live Paint face",
|
||||
has_selection,
|
||||
|s, _| {
|
||||
let tint = s.prefs.select_same_tint_percent;
|
||||
same(s, "select.same.fillColor", |a, b| same_paint(&fill_of(a), &fill_of(b), tint))
|
||||
same_fill(s, "select.same.fillColor", |a, b| same_paint(&fill_of(a), &fill_of(b), tint))
|
||||
}
|
||||
),
|
||||
cmd!(
|
||||
@@ -135,11 +136,11 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Fill & Stroke",
|
||||
["Select", "Same"],
|
||||
None,
|
||||
"{} the objects with the first selected object's fill and stroke colours (tints and compound paths as select.same.fillColor)",
|
||||
"{} the objects with the first selected object's fill and stroke colours (tints, compound paths and Live Paint faces as select.same.fillColor)",
|
||||
has_selection,
|
||||
|s, _| {
|
||||
let tint = s.prefs.select_same_tint_percent;
|
||||
same(s, "select.same.fillAndStroke", |a, b| {
|
||||
same_fill(s, "select.same.fillAndStroke", |a, b| {
|
||||
same_paint(&fill_of(a), &fill_of(b), tint) && same_paint(&stroke_of(a).0, &stroke_of(b).0, tint)
|
||||
})
|
||||
}
|
||||
@@ -684,9 +685,30 @@ fn same_reference<'a>(st: &'a DocState, cmd: &str) -> Result<(&'a Node, bool)> {
|
||||
/// [`paint_reported`] accepts. A compound path matches as a whole. In isolation mode inside a
|
||||
/// compound path, its member paths are matched instead.
|
||||
fn same(s: &mut Session, cmd: &str, eq: impl Fn(&Node, &Node) -> bool) -> Result<Value> {
|
||||
let (refn, members) = same_reference(s.doc()?, cmd)?;
|
||||
same_in(s, cmd, false, eq)
|
||||
}
|
||||
|
||||
/// Select > Same > Fill Color and Fill & Stroke: [`same`], kept to one side of a Live Paint group.
|
||||
/// From a face or edge of a Live Paint group, only the faces and edges of that group match; from
|
||||
/// any other object, no face or edge of a Live Paint group does
|
||||
/// (<https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/select-objects-with-same-fill-and-stroke.html>).
|
||||
fn same_fill(s: &mut Session, cmd: &str, eq: impl Fn(&Node, &Node) -> bool) -> Result<Value> {
|
||||
same_in(s, cmd, true, eq)
|
||||
}
|
||||
|
||||
fn same_in(s: &mut Session, cmd: &str, live_paint: bool, eq: impl Fn(&Node, &Node) -> bool) -> Result<Value> {
|
||||
let st = s.doc()?;
|
||||
let (refn, members) = same_reference(st, cmd)?;
|
||||
let group = live_paint_group(&st.doc, refn.id);
|
||||
let refn = refn.clone();
|
||||
select_where(s, cmd, &json!({}), |d, n| (if members { !n.is_container() } else { paint_reported(d, n) }) && eq(n, &refn))
|
||||
select_where(s, cmd, &json!({}), |d, n| {
|
||||
(!live_paint || live_paint_group(d, n.id) == group) && (if members { !n.is_container() } else { paint_reported(d, n) }) && eq(n, &refn)
|
||||
})
|
||||
}
|
||||
|
||||
/// The Live Paint group `id` is a face or edge of.
|
||||
fn live_paint_group(d: &Document, id: NodeId) -> Option<NodeId> {
|
||||
d.parent_of(id).filter(|p| d.node(*p).is_some_and(is_live_paint))
|
||||
}
|
||||
|
||||
/// Select the objects [`walk_selectable`] visits that `f` accepts (not looking inside accepted
|
||||
|
||||
@@ -1813,6 +1813,48 @@ fn select_same_matches_compound_paths_on_their_own_paint() {
|
||||
assert_eq!(same(&mut s, outer, "select.same.shapeType"), vec![outer, inner]);
|
||||
}
|
||||
|
||||
/// Select > Same > Fill Color and Fill & Stroke from a Live Paint face select the faces of that
|
||||
/// Live Paint group with the same fill, and nothing outside it. From an object outside every Live
|
||||
/// Paint group, they select no face.
|
||||
#[test]
|
||||
fn select_same_fill_keeps_live_paint_faces_to_their_group() {
|
||||
let mut s = session();
|
||||
let live = |s: &mut Session, y: f64| {
|
||||
let a = rect(s, 0.0, y, 100.0, 100.0);
|
||||
let b = rect(s, 50.0, y, 100.0, 100.0);
|
||||
sel(s, &[a, b]);
|
||||
id_of(&s.execute("livePaint.make", &json!({})).unwrap())
|
||||
};
|
||||
let paint = |s: &mut Session, g: NodeId, x: f64, y: f64, hex: &str| {
|
||||
NodeId(s.execute("livePaint.fill", &json!({"group": g.0, "point": [x, y], "color": hex})).unwrap()["face"].as_u64().unwrap())
|
||||
};
|
||||
let g1 = live(&mut s, 0.0);
|
||||
let g2 = live(&mut s, 300.0);
|
||||
let [left, right] = [25.0, 125.0].map(|x| paint(&mut s, g1, x, 50.0, "#ff0000"));
|
||||
let middle = paint(&mut s, g1, 75.0, 50.0, "#0000ff");
|
||||
let other = paint(&mut s, g2, 25.0, 350.0, "#ff0000");
|
||||
let [r1, r2] = [400.0, 450.0].map(|x| rect(&mut s, x, 0.0, 10.0, 10.0));
|
||||
for r in [r1, r2] {
|
||||
fill(&mut s, r, "#ff0000");
|
||||
s.execute("paint.setStroke", &json!({"none": true, "ids": [r.0]})).unwrap();
|
||||
}
|
||||
let same = |s: &mut Session, from: NodeId, cmd: &str| {
|
||||
sel(s, &[from]);
|
||||
s.execute(cmd, &json!({})).unwrap();
|
||||
let mut v = selected(s);
|
||||
v.sort();
|
||||
v
|
||||
};
|
||||
for cmd in ["select.same.fillColor", "select.same.fillAndStroke"] {
|
||||
let mut faces = vec![left, right];
|
||||
faces.sort();
|
||||
assert_eq!(same(&mut s, left, cmd), faces, "{cmd} from a face");
|
||||
assert_eq!(same(&mut s, middle, cmd), vec![middle], "{cmd} from the blue face");
|
||||
assert_eq!(same(&mut s, other, cmd), vec![other], "{cmd} from the other group's face");
|
||||
assert_eq!(same(&mut s, r1, cmd), vec![r1, r2], "{cmd} from a path outside");
|
||||
}
|
||||
}
|
||||
|
||||
/// Select by Color needs no object to start from: it matches fills and strokes as
|
||||
/// `document.inspect` reports them, `#rrggbb`.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user