mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 21:10:40 +00:00
Swatches: a new swatch holds no link or placement from the paint it is made from
swatch.new stored a gradient or pattern as it was given. A gradient swatch made from a gradient swatch by name, from a fill linked to one, or from a gradient that names one (the Gradient panel's add to swatches) kept the link to that swatch, so copying art that uses the new swatch brought the other swatch along too. A pattern swatch made from a current fill picked up from art with transformed tiles kept that transform, and art the swatch was applied to took it in place of its own. The new swatch now drops the gradient's link and stores the pattern untransformed. A gradient swatch keeps its aspect as before.
This commit is contained in:
@@ -19,7 +19,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"New Swatch",
|
||||
["Window", "Swatches"],
|
||||
None,
|
||||
"{name?, color? | colors?: [colour] (one swatch each, in one undo step; #rrggbbaa and #rgba are refused) | swatch? | gradient? | pattern?: name (default: the current fill), tint?: 0..100 (% of the global or spot `swatch`, or of the one the current fill links to), mode?: \"gray\"|\"rgb\"|\"hsb\"|\"lab\"|\"cmyk\"|\"web\" (convert the colour; lab: CIE L*a*b*, how spot colours are usually defined), global?, spot? (a spot colour, always global), group?: colour group name (solid colours only; default: ungrouped), none?: true (the None paint), paint?: paint.setFill params, keepModel?: bool (as paint.setFill)} save a colour, gradient or pattern as a swatch. A tint of a global or spot colour (below 100%, without mode, global or spot) becomes a tint swatch, \"Name 40%\", linked to its base: it follows edits to the base and applies as that tint of it. Names are unique (\"Sky 2\"); a colour's default name is its values (\"C=10 M=20 Y=30 K=0\", \"R=255 G=128 B=0\", \"Gray K=40\", \"L=55 a=60 b=40\") → {name, names: [every new swatch]}",
|
||||
"{name?, color? | colors?: [colour] (one swatch each, in one undo step; #rrggbbaa and #rgba are refused) | swatch? | gradient? | pattern?: name (default: the current fill), tint?: 0..100 (% of the global or spot `swatch`, or of the one the current fill links to), mode?: \"gray\"|\"rgb\"|\"hsb\"|\"lab\"|\"cmyk\"|\"web\" (convert the colour; lab: CIE L*a*b*, how spot colours are usually defined), global?, spot? (a spot colour, always global), group?: colour group name (solid colours only; default: ungrouped), none?: true (the None paint), paint?: paint.setFill params, keepModel?: bool (as paint.setFill)} save a colour, gradient or pattern as a swatch. A tint of a global or spot colour (below 100%, without mode, global or spot) becomes a tint swatch, \"Name 40%\", linked to its base: it follows edits to the base and applies as that tint of it. A gradient swatch holds no link to the swatch its gradient came from, and a pattern swatch holds the pattern untransformed. Names are unique (\"Sky 2\"); a colour's default name is its values (\"C=10 M=20 Y=30 K=0\", \"R=255 G=128 B=0\", \"Gray K=40\", \"L=55 a=60 b=40\") → {name, names: [every new swatch]}",
|
||||
has_doc,
|
||||
swatch_new
|
||||
),
|
||||
@@ -187,7 +187,11 @@ fn swatch_new(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
})),
|
||||
Paint::None => Err(bad(C, "a swatch needs a colour, gradient or pattern")),
|
||||
_ if spot || mode.is_some() || group.is_some() => Err(bad(C, "spot, mode and group apply to solid colours only")),
|
||||
other => Ok(other),
|
||||
// The swatch holds the gradient itself, not a link to the swatch it may have come from.
|
||||
// It keeps `geom`, which holds the aspect the swatch is applied with.
|
||||
Paint::Gradient(g) => Ok(Paint::Gradient(Box::new(vectorcraft_color::GradientPaint { swatch: None, ..*g }))),
|
||||
// The swatch holds the pattern untransformed, not the placement of the art it came from.
|
||||
Paint::Pattern { pattern, .. } => Ok(pattern_paint(&pattern)),
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
// A name applies to a single new swatch.
|
||||
|
||||
@@ -278,6 +278,54 @@ fn gradient_swatches_come_along_with_their_stop_links() {
|
||||
assert_eq!(sw.gradient, g.gradient, "the added gradient swatch links its stop to the added Sky too");
|
||||
}
|
||||
|
||||
/// A gradient swatch made from another one (by name, from a fill linked to it, or from a gradient
|
||||
/// that names it, placed or not) holds the gradient alone, so copying art that uses it brings only
|
||||
/// that swatch. A placed gradient keeps its aspect, and the swatch still fits each object it is
|
||||
/// applied to.
|
||||
#[test]
|
||||
fn a_gradient_swatch_made_from_another_holds_no_link_to_it() {
|
||||
let mut s = session();
|
||||
let glow = json!({"kind": "radial", "stops": [{"offset": 0, "color": "#3080ff"}, {"offset": 1, "color": "#ffffff"}]});
|
||||
s.execute("swatch.new", &json!({"name": "Glow", "gradient": glow})).unwrap();
|
||||
let r = rect(&mut s, 0.0, 0.0, 50.0, 50.0);
|
||||
s.execute("paint.setFill", &json!({"ids": [r.0], "swatch": "Glow"})).unwrap();
|
||||
let Paint::Gradient(applied) = fill(&s, r) else { panic!("a gradient fill") };
|
||||
assert_eq!(applied.swatch.as_deref(), Some("Glow"));
|
||||
let mut by_gradient = vectorcraft_tools::params::gradient_params(&applied);
|
||||
by_gradient["swatch"] = json!("Glow");
|
||||
let mut placed = by_gradient.clone();
|
||||
placed["start"] = json!([25, 25]);
|
||||
placed["end"] = json!([50, 25]);
|
||||
placed["aspect"] = json!(50);
|
||||
let made = [
|
||||
("By Name", json!({"name": "By Name", "swatch": "Glow"})),
|
||||
("From Fill", json!({"name": "From Fill"})),
|
||||
("From Gradient", json!({"name": "From Gradient", "gradient": by_gradient})),
|
||||
("Placed", json!({"name": "Placed", "gradient": placed})),
|
||||
];
|
||||
let mut links = vec![];
|
||||
for (name, p) in &made {
|
||||
s.execute("swatch.new", p).unwrap();
|
||||
let Paint::Gradient(sw) = doc(&s).swatch(name).unwrap().paint.clone() else { panic!("{name}: a gradient swatch") };
|
||||
assert_eq!(sw.gradient, applied.gradient, "{name}: Glow's gradient");
|
||||
links.push(sw.swatch.clone());
|
||||
}
|
||||
assert_eq!(links, [None, None, None, None], "no swatch links to Glow");
|
||||
let Paint::Gradient(sw) = doc(&s).swatch("Placed").unwrap().paint.clone() else { panic!("a gradient swatch") };
|
||||
assert!(sw.geom.is_some_and(|g| (g.aspect - 0.5).abs() < 1e-9), "the placed gradient keeps its aspect: {:?}", sw.geom);
|
||||
for (name, _) in &made {
|
||||
let t = rect(&mut s, 100.0, 0.0, 100.0, 60.0);
|
||||
s.execute("paint.setFill", &json!({"ids": [t.0], "swatch": name})).unwrap();
|
||||
if *name == "Placed" {
|
||||
let Paint::Gradient(g) = fill(&s, t) else { panic!("a gradient fill") };
|
||||
let geom = g.geom.unwrap();
|
||||
assert!(geom.start.distance(Point::new(150.0, 30.0)) < 1e-9 && (geom.aspect - 0.5).abs() < 1e-9, "{geom:?}");
|
||||
}
|
||||
copy(&mut s, &[t]);
|
||||
assert_eq!(s.clipboard.swatches.iter().map(|w| w.name.as_str()).collect::<Vec<_>>(), [*name], "Glow stays behind");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lab_spot_colours_show_as_the_target_documents_spot_option_says() {
|
||||
let mut s = session();
|
||||
|
||||
@@ -979,6 +979,32 @@ fn picking_another_pattern_swatch_keeps_the_pattern_transform() {
|
||||
assert_eq!(xf.as_coeffs(), [2.0, 0.0, 0.0, 2.0, 15.0, 10.0]);
|
||||
}
|
||||
|
||||
/// A pattern swatch made from the current fill holds the pattern untransformed: a fill picked up
|
||||
/// from art whose tiles were moved does not carry that placement to the art the swatch is applied
|
||||
/// to, which keeps its own (#1970).
|
||||
#[test]
|
||||
fn a_pattern_swatch_made_from_a_transformed_fill_holds_the_pattern_untransformed() {
|
||||
let mut s = session();
|
||||
make_dots(&mut s);
|
||||
let src = rect(&mut s, 100.0, 100.0, 50.0, 50.0);
|
||||
s.execute("paint.setFill", &json!({"ids": [src.0], "swatch": "Dots"})).unwrap();
|
||||
s.execute("pattern.transform", &json!({"ids": [src.0], "matrix": [2, 0, 0, 2, 15, 10]})).unwrap();
|
||||
sel(&mut s, &[]);
|
||||
s.execute("appearance.copyFrom", &json!({"source": src.0})).unwrap();
|
||||
let Paint::Pattern { xf, .. } = &s.paint.fill else { panic!("the picked-up pattern fill") };
|
||||
assert_eq!(xf.as_coeffs(), [2.0, 0.0, 0.0, 2.0, 15.0, 10.0], "the current fill has the tiles' placement");
|
||||
s.execute("swatch.new", &json!({"name": "Picked"})).unwrap();
|
||||
let Paint::Pattern { pattern, xf } = doc(&s).swatch("Picked").unwrap().paint.clone() else { panic!("a pattern swatch") };
|
||||
assert_eq!((pattern.as_str(), xf.as_coeffs()), ("Dots", [1.0, 0.0, 0.0, 1.0, 0.0, 0.0]));
|
||||
let dst = rect(&mut s, 300.0, 100.0, 50.0, 50.0);
|
||||
s.execute("paint.setFill", &json!({"ids": [dst.0], "color": "#00ff00"})).unwrap();
|
||||
s.execute("paint.setFill", &json!({"ids": [dst.0], "swatch": "Dots"})).unwrap();
|
||||
s.execute("pattern.transform", &json!({"ids": [dst.0], "matrix": [3, 0, 0, 3, 0, 0]})).unwrap();
|
||||
s.execute("paint.setFill", &json!({"ids": [dst.0], "swatch": "Picked"})).unwrap();
|
||||
let Paint::Pattern { xf, .. } = node(&s, dst).appearance.fill_paint() else { panic!("a pattern fill") };
|
||||
assert_eq!(xf.as_coeffs(), [3.0, 0.0, 0.0, 3.0, 0.0, 0.0], "the art keeps its own placement");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_glow_on_a_pattern_fill_stays_inside_the_object_on_the_threaded_renderer() {
|
||||
// #2426: the glow's pixmap was drawn with the pattern's paint transform, so it landed
|
||||
|
||||
+2
-1
@@ -597,7 +597,8 @@ global or spot swatch follow its colour and name, as one undo step, in the art,
|
||||
designs, pattern tiles, graphic styles and the art of brushes); `swatch.delete {names, unlink?}` deletes
|
||||
swatches and colour groups in one undo step (art using a deleted global swatch keeps its colour, unlinked).
|
||||
`swatch.new {color?, mode?, spot?, global?, group?}` saves a colour (into a colour group with `group`), gradient
|
||||
or pattern; `swatch.newGroup {fromArtwork: true, toGlobal?, includeTints?}` makes a colour group of the selected
|
||||
or pattern (a gradient swatch holds no link to the swatch its gradient came from, and a pattern swatch holds the
|
||||
pattern untransformed); `swatch.newGroup {fromArtwork: true, toGlobal?, includeTints?}` makes a colour group of the selected
|
||||
art's colours (by default as global swatches the art links to).
|
||||
`swatch.move {names, to?, group?}` reorders swatches or moves them into or out of a colour group (as dragging
|
||||
them in the Swatches panel does); give colour group names instead to reorder the groups. Dropping a swatch on art
|
||||
|
||||
Reference in New Issue
Block a user