From bad70f7be9686c612950b07411825ebb8e01bf95 Mon Sep 17 00:00:00 2001 From: YohYamasaki Date: Tue, 12 May 2026 23:06:49 +0900 Subject: [PATCH] Fix conflicts --- node-graph/libraries/core-types/src/list.rs | 3 + .../libraries/graphic-types/src/graphic.rs | 16 ++--- .../libraries/rendering/src/render_ext.rs | 18 +++--- .../libraries/rendering/src/renderer.rs | 60 +++++++++++-------- node-graph/nodes/math/src/lib.rs | 27 ++++++++- 5 files changed, 80 insertions(+), 44 deletions(-) diff --git a/node-graph/libraries/core-types/src/list.rs b/node-graph/libraries/core-types/src/list.rs index 61f54597b4..e53bc2bcd6 100644 --- a/node-graph/libraries/core-types/src/list.rs +++ b/node-graph/libraries/core-types/src/list.rs @@ -77,6 +77,9 @@ pub const ATTR_SPREAD_METHOD: &str = "spread_method"; /// Gradient's `GradientType` (`Linear` or `Radial`). pub const ATTR_GRADIENT_TYPE: &str = "gradient_type"; +/// Table data for fill. +pub const ATTR_FILL_GRAPHIC: &str = "fill_graphic"; + // ======================== // TRAIT: AnyAttributeValue // ======================== diff --git a/node-graph/libraries/graphic-types/src/graphic.rs b/node-graph/libraries/graphic-types/src/graphic.rs index df0e06b92a..eebf7e73a6 100644 --- a/node-graph/libraries/graphic-types/src/graphic.rs +++ b/node-graph/libraries/graphic-types/src/graphic.rs @@ -1,6 +1,6 @@ use core_types::bounds::{BoundingBox, RenderBoundingBox}; use core_types::graphene_hash::CacheHash; -use core_types::list::List; +use core_types::list::{Item, List}; use core_types::ops::ListConvert; use core_types::render_complexity::RenderComplexity; use core_types::uuid::NodeId; @@ -170,20 +170,20 @@ 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> { +/// Converts a `Fill` enum into the `List` representation used as paint storage. +/// TODO: Remove once all paint sources flow through `List` directly without going through the `Fill` enum. +pub fn fill_to_graphic_list(fill: &Fill) -> Option> { match fill { Fill::None => None, - Fill::Solid(color) => Some(Table::new_from_element((*color).into())), + Fill::Solid(color) => Some(List::new_from_element((*color).into())), Fill::Gradient(gradient) => { - let gradient_row = TableRow::new_from_element(gradient.stops.clone()) + let gradient_row = Item::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); + let gradient_list = List::new_from_item(gradient_row); - Some(Table::new_from_element(Graphic::Gradient(gradient_table))) + Some(List::new_from_element(Graphic::Gradient(gradient_list))) } } } diff --git a/node-graph/libraries/rendering/src/render_ext.rs b/node-graph/libraries/rendering/src/render_ext.rs index 6cbfded901..9afdb0fc9b 100644 --- a/node-graph/libraries/rendering/src/render_ext.rs +++ b/node-graph/libraries/rendering/src/render_ext.rs @@ -1,11 +1,11 @@ use crate::renderer::{RenderParams, format_transform_matrix}; -use core_types::table::Table; +use core_types::list::List; use core_types::color::SRGBA8; use core_types::uuid::generate_uuid; use core_types::{ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD, ATTR_TRANSFORM, Color}; use glam::{DAffine2, DVec2}; use graphic_types::Graphic; -use graphic_types::graphic::fill_to_paint; +use graphic_types::graphic::fill_to_graphic_list; use graphic_types::vector_types::gradient::GradientType; use graphic_types::vector_types::vector::style::{Fill, PaintOrder, PathStyle, Stroke, StrokeAlign, StrokeCap, StrokeJoin}; use std::fmt::Write; @@ -17,7 +17,7 @@ pub trait RenderExt { fn render(&self, svg_defs: &mut String, element_transform: DAffine2, stroke_transform: DAffine2, bounds: DAffine2, transformed_bounds: DAffine2, render_params: &RenderParams) -> Self::Output; } -impl RenderExt for Table { +impl RenderExt for List { type Output = String; fn render( @@ -40,7 +40,7 @@ impl RenderExt for Table { } } -impl RenderExt for Table { +impl RenderExt for List { type Output = u64; /// Adds the gradient def through mutating the first argument, returning the gradient ID. @@ -118,13 +118,13 @@ impl RenderExt for Fill { /// Renders the fill, adding necessary defs through mutating the first argument. fn render(&self, svg_defs: &mut String, element_transform: DAffine2, stroke_transform: DAffine2, bounds: DAffine2, transformed_bounds: DAffine2, render_params: &RenderParams) -> Self::Output { - let Some(paint_table) = fill_to_paint(self) else { return r#" fill="none""#.to_string() }; - let Some(paint) = paint_table.element(0) else { return String::new() }; + let Some(paint_list) = fill_to_graphic_list(self) else { return r#" fill="none""#.to_string() }; + let Some(paint) = paint_list.element(0) else { return String::new() }; match paint { - Graphic::Color(color_table) => color_table.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params), - Graphic::Gradient(stops_table) => { - let gradient_id = stops_table.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params); + Graphic::Color(color_list) => color_list.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params), + Graphic::Gradient(stops_list) => { + let gradient_id = stops_list.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params); format!(r##" fill="url('#{gradient_id}')""##) } _ => { diff --git a/node-graph/libraries/rendering/src/renderer.rs b/node-graph/libraries/rendering/src/renderer.rs index 2a0cccfad0..d94453b32d 100644 --- a/node-graph/libraries/rendering/src/renderer.rs +++ b/node-graph/libraries/rendering/src/renderer.rs @@ -6,7 +6,7 @@ use core_types::bounds::BoundingBox; use core_types::bounds::RenderBoundingBox; use core_types::color::Color; use core_types::color::SRGBA8; -use core_types::list::{Item, List}; +use core_types::list::{ATTR_FILL_GRAPHIC, Item, List}; use core_types::math::quad::Quad; use core_types::render_complexity::RenderComplexity; use core_types::transform::Footprint; @@ -18,7 +18,7 @@ use core_types::{ use dyn_any::DynAny; use glam::{DAffine2, DVec2}; use graphene_hash::CacheHashWrapper; -use graphic_types::graphic::fill_to_paint; +use graphic_types::graphic::fill_to_graphic_list; use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster}; use graphic_types::vector_types::gradient::{GradientStops, GradientType}; use graphic_types::vector_types::subpath::Subpath; @@ -1111,7 +1111,7 @@ impl Render for List { } } - fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) { + fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) { use graphic_types::vector_types::vector::style::{GradientType, StrokeCap, StrokeJoin}; for index in 0..self.len() { @@ -1182,26 +1182,32 @@ impl Render for List { let use_layer = can_draw_aligned_stroke; let wants_stroke_below = stroke.as_ref().is_some_and(|s| s.paint_order == vector::style::PaintOrder::StrokeBelow); - // TODO: This conversion is only necessary during the transition period from Fill to Table - let do_fill_path = |scene: &mut Scene, path: &kurbo::BezPath, fill_rule: peniko::Fill| { - let Some(paint_table) = fill_to_paint(element.style.fill()) else { + let do_fill_path = |scene: &mut Scene, context: &mut RenderContext, path: &kurbo::BezPath, fill_rule: peniko::Fill| { + // Try to use ATTR_FILL_GRAPHIC attribute, which is set by `fill_graphic` debug node, then fall back to Fill enum. + // TODO: Drop the Fill fall back once the Fill node becomes ready to store corresponding Graphic list directly. + let Some(fill_graphic) = self + .attribute::>(ATTR_FILL_GRAPHIC, index) + .filter(|t| !t.is_empty()) + .cloned() + .or_else(|| fill_to_graphic_list(element.style.fill())) + else { return; }; - for paint_idx in 0..paint_table.len() { - let Some(paint) = paint_table.element(paint_idx) else { continue }; + for paint_idx in 0..fill_graphic.len() { + let Some(paint) = fill_graphic.element(paint_idx) else { continue }; match paint { - Graphic::Color(table) => { - let Some(color) = table.element(0) else { continue }; + Graphic::Color(list) => { + let Some(color) = list.element(0) else { continue }; let fill = peniko::Brush::Solid(SRGBA8::from(*color).to_peniko_color()); scene.fill(fill_rule, kurbo::Affine::new(element_transform.to_cols_array()), &fill, None, path); } - Graphic::Gradient(stops_table) => { - let Some(stops) = stops_table.element(0) else { continue }; - let gradient_type: GradientType = stops_table.attribute_cloned_or_default(ATTR_GRADIENT_TYPE, 0); - let gradient_transform: DAffine2 = stops_table.attribute_cloned_or_default(ATTR_TRANSFORM, 0); - let spread_method: GradientSpreadMethod = stops_table.attribute_cloned_or_default(ATTR_SPREAD_METHOD, 0); + Graphic::Gradient(stops_list) => { + let Some(stops) = stops_list.element(0) else { continue }; + let gradient_type: GradientType = stops_list.attribute_cloned_or_default(ATTR_GRADIENT_TYPE, 0); + let gradient_transform: DAffine2 = stops_list.attribute_cloned_or_default(ATTR_TRANSFORM, 0); + let spread_method: GradientSpreadMethod = stops_list.attribute_cloned_or_default(ATTR_SPREAD_METHOD, 0); let mut peniko_stops = peniko::ColorStops::new(); for (position, color, _) in stops.interpolated_samples() { @@ -1259,14 +1265,18 @@ impl Render for List { let brush_transform = kurbo::Affine::new((inverse_element_transform * parent_transform).to_cols_array()); scene.fill(fill_rule, kurbo::Affine::new(element_transform.to_cols_array()), &fill, Some(brush_transform), path); } - _ => todo!(), + Graphic::Vector(_) | Graphic::RasterCPU(_) | Graphic::RasterGPU(_) | Graphic::Graphic(_) => { + scene.push_clip_layer(fill_rule, kurbo::Affine::new(element_transform.to_cols_array()), path); + paint.render_to_vello(scene, multiplied_transform, context, render_params); + scene.pop_layer(); + } }; } }; // Branching vectors without regions (e.g. mesh grids) need face-by-face fill rendering. let use_face_fill = element.use_face_fill(); - let do_fill = |scene: &mut Scene| { + let do_fill = |scene: &mut Scene, context: &mut RenderContext| { if use_face_fill { for mut face_path in element.construct_faces().filter(|face| face.area() >= 0.) { face_path.apply_affine(Affine::new(applied_stroke_transform.to_cols_array())); @@ -1274,12 +1284,12 @@ impl Render for List { for element in face_path { kurbo_path.push(element); } - do_fill_path(scene, &kurbo_path, peniko::Fill::NonZero); + do_fill_path(scene, context, &kurbo_path, peniko::Fill::NonZero); } } else if element.is_branching() { - do_fill_path(scene, &path, peniko::Fill::EvenOdd); + do_fill_path(scene, context, &path, peniko::Fill::EvenOdd); } else { - do_fill_path(scene, &path, peniko::Fill::NonZero); + do_fill_path(scene, context, &path, peniko::Fill::NonZero); } }; @@ -1349,7 +1359,7 @@ impl Render for List { if wants_stroke_below { scene.push_layer(peniko::Fill::NonZero, peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect); - vector_list.render_to_vello(scene, parent_transform, _context, &render_params.for_alignment(applied_stroke_transform)); + vector_list.render_to_vello(scene, parent_transform, context, &render_params.for_alignment(applied_stroke_transform)); scene.push_layer(peniko::Fill::NonZero, peniko::BlendMode::new(peniko::Mix::Normal, compose), 1., kurbo::Affine::IDENTITY, &rect); do_stroke(scene, 2.); @@ -1357,13 +1367,13 @@ impl Render for List { scene.pop_layer(); scene.pop_layer(); - do_fill(scene); + do_fill(scene, context); } else { // Fill first (unclipped), then stroke (clipped) above - do_fill(scene); + do_fill(scene, context); scene.push_layer(peniko::Fill::NonZero, peniko::Mix::Normal, 1., kurbo::Affine::IDENTITY, &rect); - vector_list.render_to_vello(scene, parent_transform, _context, &render_params.for_alignment(applied_stroke_transform)); + vector_list.render_to_vello(scene, parent_transform, context, &render_params.for_alignment(applied_stroke_transform)); scene.push_layer(peniko::Fill::NonZero, peniko::BlendMode::new(peniko::Mix::Normal, compose), 1., kurbo::Affine::IDENTITY, &rect); do_stroke(scene, 2.); @@ -1385,7 +1395,7 @@ impl Render for List { for operation in &order { match operation { - Op::Fill => do_fill(scene), + Op::Fill => do_fill(scene, context), Op::Stroke => do_stroke(scene, 1.), } } diff --git a/node-graph/nodes/math/src/lib.rs b/node-graph/nodes/math/src/lib.rs index 3e864ee727..43ffa38619 100644 --- a/node-graph/nodes/math/src/lib.rs +++ b/node-graph/nodes/math/src/lib.rs @@ -1,11 +1,11 @@ use core_types::Context; -use core_types::list::List; +use core_types::list::{ATTR_FILL_GRAPHIC, List}; use core_types::registry::types::{Fraction, Percentage, PixelSize}; use core_types::transform::Footprint; use core_types::{Color, Ctx, num_traits}; use glam::{DAffine2, DVec2}; use graphic_types::raster_types::{CPU, GPU, Raster}; -use graphic_types::{Artboard, Graphic, Vector}; +use graphic_types::{Artboard, Graphic, IntoGraphicList, Vector}; use log::warn; use math_parser::ast; use math_parser::context::{EvalContext, NothingMap, ValueProvider}; @@ -985,6 +985,29 @@ fn normalize(_: impl Ctx, vector: DVec2) -> DVec2 { vector.normalize_or_zero() } +/// Sets the `fill_graphic` attribute on each item of the input vector list. +/// Used for testing of clipping-based fill rendering until the proper Fill node refactor lands. +#[node_macro::node(category("Debug"))] +fn fill_graphic( + _: impl Ctx, + mut vectors: List, + #[implementations( + List, + List, + List>, + List>, + List, + List, + )] + fill_graphic: P, +) -> List { + let paint_list = fill_graphic.into_graphic_list(); + for row_idx in 0..vectors.len() { + vectors.set_attribute(ATTR_FILL_GRAPHIC, row_idx, paint_list.clone()); + } + vectors +} + #[cfg(test)] mod test { use super::*;