Recolor: Global Adjust gives linked tints of a rewritten group their adjusted color

A tint linked to a global swatch of the color group that recolor.apply
rewrites kept its link and showed the swatch's adjusted color at its
tint, which differs from the same color adjusted on its own. It now
keeps the link only when the two match, and otherwise takes the
adjusted color, unlinked.
This commit is contained in:
dotCooCoo
2026-11-01 18:10:13 -08:00
parent 0567eaffbf
commit d46284c52c
3 changed files with 39 additions and 4 deletions
+6
View File
@@ -244,6 +244,12 @@ pub fn is_tint(c: Color, base: Color) -> bool {
a.iter().zip(b).all(|(x, y)| (x - y * t).abs() <= 0.01)
}
/// Whether `a` and `b` carry the same ink in `a`'s model, within [`is_tint`]'s tolerance.
pub fn same_ink(a: Color, b: Color) -> bool {
let (x, y) = (ink(a, a.model()), ink(b, a.model()));
x.iter().zip(y).all(|(p, q)| (p - q).abs() <= 0.01)
}
/// `c` darkened by `f` (0..1) toward black: more K for CMYK and Gray, scaled RGB and Lab.
pub fn shaded(c: Color, f: f32) -> Color {
let f = f.clamp(0.0, 1.0);
+5 -4
View File
@@ -7,7 +7,7 @@
use std::collections::{BTreeSet, HashMap};
use serde_json::{Value, json};
use vectorcraft_color::recolor::{ColorKey, GlobalAdjust, Method, Preserve, Rng, RowMap, cluster};
use vectorcraft_color::recolor::{ColorKey, GlobalAdjust, Method, Preserve, Rng, RowMap, cluster, same_ink};
use vectorcraft_color::{Color, Paint, Swatch};
use vectorcraft_doc::swatches::{node_colors, node_paints};
use vectorcraft_doc::{Document, Node, NodeId, NodeKind};
@@ -452,7 +452,8 @@ impl ColorMap {
/// stay unlinked), a
/// process swatch gives its colour in its own model; a color linked to a global swatch of the
/// rewritten group that takes the row's new color keeps its link (at its tint in the row) when
/// the row gives it the new color in that color's model;
/// the row gives it the new color in that color's model and the Global Adjust gives it the
/// swatch's new color at that tint;
/// other rows map the colour, which loses its link when it changes or when the group's rewrite
/// would change it. True when anything changed.
fn map_link(&self, c: &mut Color, link: &mut Option<String>, tint: &mut f32) -> bool {
@@ -462,9 +463,9 @@ impl ColorMap {
&& let Some(g) = link.as_deref().and_then(|n| self.group.iter().find(|g| g.name == n))
&& self.tos.get(i) == Some(&g.color)
&& !row.converts(*c)
&& let Some(t) = row.tint(*c)
&& let Some(t) = row.tint(*c).map(|t| t.clamp(0.0, 1.0))
&& (self.adjust.is_identity() || same_ink(self.adjust.apply(row.map(*c)), g.shown.tinted(t)))
{
let t = t.clamp(0.0, 1.0);
let shown = g.shown.tinted(t);
let changed = *tint != t || *c != shown;
(*c, *tint) = (shown, t);
+28
View File
@@ -812,6 +812,34 @@ fn global_adjust_with_a_library_and_a_group() {
assert_eq!(c, fill(&s, art), "as the art is");
}
/// Art linked to a global swatch of the rewritten group takes the same Global Adjust as unlinked
/// art of its color: at 100% it keeps the link, and a tint whose adjusted color is not the
/// swatch's new color at that tint takes the adjusted color and loses the link.
#[test]
fn global_adjust_gives_linked_tints_of_a_group_the_adjusted_color() {
let mut s = session();
run(&mut s, "swatch.newGroup", json!({"name": "Theme", "colors": ["#ff0000"]}));
let name = s.doc().unwrap().doc.swatch_groups.iter().find(|g| g.name == "Theme").unwrap().swatches[0].name.clone();
run(&mut s, "swatch.edit", json!({"name": name, "global": true}));
let full = rect(&mut s, json!("#000000"));
let tint = rect(&mut s, json!("#000000"));
run(&mut s, "paint.setFill", json!({"ids": [full.0], "swatch": name}));
run(&mut s, "paint.setFill", json!({"ids": [tint.0], "swatch": name, "tint": 50}));
let link = |s: &Session, id: NodeId| match s.doc().unwrap().doc.node(id).unwrap().appearance.fill_paint() {
vectorcraft_color::Paint::Solid { color, swatch, tint } => (color, swatch, tint),
p => panic!("{p:?}"),
};
select_all(&mut s);
let tint_key = vectorcraft_color::recolor::ColorKey::of(&fill(&s, tint)).to_string();
let rows = json!([{"from": ["rgb 255 0 0", tint_key], "to": "#ff0000"}]);
run(&mut s, "recolor.apply", json!({"map": rows, "method": "scaleTints", "group": "Theme", "globalAdjust": {"brightness": -50}}));
let (c, w, t) = link(&s, full);
assert!(close(c, Color::rgb(0.5, 0.0, 0.0)) && w.as_deref() == Some(name.as_str()) && t == 1.0, "{c:?} {w:?} {t}");
let (c, w, _) = link(&s, tint);
assert!(close(c, Color::rgb(0.5, 0.25, 0.25)), "the tint's own color, adjusted: {c:?}");
assert_eq!(w, None, "the adjusted tint is not a tint of the swatch's new color");
}
/// `globalAdjust` must be an object of numbers from -100 to 100; a refusal changes nothing and
/// records no undo step. A null counts as left out.
#[test]