Add a gradient interpolation attribute with Stepped, Linear (existing), and Smooth modes (#4418)

* Delete dead code function

* Add a Gradient Interpolation axis with Stepped, Linear, and Smooth paths through the stops

* Keep the gradient interpolation attached when dragging stops in the picker

* Give the gradient popover's dropdowns the same tooltips as their row labels

* Order the gradient popover with Intrp. above Space

* Hold gradient stops in place when the cyclic flag is toggled

* Fix wrong Smooth gradient colors around stops sitting on the ramp boundaries

* Consolidate gradient settings plumbing and fix bugs

* Bundle whole-ramp gradient settings into GradientSettings across the editor plumbing

* Name the Smooth gradient interpolation's spline type in its tooltip

* Linearize the gamma hex stop colors recovered from imported Graphite SVGs

* Code review

* Aim the Smooth bake's seam subdivision at the copied boundary color

* Add GradientEvaluator to build gradient sampling state once per loop instead of per sample

* Correct the gradient evaluator's documented setup cost to O(n log n)
This commit is contained in:
Keavon Chambers
2026-08-07 16:43:36 -07:00
committed by Dennis Kobert
parent 244a7e0f78
commit 6e32e97ba1
24 changed files with 1552 additions and 750 deletions

View File

@@ -614,9 +614,9 @@ tagged_value! {
GradientForm(vector::style::GradientForm),
#[serde(alias = "GradientSpreadMethod")] // TODO: Eventually remove this document upgrade code
GradientSpread(vector::style::GradientSpread),
#[serde(alias = "GradientInterpolation")] // TODO: Eventually remove this document upgrade code
GradientSpace(vector::style::GradientSpace),
GradientHueDirection(vector::style::GradientHueDirection),
GradientInterpolation(vector::style::GradientInterpolation),
ReferencePoint(vector::ReferencePoint),
CentroidType(vector::misc::CentroidType),
BooleanOperation(vector::misc::BooleanOperation),

View File

@@ -1,4 +1,4 @@
use crate::renderer::{ClearGuardPlacement, RenderParams, format_transform_matrix, gradient_placement, spread_adjusted_samples, transform_is_invertible};
use crate::renderer::{ClearGuardPlacement, RenderParams, format_transform_matrix, gradient_placement, lane_gradient_settings, spread_adjusted_samples, transform_is_invertible};
use crate::{Render, RenderSvgSegmentList, SvgRender};
use core_types::Color;
use core_types::attribute::Transform;
@@ -14,7 +14,7 @@ use graphic_types::vector_types::markers::{
use graphic_types::vector_types::vector::style::{PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
use std::fmt::Write;
use vector_types::Gradient;
use vector_types::gradient::{GradientHueDirection, GradientSpace, GradientSpread};
use vector_types::gradient::GradientSpread;
#[derive(Copy, Clone, PartialEq)]
pub enum PaintTarget {
@@ -112,20 +112,9 @@ pub fn render_gradient_paint<S: core_types::lane::LaneSource<Element = Gradient>
let Some(stops) = source.element(0) else { return 0 };
let gradient_form: GradientForm = source.attr::<GradientFormAttr>(0);
let local_gradient_transform: DAffine2 = source.attr::<Transform>(0);
let gradient_spread: GradientSpread = source.attr::<GradientSpreadAttr>(0);
let gradient_space: GradientSpace = source.attr::<GradientSpaceAttr>(0);
let gradient_hue_direction: GradientHueDirection = source.attr::<GradientHueDirectionAttr>(0);
let gradient_cyclic: bool = source.attr::<GradientCyclicAttr>(0);
let settings = lane_gradient_settings(source, 0);
let (samples, _) = spread_adjusted_samples(
stops,
gradient_spread,
gradient_form,
gradient_cyclic,
gradient_space,
gradient_hue_direction,
ClearGuardPlacement::SvgStopOrder,
);
let (samples, _) = spread_adjusted_samples(stops, settings, gradient_form, ClearGuardPlacement::SvgStopOrder);
for (position, color, original_midpoint) in samples {
stop.push_str("<stop");
@@ -164,10 +153,10 @@ pub fn render_gradient_paint<S: core_types::lane::LaneSource<Element = Gradient>
format!(r#" gradientTransform="{gradient_transform}""#)
};
let gradient_spread = if matches!(gradient_spread, GradientSpread::Pad | GradientSpread::Clear) {
let gradient_spread = if matches!(settings.spread, GradientSpread::Pad | GradientSpread::Clear) {
String::new()
} else {
format!(r#" spreadMethod="{}""#, gradient_spread.svg_name())
format!(r#" spreadMethod="{}""#, settings.spread.svg_name())
};
let gradient_id = generate_uuid();

View File

@@ -26,7 +26,8 @@ use graphene_resource::Resource;
use graphic_types::graphic::{PaintColumns, PaintOverlay, PaintReach, has_paint, is_paint_present, paint_graphics, set_paint_attribute, vector_can_reduce_to_clip_path};
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke};
use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster, Texture};
use graphic_types::vector_types::gradient::{Gradient, GradientForm, GradientHueDirection, GradientSpace};
use graphic_types::vector_types::gradient::{Gradient, GradientForm, GradientSettings};
use graphic_types::vector_types::markers::GradientInterpolation as GradientInterpolationAttr;
use graphic_types::vector_types::markers::{
GradientCyclic as GradientCyclicAttr, GradientForm as GradientFormAttr, GradientHueDirection as GradientHueDirectionAttr, GradientSpace as GradientSpaceAttr, GradientSpread as GradientSpreadAttr,
};
@@ -424,17 +425,9 @@ pub(crate) enum ClearGuardPlacement {
/// The `Clear` spread brackets the samples with transparent guard stops placed per `guards`: the pad extension then
/// paints transparency outward while hard stops cut the paint off exactly at the unit range's boundaries. A radial
/// gradient's span still starts at zero, since its sampling distance never goes below the center.
pub(crate) fn spread_adjusted_samples(
gradient: &Gradient,
gradient_spread: GradientSpread,
gradient_form: GradientForm,
gradient_cyclic: bool,
gradient_space: GradientSpace,
gradient_hue_direction: GradientHueDirection,
guards: ClearGuardPlacement,
) -> (GradientSamples, (f64, f64)) {
let samples = gradient.interpolated_samples(gradient_cyclic, gradient_space, gradient_hue_direction);
if gradient_spread != GradientSpread::Clear {
pub(crate) fn spread_adjusted_samples(gradient: &Gradient, settings: GradientSettings, gradient_form: GradientForm, guards: ClearGuardPlacement) -> (GradientSamples, (f64, f64)) {
let samples = gradient.interpolated_samples(settings);
if settings.spread != GradientSpread::Clear {
return (samples, (0., 1.));
}
@@ -512,25 +505,25 @@ fn peniko_extend(gradient_spread: GradientSpread) -> peniko::Extend {
}
}
/// The gradient's whole-ramp settings from its lane attributes.
pub(crate) fn lane_gradient_settings<S: LaneSource<Element = Gradient>>(source: &S, index: usize) -> GradientSettings {
GradientSettings {
spread: source.attr::<GradientSpreadAttr>(index),
cyclic: source.attr::<GradientCyclicAttr>(index),
space: source.attr::<GradientSpaceAttr>(index),
hue_direction: source.attr::<GradientHueDirectionAttr>(index),
interpolation: source.attr::<GradientInterpolationAttr>(index),
}
}
fn create_peniko_gradient_brush<S: LaneSource<Element = Gradient>>(gradient_list: &S, multiplied_transform: &DAffine2) -> Option<(peniko::Brush, DAffine2)> {
let stops = gradient_list.element(0)?;
let gradient_form: GradientForm = gradient_list.attr::<GradientFormAttr>(0);
let gradient_transform: DAffine2 = gradient_list.attr::<Transform>(0);
let gradient_spread: GradientSpread = gradient_list.attr::<GradientSpreadAttr>(0);
let gradient_space: GradientSpace = gradient_list.attr::<GradientSpaceAttr>(0);
let gradient_hue_direction: GradientHueDirection = gradient_list.attr::<GradientHueDirectionAttr>(0);
let gradient_cyclic: bool = gradient_list.attr::<GradientCyclicAttr>(0);
let settings = lane_gradient_settings(gradient_list, 0);
let (samples, span) = spread_adjusted_samples(
stops,
gradient_spread,
gradient_form,
gradient_cyclic,
gradient_space,
gradient_hue_direction,
ClearGuardPlacement::VelloRampTexels,
);
let (samples, span) = spread_adjusted_samples(stops, settings, gradient_form, ClearGuardPlacement::VelloRampTexels);
let peniko_stops = peniko_color_stops(&samples);
@@ -552,7 +545,7 @@ fn create_peniko_gradient_brush<S: LaneSource<Element = Gradient>>(gradient_list
}
.into(),
},
extend: peniko_extend(gradient_spread),
extend: peniko_extend(settings.spread),
stops: peniko_stops,
interpolation_alpha_space: peniko::InterpolationAlphaSpace::Premultiplied,
..Default::default()
@@ -2443,11 +2436,8 @@ fn render_gradient_svg<S: LaneSource<Element = Gradient>>(source: &S, render: &m
let blend_mode: BlendMode = source.attr::<BlendModeAttr>(index);
let opacity_attr: f64 = source.attr::<Opacity>(index);
let opacity_fill_attr: f64 = source.attr::<OpacityFill>(index);
let gradient_spread: GradientSpread = source.attr::<GradientSpreadAttr>(index);
let settings = lane_gradient_settings(source, index);
let gradient_form: GradientForm = source.attr::<GradientFormAttr>(index);
let gradient_space: GradientSpace = source.attr::<GradientSpaceAttr>(index);
let gradient_hue_direction: GradientHueDirection = source.attr::<GradientHueDirectionAttr>(index);
let gradient_cyclic: bool = source.attr::<GradientCyclicAttr>(index);
let tag = if thumbnail_rect.is_some() { "rect" } else { "polyline" };
render.leaf_tag(tag, |attributes| {
if let Some((min, size)) = thumbnail_rect {
@@ -2463,15 +2453,7 @@ fn render_gradient_svg<S: LaneSource<Element = Gradient>>(source: &S, render: &m
attributes.push("points", format!("{MAX},{MAX} -{MAX},{MAX} -{MAX},-{MAX} {MAX},-{MAX}"));
}
let (samples, _) = spread_adjusted_samples(
gradient,
gradient_spread,
gradient_form,
gradient_cyclic,
gradient_space,
gradient_hue_direction,
ClearGuardPlacement::SvgStopOrder,
);
let (samples, _) = spread_adjusted_samples(gradient, settings, gradient_form, ClearGuardPlacement::SvgStopOrder);
let mut stop_string = String::new();
for (position, color, original_midpoint) in samples {
@@ -2495,10 +2477,10 @@ fn render_gradient_svg<S: LaneSource<Element = Gradient>>(source: &S, render: &m
};
let gradient_id = generate_uuid();
let spread_method_attribute = if matches!(gradient_spread, GradientSpread::Pad | GradientSpread::Clear) {
let spread_method_attribute = if matches!(settings.spread, GradientSpread::Pad | GradientSpread::Clear) {
String::new()
} else {
format!(r#" spreadMethod="{}""#, gradient_spread.svg_name())
format!(r#" spreadMethod="{}""#, settings.spread.svg_name())
};
// The unit gradient line is the +X unit vector in local space, before the item's transform is applied
@@ -2540,11 +2522,8 @@ fn render_gradient_vello<S: LaneSource<Element = Gradient>>(source: &S, scene: &
for index in 0..source.lane_count() {
let Some(gradient) = source.element(index) else { continue };
let gradient_spread: GradientSpread = source.attr::<GradientSpreadAttr>(index);
let settings = lane_gradient_settings(source, index);
let gradient_form: GradientForm = source.attr::<GradientFormAttr>(index);
let gradient_space: GradientSpace = source.attr::<GradientSpaceAttr>(index);
let gradient_hue_direction: GradientHueDirection = source.attr::<GradientHueDirectionAttr>(index);
let gradient_cyclic: bool = source.attr::<GradientCyclicAttr>(index);
let transform: DAffine2 = source.attr::<Transform>(index);
let blend_mode_attr: BlendMode = source.attr::<BlendModeAttr>(index);
let opacity_attr: f64 = source.attr::<Opacity>(index);
@@ -2554,18 +2533,10 @@ fn render_gradient_vello<S: LaneSource<Element = Gradient>>(source: &S, scene: &
let blend_mode = blend_mode_attr.to_peniko();
let opacity = (opacity_attr * if render_params.for_mask { 1. } else { opacity_fill_attr }) as f32;
let (samples, span) = spread_adjusted_samples(
gradient,
gradient_spread,
gradient_form,
gradient_cyclic,
gradient_space,
gradient_hue_direction,
ClearGuardPlacement::VelloRampTexels,
);
let (samples, span) = spread_adjusted_samples(gradient, settings, gradient_form, ClearGuardPlacement::VelloRampTexels);
let stops = peniko_color_stops(&samples);
let extend = peniko_extend(gradient_spread);
let extend = peniko_extend(settings.spread);
// The unit gradient line is the +X unit vector in local space, before the item's transform is applied.
// For radial, the unit-radius circle at the origin scales out to the line's length once the brush transform applies.
@@ -3369,30 +3340,39 @@ mod group_walk_tests {
#[cfg(test)]
mod spread_tests {
use super::*;
use graphic_types::vector_types::gradient::{GradientHueDirection, GradientInterpolation, GradientSpace};
#[test]
fn spread_adjusted_samples_wraps_clear_in_transparent_guards() {
let gradient = Gradient::from(vec![Color::BLACK, Color::WHITE]);
let (samples, span) = spread_adjusted_samples(
&gradient,
GradientSpread::Repeat,
GradientSettings {
spread: GradientSpread::Repeat,
space: GradientSpace::RgbGamma,
..Default::default()
},
GradientForm::Linear,
false,
GradientSpace::RgbGamma,
Default::default(),
ClearGuardPlacement::SvgStopOrder,
);
assert_eq!(span, (0., 1.));
assert_eq!(samples, gradient.interpolated_samples(false, GradientSpace::RgbGamma, Default::default()));
assert_eq!(
samples,
gradient.interpolated_samples(GradientSettings {
space: GradientSpace::RgbGamma,
..Default::default()
})
);
// SVG guards share the range ends' exact offsets, ordered so the pad extension resolves to the transparent outer stops
let (samples, span) = spread_adjusted_samples(
&gradient,
GradientSpread::Clear,
GradientSettings {
spread: GradientSpread::Clear,
space: GradientSpace::RgbGamma,
..Default::default()
},
GradientForm::Linear,
false,
GradientSpace::RgbGamma,
Default::default(),
ClearGuardPlacement::SvgStopOrder,
);
assert_eq!(span, (0., 1.));
@@ -3405,11 +3385,12 @@ mod spread_tests {
let texel = 1. / (VELLO_GRADIENT_RAMP_TEXELS - 1.);
let (samples, span) = spread_adjusted_samples(
&gradient,
GradientSpread::Clear,
GradientSettings {
spread: GradientSpread::Clear,
space: GradientSpace::RgbGamma,
..Default::default()
},
GradientForm::Linear,
false,
GradientSpace::RgbGamma,
Default::default(),
ClearGuardPlacement::VelloRampTexels,
);
assert_eq!(
@@ -3426,11 +3407,12 @@ mod spread_tests {
// A radial keeps its stops and span anchored at zero, with no guard below the center
let (samples, span) = spread_adjusted_samples(
&gradient,
GradientSpread::Clear,
GradientSettings {
spread: GradientSpread::Clear,
space: GradientSpace::RgbGamma,
..Default::default()
},
GradientForm::Radial,
false,
GradientSpace::RgbGamma,
Default::default(),
ClearGuardPlacement::VelloRampTexels,
);
assert_eq!(span.0, 0.);
@@ -3442,11 +3424,12 @@ mod spread_tests {
fn spread_adjusted_samples_keeps_a_stopless_clear_gradient_black_inside_the_range() {
let (samples, _) = spread_adjusted_samples(
&Gradient::from(Vec::new()),
GradientSpread::Clear,
GradientSettings {
spread: GradientSpread::Clear,
space: GradientSpace::RgbGamma,
..Default::default()
},
GradientForm::Linear,
false,
GradientSpace::RgbGamma,
Default::default(),
ClearGuardPlacement::SvgStopOrder,
);
let colors: Vec<Color> = samples.iter().map(|&(_, color, _)| color).collect();

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@ pub mod vector;
// Re-export commonly used types at the crate root
pub use core_types as gcore;
pub use gradient::{Gradient, GradientForm, GradientHueDirection, GradientRamp, GradientSpace, GradientSpread, GradientStop};
pub use gradient::{Gradient, GradientForm, GradientHueDirection, GradientInterpolation, GradientRamp, GradientSettings, GradientSpace, GradientSpread, GradientStop};
pub use markers::{ATTR_EDITOR_CLICK_TARGET, ATTR_GRADIENT_FORM, ATTR_GRADIENT_SPREAD};
pub use math::{QuadExt, RectExt};
pub use subpath::Subpath;

View File

@@ -13,6 +13,8 @@ core_types::attribute! {
/// Gradient's `bool` (implicit default `false`) for treating the stop list as a cycle, where a wrapped interval
/// interpolates from the last stop through the 1|0 boundary back to the first.
pub GradientCyclic("gradient_cyclic"): bool;
/// Gradient's `GradientInterpolation` (`Stepped`, `Linear`, or `Smooth`), how the color progresses across each interval.
pub GradientInterpolation("gradient_interpolation"): crate::gradient::GradientInterpolation;
/// Gradient's shape (`Linear` or `Radial`).
pub GradientForm("gradient_form"): crate::gradient::GradientForm;
/// Optional `Vector` that overrides the item's own geometry for click-target generation.
@@ -29,6 +31,7 @@ core_types::named_value! {
for crate::gradient::GradientForm;
for crate::gradient::GradientSpace;
for crate::gradient::GradientHueDirection;
for crate::gradient::GradientInterpolation;
}
pub const ATTR_GRADIENT_SPREAD: &str = GradientSpread::NAME;
@@ -36,6 +39,7 @@ pub const ATTR_GRADIENT_FORM: &str = GradientForm::NAME;
pub const ATTR_GRADIENT_CYCLIC: &str = GradientCyclic::NAME;
pub const ATTR_GRADIENT_SPACE: &str = GradientSpace::NAME;
pub const ATTR_GRADIENT_HUE_DIRECTION: &str = GradientHueDirection::NAME;
pub const ATTR_GRADIENT_INTERPOLATION: &str = GradientInterpolation::NAME;
pub const ATTR_EDITOR_CLICK_TARGET: &str = EditorClickTarget::NAME;
#[cfg(test)]

View File

@@ -76,7 +76,7 @@ impl FillChoice<SRGBA8> {
let hex = srgba.to_rgba_hex();
Some(format!("linear-gradient(#{hex}, #{hex})"))
}
Self::Gradient(ramp) => Some(ramp.stops.to_css_linear_gradient(ramp.gradient_cyclic, ramp.gradient_space, ramp.gradient_hue_direction)),
Self::Gradient(ramp) => Some(ramp.stops.to_css_linear_gradient(ramp.into())),
}
}
}
@@ -361,19 +361,6 @@ impl Stroke {
self
}
pub fn with_dash_lengths(mut self, dash_lengths: &str) -> Option<Self> {
dash_lengths
.split(&[',', ' '])
.filter(|x| !x.is_empty())
.map(str::parse::<f64>)
.collect::<Result<Vec<_>, _>>()
.ok()
.map(|lengths| {
self.dash_lengths = lengths;
self
})
}
pub fn with_dash_offset(mut self, dash_offset: f64) -> Self {
self.dash_offset = dash_offset;
self

View File

@@ -12,7 +12,8 @@ use rand::{Rng, SeedableRng};
use std::ops::{Add, Mul, Rem, Sub};
use vector_types::Gradient;
use vector_types::markers::{
GradientCyclic as GradientCyclicAttr, GradientForm as GradientFormAttr, GradientHueDirection as GradientHueDirectionAttr, GradientSpace as GradientSpaceAttr, GradientSpread as GradientSpreadAttr,
GradientCyclic as GradientCyclicAttr, GradientForm as GradientFormAttr, GradientHueDirection as GradientHueDirectionAttr, GradientInterpolation as GradientInterpolationAttr,
GradientSpace as GradientSpaceAttr, GradientSpread as GradientSpreadAttr,
};
/// The struct that stores the context for the maths parser.
@@ -1228,6 +1229,12 @@ fn gradient_hue_direction(_: impl Ctx, gradient: Gradient, gradient_hue_directio
(gradient, Attr(gradient_hue_direction))
}
/// Sets how the color progresses across each interval between gradient stops: stepped, linear, or smoothstep.
#[node_macro::node(category("Gradient"))]
fn gradient_interpolation(_: impl Ctx, gradient: Gradient, gradient_interpolation: vector_types::GradientInterpolation) -> (Gradient, Attr<GradientInterpolationAttr>) {
(gradient, Attr(gradient_interpolation))
}
/// Sets the position of each of a gradient's stops, a factor from 0 to 1 along the gradient.
///
/// A list shorter than the stop count repeats its last value, a longer list is truncated, and an empty list sets each stop to its default evenly spaced position.
@@ -1263,11 +1270,14 @@ fn sample_gradient(
return Err(GraphError::past_end().into());
}
let gradient_spread = gradient.lane(0).attr::<GradientSpreadAttr>();
let gradient_space = gradient.lane(0).attr::<GradientSpaceAttr>();
let gradient_hue_direction = gradient.lane(0).attr::<GradientHueDirectionAttr>();
let gradient_cyclic = gradient.lane(0).attr::<GradientCyclicAttr>();
Ok(gradient.element_ref(0).evaluate(position, gradient_spread, gradient_cyclic, gradient_space, gradient_hue_direction))
let settings = vector_types::GradientSettings {
spread: gradient.lane(0).attr::<GradientSpreadAttr>(),
cyclic: gradient.lane(0).attr::<GradientCyclicAttr>(),
space: gradient.lane(0).attr::<GradientSpaceAttr>(),
hue_direction: gradient.lane(0).attr::<GradientHueDirectionAttr>(),
interpolation: gradient.lane(0).attr::<GradientInterpolationAttr>(),
};
Ok(gradient.element_ref(0).evaluate(position, settings))
}
/// Constructs a footprint value which may be set to any transformation of a unit square describing a render area, and a render resolution at least 1x1 integer pixels.

View File

@@ -44,10 +44,10 @@ mod blend_std {
let mut combined_stops = self.positions(false).into_iter().chain(under.positions(false)).collect::<Vec<_>>();
combined_stops.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
combined_stops.dedup_by(|a, b| (*a - *b).abs() < 1e-6);
let over_evaluator = self.evaluator(Default::default());
let under_evaluator = under.evaluator(Default::default());
let stops = combined_stops.into_iter().map(|position| {
let over_color = self.evaluate(position, Default::default(), false, Default::default(), Default::default());
let under_color = under.evaluate(position, Default::default(), false, Default::default(), Default::default());
let color = blend_fn(over_color, under_color);
let color = blend_fn(over_evaluator.evaluate(position), under_evaluator.evaluate(position));
GradientStop { position, midpoint: 0.5, color }
});

View File

@@ -23,16 +23,19 @@ fn gradient_map<T: Adjust<Color> + Clone + Send + Sync + core_types::CacheHash +
if gradient.is_empty() {
return image;
}
let gradient_spread = gradient.lane(0).attr::<vector_types::markers::GradientSpread>();
let gradient_space = gradient.lane(0).attr::<vector_types::markers::GradientSpace>();
let gradient_hue_direction = gradient.lane(0).attr::<vector_types::markers::GradientHueDirection>();
let gradient_cyclic = gradient.lane(0).attr::<vector_types::markers::GradientCyclic>();
let gradient = gradient.element_ref(0);
let settings = vector_types::GradientSettings {
spread: gradient.lane(0).attr::<vector_types::markers::GradientSpread>(),
cyclic: gradient.lane(0).attr::<vector_types::markers::GradientCyclic>(),
space: gradient.lane(0).attr::<vector_types::markers::GradientSpace>(),
hue_direction: gradient.lane(0).attr::<vector_types::markers::GradientHueDirection>(),
interpolation: gradient.lane(0).attr::<vector_types::markers::GradientInterpolation>(),
};
let evaluator = gradient.element_ref(0).evaluator(settings);
image.adjust(|color| {
let intensity = color.luminance_rec_709();
let intensity = if reverse { 1. - intensity } else { intensity };
gradient.evaluate(intensity as f64, gradient_spread, gradient_cyclic, gradient_space, gradient_hue_direction)
evaluator.evaluate(intensity as f64)
});
image

View File

@@ -45,17 +45,7 @@ use vector_types::vector::{PointDomain, RegionDomain};
/// The gradient color for one assign-colors position, replaying the
/// randomized draws up to it.
fn assign_color_at(
gradient: &Gradient,
gradient_cyclic: bool,
gradient_space: vector_types::GradientSpace,
gradient_hue_direction: vector_types::GradientHueDirection,
position: usize,
length: usize,
randomize: bool,
seed: SeedValue,
repeat_every: u32,
) -> Color {
fn assign_color_at(gradient: &Gradient, settings: vector_types::GradientSettings, position: usize, length: usize, randomize: bool, seed: SeedValue, repeat_every: u32) -> Color {
let factor = match randomize {
true => {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
@@ -71,7 +61,7 @@ fn assign_color_at(
_ => position as f64 % repeat_every as f64 / (repeat_every - 1) as f64,
},
};
gradient.evaluate(factor, Default::default(), gradient_cyclic, gradient_space, gradient_hue_direction)
gradient.evaluate(factor, settings)
}
/// Uniquely sets the fill and/or stroke style of every vector element to individual colors sampled along a chosen gradient.
@@ -114,30 +104,24 @@ fn assign_colors<'e>(
if gradient.is_empty() {
return Ok((content.lane(lane).map_element(element), Attr(existing_fill), Attr(existing_stroke)));
}
let gradient_cyclic = gradient.lane(0).attr::<vector_types::markers::GradientCyclic>();
let gradient_space = gradient.lane(0).attr::<vector_types::markers::GradientSpace>();
let gradient_hue_direction = gradient.lane(0).attr::<vector_types::markers::GradientHueDirection>();
let settings = vector_types::GradientSettings {
spread: gradient.lane(0).attr::<vector_types::markers::GradientSpread>(),
cyclic: gradient.lane(0).attr::<vector_types::markers::GradientCyclic>(),
space: gradient.lane(0).attr::<vector_types::markers::GradientSpace>(),
hue_direction: gradient.lane(0).attr::<vector_types::markers::GradientHueDirection>(),
interpolation: gradient.lane(0).attr::<vector_types::markers::GradientInterpolation>(),
};
let gradient_element = gradient.element_ref(0);
let reversed;
let gradient_element = match reverse {
true => {
reversed = gradient_element.reversed();
reversed = gradient_element.reversed(settings.cyclic);
&reversed
}
false => gradient_element,
};
let color = assign_color_at(
gradient_element,
gradient_cyclic,
gradient_space,
gradient_hue_direction,
lane,
content.len(),
randomize,
seed,
repeat_every,
);
let color = assign_color_at(gradient_element, settings, lane, content.len(), randomize, seed, repeat_every);
let paint = List::new_from_element(color).into_graphic_list();
let parked = park_paint(ctx.arena(), paint)?;
@@ -195,14 +179,18 @@ fn assign_colors_graphic<'e>(
if gradient.is_empty() {
return Ok(content.lane(lane).map_element(original.clone()));
}
let gradient_cyclic = gradient.lane(0).attr::<vector_types::markers::GradientCyclic>();
let gradient_space = gradient.lane(0).attr::<vector_types::markers::GradientSpace>();
let gradient_hue_direction = gradient.lane(0).attr::<vector_types::markers::GradientHueDirection>();
let settings = vector_types::GradientSettings {
spread: gradient.lane(0).attr::<vector_types::markers::GradientSpread>(),
cyclic: gradient.lane(0).attr::<vector_types::markers::GradientCyclic>(),
space: gradient.lane(0).attr::<vector_types::markers::GradientSpace>(),
hue_direction: gradient.lane(0).attr::<vector_types::markers::GradientHueDirection>(),
interpolation: gradient.lane(0).attr::<vector_types::markers::GradientInterpolation>(),
};
let gradient_element = gradient.element_ref(0);
let reversed;
let gradient_element = match reverse {
true => {
reversed = gradient_element.reversed();
reversed = gradient_element.reversed(settings.cyclic);
&reversed
}
false => gradient_element,
@@ -243,17 +231,7 @@ fn assign_colors_graphic<'e>(
Some(mut rows) => {
for row in 0..rows.len() {
let has_stroke = rows.element(row).is_some_and(|vector| vector.stroke.is_some());
let color = assign_color_at(
gradient_element,
gradient_cyclic,
gradient_space,
gradient_hue_direction,
position + row,
length,
randomize,
seed,
repeat_every,
);
let color = assign_color_at(gradient_element, settings, position + row, length, randomize, seed, repeat_every);
let paint = List::new_from_element(color).into_graphic_list();
if fill {
set_paint_attribute_at(&mut rows, row, ATTR_FILL, paint.clone());