From e192269481f406ab020d923e7dffb0e472918024 Mon Sep 17 00:00:00 2001 From: YohYamasaki Date: Wed, 6 May 2026 17:19:57 +0900 Subject: [PATCH] Add conversion from Fill to Table --- .../libraries/graphic-types/src/graphic.rs | 21 ++++++++++++- .../libraries/vector-types/src/gradient.rs | 30 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/node-graph/libraries/graphic-types/src/graphic.rs b/node-graph/libraries/graphic-types/src/graphic.rs index 0efade8880..df0e06b92a 100644 --- a/node-graph/libraries/graphic-types/src/graphic.rs +++ b/node-graph/libraries/graphic-types/src/graphic.rs @@ -4,7 +4,7 @@ use core_types::list::List; use core_types::ops::ListConvert; use core_types::render_complexity::RenderComplexity; use core_types::uuid::NodeId; -use core_types::{ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_TRANSFORM, Color}; +use core_types::{ATTR_CLIPPING_MASK, ATTR_EDITOR_LAYER_PATH, ATTR_GRADIENT_TYPE, ATTR_OPACITY, ATTR_OPACITY_FILL, ATTR_SPREAD_METHOD, ATTR_TRANSFORM, Color}; use dyn_any::DynAny; use glam::DAffine2; use raster_types::{CPU, GPU, Raster}; @@ -12,6 +12,7 @@ use vector_types::GradientStops; // use vector_types::Vector; pub use vector_types::Vector; +use vector_types::vector::style::Fill; /// The possible forms of graphical content that can be rendered by the Render node into either an image or SVG syntax. #[derive(Clone, Debug, CacheHash, PartialEq, DynAny)] @@ -169,6 +170,24 @@ fn flatten_graphic_list(content: List, extract_variant: fn(Graphic) output } +/// Converts a `Fill` enum into the `Table` representation used as paint storage. +/// TODO: Remove once all paint sources flow through `Table` directly without going through the `Fill` enum. +pub fn fill_to_paint(fill: &Fill) -> Option> { + match fill { + Fill::None => None, + Fill::Solid(color) => Some(Table::new_from_element((*color).into())), + Fill::Gradient(gradient) => { + let gradient_row = TableRow::new_from_element(gradient.stops.clone()) + .with_attribute(ATTR_TRANSFORM, gradient.to_transform()) + .with_attribute(ATTR_GRADIENT_TYPE, gradient.gradient_type) + .with_attribute(ATTR_SPREAD_METHOD, gradient.spread_method); + let gradient_table = Table::new_from_row(gradient_row); + + Some(Table::new_from_element(Graphic::Gradient(gradient_table))) + } + } +} + /// Maps from a concrete element type to its corresponding `Graphic` enum variant, /// enabling type-directed casting of typed `List`s from a `Graphic` value. pub trait TryFromGraphic: Clone + Sized { diff --git a/node-graph/libraries/vector-types/src/gradient.rs b/node-graph/libraries/vector-types/src/gradient.rs index 9df066e76e..40aaa939e7 100644 --- a/node-graph/libraries/vector-types/src/gradient.rs +++ b/node-graph/libraries/vector-types/src/gradient.rs @@ -587,6 +587,12 @@ impl Gradient { Some(index) } + + /// Builds the affine that places the gradient endpoints at `start` and `end` when applied to canonical gradient space (0,0) -> (1,0) + pub fn to_transform(&self) -> DAffine2 { + let direction = self.end - self.start; + DAffine2::from_cols(direction, direction.perp(), self.start) + } } // TODO: Eventually remove this migration document upgrade code @@ -625,3 +631,27 @@ impl core_types::bounds::BoundingBox for GradientStops { core_types::bounds::RenderBoundingBox::Rectangle([start.min(end), start.max(end)]) } } + +#[cfg(test)] +mod tests { + use super::*; + use glam::DVec2; + + fn linear_gradient(start: DVec2, end: DVec2) -> Gradient { + Gradient { start, end, ..Default::default() } + } + + #[test] + fn to_transform_roundtrip() { + let cases = [(DVec2::ZERO, DVec2::X), (DVec2::new(10., 20.), DVec2::new(50., 30.)), (DVec2::new(-5., -5.), DVec2::new(5., 3.))]; + + for (start, end) in cases { + let transform = linear_gradient(start, end).to_transform(); + let recovered_start = transform.transform_point2(DVec2::ZERO); + let recovered_end = transform.transform_point2(DVec2::X); + + assert!((recovered_start - start).length() < 1e-10); + assert!((recovered_end - end).length() < 1e-10); + } + } +}