mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 18:27:02 -06:00
Keep the swatch link when blending two tints of one swatch (#3234)
This commit is contained in:
committed by
bflatastic
parent
fc644b9a9e
commit
ecd01ad6f1
+50
-9
@@ -44,11 +44,14 @@ fn lerp_f64(x: f64, y: f64, t: f64) -> f64 {
|
||||
x + (y - x) * t
|
||||
}
|
||||
|
||||
fn solid_gradient(c: Color, like: &Gradient) -> Gradient {
|
||||
/// `like` with every stop painting `c`, linked as the solid is (`swatch` at `tint`).
|
||||
fn solid_gradient(c: Color, swatch: Option<&str>, tint: f32, like: &Gradient) -> Gradient {
|
||||
let mut g = like.clone();
|
||||
for s in &mut g.stops {
|
||||
s.color = c;
|
||||
s.opacity = 1.0;
|
||||
s.swatch = swatch.map(str::to_string);
|
||||
s.tint = tint;
|
||||
}
|
||||
g
|
||||
}
|
||||
@@ -106,10 +109,19 @@ fn lerp_gradient(a: &GradientPaint, b: &GradientPaint, t: f32) -> Option<Gradien
|
||||
.stops
|
||||
.iter()
|
||||
.zip(&gb.stops)
|
||||
.map(|(sa, sb)| GradientStop {
|
||||
opacity: lerp_f32(sa.opacity, sb.opacity, t),
|
||||
midpoint: lerp_f32(sa.midpoint, sb.midpoint, t),
|
||||
..GradientStop::new(lerp_f32(sa.offset, sb.offset, t), lerp_color(&sa.color, &sb.color, t))
|
||||
.map(|(sa, sb)| {
|
||||
// Stops on two tints of one swatch stay linked to it, as solids do (#3234).
|
||||
let (swatch, tint) = match (&sa.swatch, &sb.swatch) {
|
||||
(Some(x), Some(y)) if x == y => (Some(x.clone()), lerp_f32(sa.tint, sb.tint, t)),
|
||||
_ => (None, 1.0),
|
||||
};
|
||||
GradientStop {
|
||||
opacity: lerp_f32(sa.opacity, sb.opacity, t),
|
||||
midpoint: lerp_f32(sa.midpoint, sb.midpoint, t),
|
||||
swatch,
|
||||
tint,
|
||||
..GradientStop::new(lerp_f32(sa.offset, sb.offset, t), lerp_color(&sa.color, &sb.color, t))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
out.angle = a.angle + (b.angle - a.angle) * t as f64;
|
||||
@@ -131,17 +143,21 @@ fn lerp_gradient(a: &GradientPaint, b: &GradientPaint, t: f32) -> Option<Gradien
|
||||
/// solid↔gradient; otherwise switch halfway.
|
||||
pub fn lerp_paint(a: &Paint, b: &Paint, t: f32) -> Paint {
|
||||
match (a, b) {
|
||||
// Two tints of one swatch blend into tints of that swatch (a spot stays a spot on export).
|
||||
(Paint::Solid { color: x, swatch: Some(sa), tint: ta }, Paint::Solid { color: y, swatch: Some(sb), tint: tb }) if sa == sb => {
|
||||
Paint::Solid { color: lerp_color(x, y, t), swatch: Some(sa.clone()), tint: lerp_f32(*ta, *tb, t) }
|
||||
}
|
||||
(Paint::Solid { color: x, .. }, Paint::Solid { color: y, .. }) => Paint::solid(lerp_color(x, y, t)),
|
||||
(Paint::Gradient(ga), Paint::Gradient(gb)) => match lerp_gradient(ga, gb, t) {
|
||||
Some(g) => Paint::Gradient(Box::new(g)),
|
||||
None => (if t < 0.5 { a } else { b }).clone(),
|
||||
},
|
||||
(Paint::Solid { color, .. }, Paint::Gradient(g)) => {
|
||||
let ga = GradientPaint { gradient: solid_gradient(*color, &g.gradient), ..(**g).clone() };
|
||||
(Paint::Solid { color, swatch, tint }, Paint::Gradient(g)) => {
|
||||
let ga = GradientPaint { gradient: solid_gradient(*color, swatch.as_deref(), *tint, &g.gradient), ..(**g).clone() };
|
||||
Paint::Gradient(Box::new(lerp_gradient(&ga, g, t).unwrap_or_else(|| (**g).clone())))
|
||||
}
|
||||
(Paint::Gradient(g), Paint::Solid { color, .. }) => {
|
||||
let gb = GradientPaint { gradient: solid_gradient(*color, &g.gradient), ..(**g).clone() };
|
||||
(Paint::Gradient(g), Paint::Solid { color, swatch, tint }) => {
|
||||
let gb = GradientPaint { gradient: solid_gradient(*color, swatch.as_deref(), *tint, &g.gradient), ..(**g).clone() };
|
||||
Paint::Gradient(Box::new(lerp_gradient(g, &gb, t).unwrap_or_else(|| (**g).clone())))
|
||||
}
|
||||
_ => (if t < 0.5 { a } else { b }).clone(),
|
||||
@@ -1114,6 +1130,31 @@ mod tests {
|
||||
assert!(pair.at(0.5).bounds().unwrap().height() < 1e-6, "the clicked anchors pair up");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blend_between_tints_of_one_swatch_keeps_the_link() {
|
||||
let tinted = |tint: f32| Paint::Solid { color: Color::cmyk(1.0, 0.7, 0.0, 0.4).tinted(tint), swatch: Some("Spot".into()), tint };
|
||||
let Paint::Solid { swatch, tint, .. } = lerp_paint(&tinted(0.2), &tinted(1.0), 0.5) else { panic!() };
|
||||
assert_eq!(swatch.as_deref(), Some("Spot"));
|
||||
assert!((tint - 0.6).abs() < 1e-6, "{tint}");
|
||||
let other = Paint::Solid { color: Color::BLACK, swatch: Some("Other".into()), tint: 1.0 };
|
||||
assert!(matches!(lerp_paint(&tinted(0.2), &other, 0.5), Paint::Solid { swatch: None, .. }));
|
||||
|
||||
// Gradient stops on tints of one swatch stay linked too; a process solid unlinks them.
|
||||
let spot_gradient = |tint: f32| {
|
||||
let mut stop = GradientStop::new(0.0, Color::cmyk(1.0, 0.7, 0.0, 0.4).tinted(tint));
|
||||
stop.swatch = Some("Spot".into());
|
||||
stop.tint = tint;
|
||||
let stops = vec![stop.clone(), GradientStop { offset: 1.0, ..stop }];
|
||||
Paint::Gradient(Box::new(GradientPaint::new(Gradient { stops, ..Gradient::default() })))
|
||||
};
|
||||
let Paint::Gradient(m) = lerp_paint(&spot_gradient(0.2), &spot_gradient(1.0), 0.5) else { panic!() };
|
||||
assert!(m.gradient.stops.iter().all(|s| s.swatch.as_deref() == Some("Spot") && (s.tint - 0.6).abs() < 1e-6), "{:?}", m.gradient.stops);
|
||||
let Paint::Gradient(m) = lerp_paint(&tinted(0.2), &spot_gradient(1.0), 0.5) else { panic!() };
|
||||
assert!(m.gradient.stops.iter().all(|s| s.swatch.as_deref() == Some("Spot") && (s.tint - 0.6).abs() < 1e-6), "{:?}", m.gradient.stops);
|
||||
let Paint::Gradient(m) = lerp_paint(&Paint::solid(Color::BLACK), &spot_gradient(1.0), 0.5) else { panic!() };
|
||||
assert!(m.gradient.stops.iter().all(|s| s.swatch.is_none()), "{:?}", m.gradient.stops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gradients_with_different_stop_counts_interpolate() {
|
||||
let g = |stops: &[(f32, Color)]| {
|
||||
|
||||
Reference in New Issue
Block a user