Make the node graph use Table<GradientStops> instead of GradientStops (#3837)

* Switch from GradientStops to Table<GradientStops> in all nodes

* Remove TaggedValue::ColorNotInTable

* Fix bug

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Add migrations

* Fix default gradient on empty table

* Update demo artwork

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
Keavon Chambers
2026-02-26 14:07:31 -08:00
committed by GitHub
parent 81c73d11ff
commit f1cbc4b396
26 changed files with 132 additions and 111 deletions

View File

@@ -3,7 +3,8 @@ use core_types::transform::Footprint;
use core_types::uuid::NodeId;
use core_types::{CloneVarArgs, Color, Context, Ctx, ExtractAll, ExtractAnimationTime, ExtractPointerPosition, ExtractRealTime, OwnedContextImpl};
use glam::{DAffine2, DVec2};
use graphic_types::{Artboard, Graphic, Vector, vector_types::GradientStops};
use graphic_types::vector_types::GradientStops;
use graphic_types::{Artboard, Graphic, Vector};
use raster_types::{CPU, GPU, Raster};
const DAY: f64 = 1000. * 3600. * 24.;
@@ -84,7 +85,6 @@ async fn quantize_real_time<T>(
Context -> Table<Color>,
Context -> Table<Artboard>,
Context -> Table<GradientStops>,
Context -> GradientStops,
Context -> (),
)]
value: impl Node<'n, Context<'static>, Output = T>,
@@ -128,7 +128,6 @@ async fn quantize_animation_time<T>(
Context -> Table<Color>,
Context -> Table<Artboard>,
Context -> Table<GradientStops>,
Context -> GradientStops,
Context -> (),
)]
value: impl Node<'n, Context<'static>, Output = T>,

View File

@@ -5,7 +5,8 @@ use core_types::transform::Footprint;
use core_types::uuid::NodeId;
use core_types::{Color, OwnedContextImpl};
use glam::{DAffine2, DVec2};
use graphic_types::{Artboard, Graphic, Vector, vector_types::GradientStops};
use graphic_types::vector_types::GradientStops;
use graphic_types::{Artboard, Graphic, Vector};
use raster_types::{CPU, GPU, Raster};
/// Filters out what should be unused components of the context based on the specified requirements.
@@ -37,7 +38,6 @@ async fn context_modification<T>(
Context -> Table<Color>,
Context -> Table<Artboard>,
Context -> Table<GradientStops>,
Context -> GradientStops,
)]
value: impl Node<Context<'static>, Output = T>,
/// The parts of the context to keep when evaluating the input value. All other parts are nullified.

View File

@@ -3,7 +3,8 @@ use core_types::registry::types::TextArea;
use core_types::table::Table;
use core_types::{Context, Ctx};
use glam::{DAffine2, DVec2};
use graphic_types::{Artboard, Graphic, Vector, vector_types::GradientStops};
use graphic_types::vector_types::GradientStops;
use graphic_types::{Artboard, Graphic, Vector};
use raster_types::{CPU, GPU, Raster};
/// Type-asserts a value to be a string.
@@ -152,7 +153,7 @@ async fn switch<T, C: Send + 'n + Clone>(
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Table<Color>,
Context -> GradientStops,
Context -> Table<GradientStops>,
)]
if_true: impl Node<C, Output = T>,
#[expose]
@@ -171,7 +172,7 @@ async fn switch<T, C: Send + 'n + Clone>(
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Table<Color>,
Context -> GradientStops,
Context -> Table<GradientStops>,
)]
if_false: impl Node<C, Output = T>,
) -> T {

View File

@@ -318,22 +318,47 @@ pub async fn flatten_gradient<T: IntoGraphicTable + 'n + Send + Clone>(_: impl C
/// Constructs a gradient from a table of colors, where the colors are evenly distributed as gradient stops across the range from 0 to 1.
#[node_macro::node(category("Color"))]
fn colors_to_gradient<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Color>)] colors: T) -> GradientStops {
fn colors_to_gradient<T: IntoGraphicTable + 'n + Send + Clone>(_: impl Ctx, #[implementations(Table<Graphic>, Table<Color>)] colors: T) -> Table<GradientStops> {
let colors = colors.into_flattened_table::<Color>();
let total_colors = colors.len();
if total_colors == 0 {
return GradientStops::new(vec![GradientStop {
position: 0.,
midpoint: 0.5,
color: Color::BLACK,
}]);
return Table::new_from_element(GradientStops::new(vec![
GradientStop {
position: 0.,
midpoint: 0.5,
color: Color::BLACK,
},
GradientStop {
position: 1.,
midpoint: 0.5,
color: Color::BLACK,
},
]));
}
if let (Some(color), None) = {
let mut colors_iter = colors.iter();
(colors_iter.next(), colors_iter.next())
} {
return Table::new_from_element(GradientStops::new(vec![
GradientStop {
position: 0.,
midpoint: 0.5,
color: *color.element,
},
GradientStop {
position: 1.,
midpoint: 0.5,
color: *color.element,
},
]));
}
let colors = colors.into_iter().enumerate().map(|(index, row)| GradientStop {
position: index as f64 / (total_colors - 1).max(1) as f64,
position: index as f64 / (total_colors - 1) as f64,
midpoint: 0.5,
color: row.element,
});
GradientStops::new(colors)
Table::new_from_element(GradientStops::new(colors))
}

View File

@@ -767,21 +767,17 @@ fn color_value(_: impl Ctx, _primary: (), #[default(Color::BLACK)] color: Table<
/// Constructs a gradient value which may be set to any sequence of color stops to represent the transition between colors.
#[node_macro::node(category("Value"))]
fn gradient_value(_: impl Ctx, _primary: (), gradient: GradientStops) -> GradientStops {
fn gradient_value(_: impl Ctx, _primary: (), gradient: Table<GradientStops>) -> Table<GradientStops> {
gradient
}
/// Constructs a gradient value which may be set to any sequence of color stops to represent the transition between colors.
#[node_macro::node(category("Value"))]
fn gradient_table_value(_: impl Ctx, _primary: (), gradient: GradientStops) -> Table<GradientStops> {
Table::new_from_element(gradient)
}
/// Gets the color at the specified position along the gradient, given a position from 0 (left) to 1 (right).
#[node_macro::node(category("Color"))]
fn sample_gradient(_: impl Ctx, _primary: (), gradient: GradientStops, position: Fraction) -> Table<Color> {
fn sample_gradient(_: impl Ctx, _primary: (), gradient: Table<GradientStops>, position: Fraction) -> Table<Color> {
let Some(row) = gradient.get(0) else { return Table::new() };
let position = position.clamp(0., 1.);
let color = gradient.evaluate(position);
let color = row.element.evaluate(position);
Table::new_from_element(color)
}

View File

@@ -54,7 +54,6 @@ fn luminance<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,
@@ -80,7 +79,6 @@ fn gamma_correction<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,
@@ -102,7 +100,6 @@ fn extract_channel<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,
@@ -127,7 +124,6 @@ fn make_opaque<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,
@@ -154,7 +150,6 @@ fn brightness_contrast_classic<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,
@@ -186,7 +181,6 @@ fn brightness_contrast<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,
@@ -268,7 +262,6 @@ fn levels<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut image: T,
@@ -337,7 +330,6 @@ fn black_and_white<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut image: T,
@@ -411,7 +403,6 @@ fn hue_saturation<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,
@@ -447,7 +438,6 @@ fn invert<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,
@@ -471,7 +461,6 @@ fn threshold<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut image: T,
@@ -518,7 +507,6 @@ fn vibrance<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut image: T,
@@ -684,7 +672,6 @@ fn channel_mixer<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut image: T,
@@ -815,7 +802,6 @@ fn selective_color<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut image: T,
@@ -962,7 +948,6 @@ fn posterize<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,
@@ -997,7 +982,6 @@ fn exposure<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut input: T,

View File

@@ -136,7 +136,6 @@ fn blend<T: Blend<Color> + Send>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
over: T,
@@ -145,7 +144,6 @@ fn blend<T: Blend<Color> + Send>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
under: T,
@@ -162,7 +160,6 @@ fn color_overlay<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
#[gpu_image]
mut image: T,

View File

@@ -16,16 +16,17 @@ async fn gradient_map<T: Adjust<Color>>(
Table<Raster<CPU>>,
Table<Color>,
Table<GradientStops>,
GradientStops,
)]
mut image: T,
gradient: GradientStops,
gradient: Table<GradientStops>,
reverse: bool,
) -> T {
let Some(row) = gradient.get(0) else { return image };
image.adjust(|color| {
let intensity = color.luminance_srgb();
let intensity = if reverse { 1. - intensity } else { intensity };
gradient.evaluate(intensity as f64).to_linear_srgb()
row.element.evaluate(intensity as f64).to_linear_srgb()
});
image

View File

@@ -60,7 +60,7 @@ async fn assign_colors<T>(
stroke: bool,
/// The range of colors to select from.
#[widget(ParsedWidgetOverride::Custom = "assign_colors_gradient")]
gradient: GradientStops,
gradient: Table<GradientStops>,
/// Whether to reverse the gradient.
reverse: bool,
/// Whether to randomize the color selection for each element from throughout the gradient.
@@ -76,8 +76,10 @@ async fn assign_colors<T>(
where
T: VectorTableIterMut + 'n + Send,
{
let Some(row) = gradient.into_iter().next() else { return content };
let length = content.vector_iter_mut().count();
let gradient = if reverse { gradient.reversed() } else { gradient };
let gradient = if reverse { row.element.reversed() } else { row.element };
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());