Make the Gradient tool work with gradient node chains feeding a Fill node (#4177)

* Add Gradient node

* Add support of new Gradient node for Gradient tool

* Adapt gemini reviews

* Add tests

* Bring back visibility check

* Remove the 'Gradient' node since it's unwanted after all

* Formatting

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
YohYamasaki
2026-06-06 05:23:14 +02:00
committed by GitHub
parent 9d2071ce6a
commit a3b62dac00
5 changed files with 172 additions and 39 deletions

View File

@@ -181,6 +181,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::ExtrudeJoiningAlgorithm]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientSpreadMethod]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text::TextAlign]),

View File

@@ -1,13 +1,13 @@
//! Contains stylistic options for SVG elements.
pub use crate::gradient::*;
use core_types::ATTR_OPACITY;
use core_types::Color;
use core_types::color::{Alpha, SRGBA8};
use core_types::list::List;
use core_types::transform::Transform;
use core_types::{ATTR_GRADIENT_TYPE, ATTR_OPACITY, ATTR_SPREAD_METHOD, ATTR_TRANSFORM, Color};
use dyn_any::DynAny;
use glam::DAffine2;
use glam::DVec2;
use std::f64::consts::{PI, TAU};
/// Describes the fill of a layer.
@@ -143,9 +143,16 @@ impl From<List<Color>> for Fill {
impl From<List<GradientStops>> for Fill {
fn from(gradient: List<GradientStops>) -> Fill {
let gradient_type = gradient.attribute_cloned_or_default::<GradientType>(ATTR_GRADIENT_TYPE, 0);
let spread_method = gradient.attribute_cloned_or_default::<GradientSpreadMethod>(ATTR_SPREAD_METHOD, 0);
let transform = gradient.attribute_cloned_or_default::<DAffine2>(ATTR_TRANSFORM, 0);
Fill::Gradient(Gradient {
stops: gradient.element(0).cloned().unwrap_or_default(),
..Default::default()
gradient_type,
spread_method,
start: transform.transform_point2(DVec2::ZERO),
end: transform.transform_point2(DVec2::X),
})
}
}
@@ -772,3 +779,39 @@ pub enum RenderMode {
/// Render a preview of how the object would be exported as an SVG.
SvgPreview,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fill_from_gradient_list_preserves_attributes() {
let mut list = List::new_from_element(GradientStops::default());
list.set_attribute(ATTR_GRADIENT_TYPE, 0, GradientType::Radial);
list.set_attribute(ATTR_SPREAD_METHOD, 0, GradientSpreadMethod::Reflect);
list.set_attribute(ATTR_TRANSFORM, 0, DAffine2::from_translation(DVec2::new(5., 7.)));
let Fill::Gradient(gradient) = Fill::from(list) else {
panic!("expected Fill::Gradient");
};
assert_eq!(gradient.gradient_type, GradientType::Radial);
assert_eq!(gradient.spread_method, GradientSpreadMethod::Reflect);
assert_eq!(gradient.start, DVec2::new(5., 7.));
assert_eq!(gradient.end, DVec2::new(6., 7.));
}
#[test]
fn fill_from_empty_gradient_list_uses_defaults() {
let list = List::new_from_element(GradientStops::default());
let Fill::Gradient(gradient) = Fill::from(list) else {
panic!("expected Fill::Gradient");
};
assert_eq!(gradient.gradient_type, GradientType::default());
assert_eq!(gradient.spread_method, GradientSpreadMethod::default());
assert_eq!(gradient.start, DVec2::ZERO);
assert_eq!(gradient.end, DVec2::X);
}
}