Rename spread method to gradient spread so it matches the other gradient attribute names (#4404)

* Rename spread method to gradient spread so it matches the other gradient attribute names

* Keep the legacy gradient struct's spread method field name matching its on-disk key
This commit is contained in:
Keavon Chambers
2026-09-15 14:38:59 +02:00
committed by Dennis Kobert
parent 8d4f1346a1
commit d5e31c23bb
23 changed files with 245 additions and 240 deletions
@@ -1,6 +1,6 @@
use crate::messages::layout::utility_types::widgets::input_widgets::{SpectrumInputUpdate, VisualColorPickersInputUpdate}; use crate::messages::layout::utility_types::widgets::input_widgets::{SpectrumInputUpdate, VisualColorPickersInputUpdate};
use crate::messages::prelude::*; use crate::messages::prelude::*;
use graphene_std::vector::style::{FillChoice, GradientSpreadMethod}; use graphene_std::vector::style::{FillChoice, GradientSpread};
/// Identifies which RGB channel a numeric input change targets. /// Identifies which RGB channel a numeric input change targets.
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
@@ -47,8 +47,8 @@ pub enum ColorPickerMessage {
/// `SpectrumInput` change: marker move/insert/delete, midpoint move/reset, or active marker selection changed. /// `SpectrumInput` change: marker move/insert/delete, midpoint move/reset, or active marker selection changed.
GradientUpdate { update: SpectrumInputUpdate }, GradientUpdate { update: SpectrumInputUpdate },
/// Spread method choice from the gradient "Ends" selection. /// Gradient spread choice from the gradient "Ends" selection.
SetSpreadMethod { spread_method: GradientSpreadMethod }, SetGradientSpread { gradient_spread: GradientSpread },
/// Tell the frontend to start an undo transaction (forwarded as a `FrontendMessage` it bridges out to the picker's parent). /// Tell the frontend to start an undo transaction (forwarded as a `FrontendMessage` it bridges out to the picker's parent).
StartTransaction, StartTransaction,
@@ -5,7 +5,7 @@ use crate::messages::prelude::*;
use graphene_std::Color; use graphene_std::Color;
use graphene_std::color::SRGBA8; use graphene_std::color::SRGBA8;
use graphene_std::core_types::misc::parse_css_color; use graphene_std::core_types::misc::parse_css_color;
use graphene_std::vector::style::{FillChoice, Gradient, GradientRamp, GradientSpreadMethod, GradientStops}; use graphene_std::vector::style::{FillChoice, Gradient, GradientRamp, GradientSpread, GradientStops};
/// Bounds for a midpoint position (relative to the interval between two adjacent gradient stops). /// Bounds for a midpoint position (relative to the interval between two adjacent gradient stops).
const MIN_MIDPOINT: f64 = 0.01; const MIN_MIDPOINT: f64 = 0.01;
@@ -29,7 +29,7 @@ pub struct ColorPickerMessageHandler {
// When set, the picker is editing a gradient: the visual pickers and inputs target the active stop's color. // When set, the picker is editing a gradient: the visual pickers and inputs target the active stop's color.
gradient: Option<Gradient>, gradient: Option<Gradient>,
spread_method: GradientSpreadMethod, gradient_spread: GradientSpread,
active_marker_index: Option<u32>, active_marker_index: Option<u32>,
active_marker_is_midpoint: bool, active_marker_is_midpoint: bool,
@@ -51,7 +51,7 @@ impl Default for ColorPickerMessageHandler {
old_alpha: 1., old_alpha: 1.,
old_is_none: true, old_is_none: true,
gradient: None, gradient: None,
spread_method: GradientSpreadMethod::default(), gradient_spread: GradientSpread::default(),
active_marker_index: None, active_marker_index: None,
active_marker_is_midpoint: false, active_marker_is_midpoint: false,
allow_none: true, allow_none: true,
@@ -72,13 +72,13 @@ impl MessageHandler<ColorPickerMessage, ()> for ColorPickerMessageHandler {
FillChoice::None => { FillChoice::None => {
self.set_new_hsva(0., 0., 0., 1., true); self.set_new_hsva(0., 0., 0., 1., true);
self.gradient = None; self.gradient = None;
self.spread_method = GradientSpreadMethod::default(); self.gradient_spread = GradientSpread::default();
self.active_marker_index = None; self.active_marker_index = None;
self.active_marker_is_midpoint = false; self.active_marker_is_midpoint = false;
} }
FillChoice::Solid(color) => { FillChoice::Solid(color) => {
self.gradient = None; self.gradient = None;
self.spread_method = GradientSpreadMethod::default(); self.gradient_spread = GradientSpread::default();
self.active_marker_index = None; self.active_marker_index = None;
self.active_marker_is_midpoint = false; self.active_marker_is_midpoint = false;
self.adopt_color(color); self.adopt_color(color);
@@ -86,7 +86,7 @@ impl MessageHandler<ColorPickerMessage, ()> for ColorPickerMessageHandler {
FillChoice::Gradient(ramp) => { FillChoice::Gradient(ramp) => {
self.active_marker_index = Some(0); self.active_marker_index = Some(0);
self.active_marker_is_midpoint = false; self.active_marker_is_midpoint = false;
self.spread_method = ramp.spread_method; self.gradient_spread = ramp.gradient_spread;
let gradient = Gradient::from(ramp); let gradient = Gradient::from(ramp);
let first_color = gradient.color(0).unwrap_or(Color::BLACK); let first_color = gradient.color(0).unwrap_or(Color::BLACK);
self.gradient = Some(gradient); self.gradient = Some(gradient);
@@ -192,13 +192,13 @@ impl MessageHandler<ColorPickerMessage, ()> for ColorPickerMessageHandler {
self.send_layouts(responses); self.send_layouts(responses);
} }
ColorPickerMessage::GradientUpdate { update } => self.apply_gradient_update(update, responses), ColorPickerMessage::GradientUpdate { update } => self.apply_gradient_update(update, responses),
ColorPickerMessage::SetSpreadMethod { spread_method } => { ColorPickerMessage::SetGradientSpread { gradient_spread } => {
let Some(gradient) = &self.gradient else { return }; let Some(gradient) = &self.gradient else { return };
responses.add(FrontendMessage::ColorPickerStartHistoryTransaction); responses.add(FrontendMessage::ColorPickerStartHistoryTransaction);
self.spread_method = spread_method; self.gradient_spread = gradient_spread;
responses.add(FrontendMessage::ColorPickerColorChanged { responses.add(FrontendMessage::ColorPickerColorChanged {
value: FillChoice::Gradient(GradientRamp { value: FillChoice::Gradient(GradientRamp {
spread_method, gradient_spread,
..GradientRamp::from(gradient) ..GradientRamp::from(gradient)
}), }),
}); });
@@ -289,7 +289,7 @@ impl ColorPickerMessageHandler {
gradient.set_color(active_index as usize, color); gradient.set_color(active_index as usize, color);
responses.add(FrontendMessage::ColorPickerColorChanged { responses.add(FrontendMessage::ColorPickerColorChanged {
value: FillChoice::Gradient(GradientRamp { value: FillChoice::Gradient(GradientRamp {
spread_method: self.spread_method, gradient_spread: self.gradient_spread,
..GradientRamp::from(&*gradient) ..GradientRamp::from(&*gradient)
}), }),
}); });
@@ -419,7 +419,7 @@ impl ColorPickerMessageHandler {
responses.add(FrontendMessage::ColorPickerColorChanged { responses.add(FrontendMessage::ColorPickerColorChanged {
value: FillChoice::Gradient(GradientRamp { value: FillChoice::Gradient(GradientRamp {
spread_method: self.spread_method, gradient_spread: self.gradient_spread,
..GradientRamp::from(&gradient) ..GradientRamp::from(&gradient)
}), }),
}); });
@@ -621,21 +621,21 @@ impl ColorPickerMessageHandler {
.widget_instance(), .widget_instance(),
])); ]));
// Gradient ends spread method (only present when the picker is in gradient mode) // Gradient spread (only present when the picker is in gradient mode)
if self.gradient.is_some() { if self.gradient.is_some() {
let entries = [GradientSpreadMethod::Pad, GradientSpreadMethod::Reflect, GradientSpreadMethod::Repeat] let entries = [GradientSpread::Pad, GradientSpread::Reflect, GradientSpread::Repeat]
.into_iter() .into_iter()
.map(|spread_method| { .map(|gradient_spread| {
RadioEntryData::new(format!("{spread_method:?}")) RadioEntryData::new(format!("{gradient_spread:?}"))
.label(spread_method.to_string()) .label(gradient_spread.to_string())
.on_update(move |_| ColorPickerMessage::SetSpreadMethod { spread_method }.into()) .on_update(move |_| ColorPickerMessage::SetGradientSpread { gradient_spread }.into())
}) })
.collect(); .collect();
groups.push(LayoutGroup::row(vec![ groups.push(LayoutGroup::row(vec![
TextLabel::new("Ends").tooltip_label("Spread Method").tooltip_description(ENDS_DESCRIPTION).widget_instance(), TextLabel::new("Ends").tooltip_label("Gradient Spread").tooltip_description(ENDS_DESCRIPTION).widget_instance(),
Separator::new(SeparatorStyle::Related).widget_instance(), Separator::new(SeparatorStyle::Related).widget_instance(),
RadioInput::new(entries).selected_index(Some(self.spread_method as u32)).disabled(self.disabled).widget_instance(), RadioInput::new(entries).selected_index(Some(self.gradient_spread as u32)).disabled(self.disabled).widget_instance(),
])); ]));
} }
@@ -23,7 +23,7 @@ use graphene_std::transform::{ReferencePoint, ScaleType};
use graphene_std::vector::misc::{ use graphene_std::vector::misc::{
ArcType, BooleanOperation, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType, ArcType, BooleanOperation, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType,
}; };
use graphene_std::vector::style::{DashPattern, FillChoice, GradientRamp, GradientSpreadMethod, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin}; use graphene_std::vector::style::{DashPattern, FillChoice, GradientRamp, GradientSpread, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
use graphene_std::vector::{QRCodeErrorCorrectionLevel, Vector}; use graphene_std::vector::{QRCodeErrorCorrectionLevel, Vector};
use graphene_std::{Artboard, Color, Graphic}; use graphene_std::{Artboard, Color, Graphic};
use std::any::Any; use std::any::Any;
@@ -217,7 +217,7 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
List<DAffine2>, List<DAffine2>,
List<BlendMode>, List<BlendMode>,
List<GradientType>, List<GradientType>,
List<GradientSpreadMethod>, List<GradientSpread>,
List<DashPattern>, List<DashPattern>,
List<BoxCorners>, List<BoxCorners>,
List<StrokeJoin>, List<StrokeJoin>,
@@ -270,7 +270,7 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
DAffine2, DAffine2,
BlendMode, BlendMode,
GradientType, GradientType,
GradientSpreadMethod, GradientSpread,
DashPattern, DashPattern,
BoxCorners, BoxCorners,
StrokeJoin, StrokeJoin,
@@ -1008,7 +1008,7 @@ macro_rules! impl_table_item_layout_for_choice_enum {
impl_table_item_layout_for_choice_enum!( impl_table_item_layout_for_choice_enum!(
BlendMode, BlendMode,
GradientType, GradientType,
GradientSpreadMethod, GradientSpread,
StrokeJoin, StrokeJoin,
StrokeAlign, StrokeAlign,
StrokeCap, StrokeCap,
@@ -1220,7 +1220,7 @@ macro_rules! known_item_types {
BoxCorners, BoxCorners,
BlendMode, BlendMode,
GradientType, GradientType,
GradientSpreadMethod, GradientSpread,
StrokeJoin, StrokeJoin,
StrokeAlign, StrokeAlign,
StrokeCap, StrokeCap,
@@ -1325,7 +1325,7 @@ fn table_node_id_path_layout_with_breadcrumb(path: &List<NodeId>, data: &mut Lay
} }
/// Type-dispatched recursion into an attribute value for the Data panel breadcrumb navigation. /// Type-dispatched recursion into an attribute value for the Data panel breadcrumb navigation.
/// Mirrors [`dispatch_value_widget`] but routes to [`TableItemLayout::layout_with_breadcrumb`]. /// Mirrors [`dispatch_value_widgets`] but routes to [`TableItemLayout::layout_with_breadcrumb`].
/// Returns `None` for unrecognized types. /// Returns `None` for unrecognized types.
fn drilldown_attribute_layout(any: &dyn Any, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> { fn drilldown_attribute_layout(any: &dyn Any, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> {
// `List<NodeId>` is interpreted as a path (e.g. the `editor:layer_path` attribute), so each item's NodeId value // `List<NodeId>` is interpreted as a path (e.g. the `editor:layer_path` attribute), so each item's NodeId value
@@ -10,7 +10,7 @@ use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Image; use graphene_std::raster_types::Image;
use graphene_std::subpath::Subpath; use graphene_std::subpath::Subpath;
use graphene_std::text::{Font, TypesettingConfig}; use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::style::{GradientSpreadMethod, GradientType, Stroke}; use graphene_std::vector::style::{GradientSpread, GradientType, Stroke};
use graphene_std::vector::{Gradient, PointId, VectorModificationType}; use graphene_std::vector::{Gradient, PointId, VectorModificationType};
#[impl_message(Message, DocumentMessage, GraphOperation)] #[impl_message(Message, DocumentMessage, GraphOperation)]
@@ -25,7 +25,7 @@ pub enum GraphOperationMessage {
#[serde(skip)] #[serde(skip)]
gradient: Gradient, gradient: Gradient,
gradient_type: GradientType, gradient_type: GradientType,
spread_method: GradientSpreadMethod, gradient_spread: GradientSpread,
transform: DAffine2, transform: DAffine2,
}, },
BlendingFillSet { BlendingFillSet {
@@ -53,9 +53,9 @@ pub enum GraphOperationMessage {
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
gradient_type: GradientType, gradient_type: GradientType,
}, },
GradientSpreadMethodSet { GradientSpreadSet {
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
spread_method: GradientSpreadMethod, gradient_spread: GradientSpread,
}, },
OpacitySet { OpacitySet {
layer: LayerNodeIdentifier, layer: LayerNodeIdentifier,
@@ -14,7 +14,7 @@ use graph_craft::document::{NodeId, NodeInput};
use graphene_std::list::List; use graphene_std::list::List;
use graphene_std::renderer::convert_usvg_path::convert_usvg_path; use graphene_std::renderer::convert_usvg_path::convert_usvg_path;
use graphene_std::text::{Font, TypesettingConfig}; use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::style::{Gradient, GradientSpreadMethod, GradientStop, GradientType, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin}; use graphene_std::vector::style::{Gradient, GradientSpread, GradientStop, GradientType, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
use graphene_std::{Artboard, Color}; use graphene_std::{Artboard, Color};
#[derive(ExtractField)] #[derive(ExtractField)]
@@ -44,11 +44,11 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
layer, layer,
gradient, gradient,
gradient_type, gradient_type,
spread_method, gradient_spread,
transform, transform,
} => { } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) { if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
modify_inputs.fill_gradient_set(gradient, gradient_type, spread_method, transform); modify_inputs.fill_gradient_set(gradient, gradient_type, gradient_spread, transform);
} }
} }
GraphOperationMessage::BlendingFillSet { layer, fill } => { GraphOperationMessage::BlendingFillSet { layer, fill } => {
@@ -81,9 +81,9 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
modify_inputs.gradient_type_set(gradient_type); modify_inputs.gradient_type_set(gradient_type);
} }
} }
GraphOperationMessage::GradientSpreadMethodSet { layer, spread_method } => { GraphOperationMessage::GradientSpreadSet { layer, gradient_spread } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) { if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer, network_interface, responses) {
modify_inputs.gradient_spread_method_set(spread_method); modify_inputs.gradient_spread_set(gradient_spread);
} }
} }
GraphOperationMessage::OpacitySet { layer, opacity } => { GraphOperationMessage::OpacitySet { layer, opacity } => {
@@ -806,11 +806,11 @@ fn apply_usvg_stroke(stroke: &usvg::Stroke, modify_inputs: &mut ModifyInputsCont
} }
} }
fn convert_spread_method(spread_method: usvg::SpreadMethod) -> GradientSpreadMethod { fn convert_gradient_spread(spread_method: usvg::SpreadMethod) -> GradientSpread {
match spread_method { match spread_method {
usvg::SpreadMethod::Pad => GradientSpreadMethod::Pad, usvg::SpreadMethod::Pad => GradientSpread::Pad,
usvg::SpreadMethod::Reflect => GradientSpreadMethod::Reflect, usvg::SpreadMethod::Reflect => GradientSpread::Reflect,
usvg::SpreadMethod::Repeat => GradientSpreadMethod::Repeat, usvg::SpreadMethod::Repeat => GradientSpread::Repeat,
} }
} }
@@ -837,8 +837,8 @@ fn apply_usvg_fill(fill: &usvg::Fill, modify_inputs: &mut ModifyInputsContext, g
Gradient::new(stops) Gradient::new(stops)
} }
}; };
let spread_method = convert_spread_method(linear.spread_method()); let gradient_spread = convert_gradient_spread(linear.spread_method());
modify_inputs.fill_gradient_set(gradient, gradient_type, spread_method, transform); modify_inputs.fill_gradient_set(gradient, gradient_type, gradient_spread, transform);
} }
usvg::Paint::RadialGradient(radial) => { usvg::Paint::RadialGradient(radial) => {
let gradient_transform = usvg_transform(radial.transform()); let gradient_transform = usvg_transform(radial.transform());
@@ -861,9 +861,9 @@ fn apply_usvg_fill(fill: &usvg::Fill, modify_inputs: &mut ModifyInputsContext, g
Gradient::new(stops) Gradient::new(stops)
} }
}; };
let spread_method = convert_spread_method(radial.spread_method()); let gradient_spread = convert_gradient_spread(radial.spread_method());
modify_inputs.fill_gradient_set(gradient, gradient_type, spread_method, transform); modify_inputs.fill_gradient_set(gradient, gradient_type, gradient_spread, transform);
} }
usvg::Paint::Pattern(_) => warn!("SVG patterns are not currently supported"), usvg::Paint::Pattern(_) => warn!("SVG patterns are not currently supported"),
}; };
@@ -17,7 +17,7 @@ use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Image; use graphene_std::raster_types::Image;
use graphene_std::subpath::Subpath; use graphene_std::subpath::Subpath;
use graphene_std::text::{Font, TypesettingConfig}; use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::style::{GradientSpreadMethod, GradientType, Stroke}; use graphene_std::vector::style::{GradientSpread, GradientType, Stroke};
use graphene_std::vector::{Gradient, GradientRamp, PointId, Vector, VectorModification, VectorModificationType}; use graphene_std::vector::{Gradient, GradientRamp, PointId, Vector, VectorModification, VectorModificationType};
use graphene_std::{Artboard, Color, Graphic}; use graphene_std::{Artboard, Color, Graphic};
@@ -404,14 +404,14 @@ impl<'a> ModifyInputsContext<'a> {
self.set_input_with_refresh(input_connector, NodeInput::value(fill_value, false), false); self.set_input_with_refresh(input_connector, NodeInput::value(fill_value, false), false);
} }
pub fn fill_gradient_set(&mut self, gradient: Gradient, gradient_type: GradientType, spread_method: GradientSpreadMethod, transform: DAffine2) { pub fn fill_gradient_set(&mut self, gradient: Gradient, gradient_type: GradientType, gradient_spread: GradientSpread, transform: DAffine2) {
let Some(fill_node_id) = self.existing_proto_node_id(graphene_std::vector_nodes::fill::IDENTIFIER, true) else { let Some(fill_node_id) = self.existing_proto_node_id(graphene_std::vector_nodes::fill::IDENTIFIER, true) else {
return; return;
}; };
let backup_input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::BackupGradientInput); let backup_input_connector = InputConnector::node(fill_node_id, graphene_std::vector::fill::BackupGradientInput);
let ramp = GradientRamp::from(gradient); let ramp = GradientRamp::from(gradient);
let ramp = GradientRamp { spread_method, ..ramp }; let ramp = GradientRamp { gradient_spread, ..ramp };
self.set_input_with_refresh(backup_input_connector, NodeInput::value(TaggedValue::GradientRamp(ramp.clone()), false), true); self.set_input_with_refresh(backup_input_connector, NodeInput::value(TaggedValue::GradientRamp(ramp.clone()), false), true);
// Skip the rerender on all but the last input so the whole update triggers a single graph run // Skip the rerender on all but the last input so the whole update triggers a single graph run
@@ -677,20 +677,20 @@ impl<'a> ModifyInputsContext<'a> {
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::GradientType(gradient_type), false), false); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::GradientType(gradient_type), false), false);
} }
/// Write the spread method to the last 'Spread Method' node in the chain, inserting one only when the value differs /// Write the gradient spread to the last 'Gradient Spread' node in the chain, inserting one only when the value differs
/// from the default (`Pad`). /// from the default (`Pad`).
pub fn gradient_spread_method_set(&mut self, spread_method: GradientSpreadMethod) { pub fn gradient_spread_set(&mut self, gradient_spread: GradientSpread) {
let Some(output_layer) = self.get_output_layer() else { return }; let Some(output_layer) = self.get_output_layer() else { return };
let target_input = gradient_chain_target_input(output_layer, self.network_interface); let target_input = gradient_chain_target_input(output_layer, self.network_interface);
let identifier = graphene_std::math_nodes::spread_method::IDENTIFIER; let identifier = graphene_std::math_nodes::gradient_spread::IDENTIFIER;
let create_if_nonexistent = spread_method != GradientSpreadMethod::default(); let create_if_nonexistent = gradient_spread != GradientSpread::default();
let Some(node_id) = self.existing_proto_node_id_at(&target_input, identifier, create_if_nonexistent) else { let Some(node_id) = self.existing_proto_node_id_at(&target_input, identifier, create_if_nonexistent) else {
return; return;
}; };
let input_connector = InputConnector::node(node_id, graphene_std::math_nodes::spread_method::SpreadMethodInput); let input_connector = InputConnector::node(node_id, graphene_std::math_nodes::gradient_spread::GradientSpreadInput);
self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::GradientSpreadMethod(spread_method), false), false); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::GradientSpread(gradient_spread), false), false);
} }
pub fn clip_mode_toggle(&mut self, clip_mode: Option<bool>) { pub fn clip_mode_toggle(&mut self, clip_mode: Option<bool>) {
@@ -34,7 +34,7 @@ use graphene_std::vector::misc::{
ArcType, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType, ArcType, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType,
}; };
use graphene_std::vector::style::{ use graphene_std::vector::style::{
FillChoice, Gradient, GradientRamp, GradientSpreadMethod, GradientStops, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation, FillChoice, Gradient, GradientRamp, GradientSpread, GradientStops, GradientType, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation,
}; };
use graphene_std::vector::{QRCodeErrorCorrectionLevel, VectorModification}; use graphene_std::vector::{QRCodeErrorCorrectionLevel, VectorModification};
use graphene_std::{NodeParameter, ParameterRef}; use graphene_std::{NodeParameter, ParameterRef};
@@ -293,7 +293,7 @@ pub(crate) fn property_from_type(
// AUTO-GENERATED ENUM TYPES // AUTO-GENERATED ENUM TYPES
// ========================= // =========================
Some(x) if id_is::<GradientType>(x) => enum_choice::<GradientType>().for_socket(default_info).property_row(), Some(x) if id_is::<GradientType>(x) => enum_choice::<GradientType>().for_socket(default_info).property_row(),
Some(x) if id_is::<GradientSpreadMethod>(x) => enum_choice::<GradientSpreadMethod>().for_socket(default_info).property_row(), Some(x) if id_is::<GradientSpread>(x) => enum_choice::<GradientSpread>().for_socket(default_info).property_row(),
Some(x) if id_is::<RealTimeMode>(x) => enum_choice::<RealTimeMode>().for_socket(default_info).property_row(), Some(x) if id_is::<RealTimeMode>(x) => enum_choice::<RealTimeMode>().for_socket(default_info).property_row(),
Some(x) if id_is::<RedGreenBlue>(x) => enum_choice::<RedGreenBlue>().for_socket(default_info).property_row(), Some(x) if id_is::<RedGreenBlue>(x) => enum_choice::<RedGreenBlue>().for_socket(default_info).property_row(),
Some(x) if id_is::<RedGreenBlueAlpha>(x) => enum_choice::<RedGreenBlueAlpha>().for_socket(default_info).property_row(), Some(x) if id_is::<RedGreenBlueAlpha>(x) => enum_choice::<RedGreenBlueAlpha>().for_socket(default_info).property_row(),
@@ -2403,7 +2403,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
Gradient { Gradient {
gradient: Gradient, gradient: Gradient,
gradient_type: GradientType, gradient_type: GradientType,
spread_method: GradientSpreadMethod, gradient_spread: GradientSpread,
transform: DAffine2, transform: DAffine2,
/// Whether the transform input holds a plain value (so the "Reverse Direction" button may write to it) rather than a wire. /// Whether the transform input holds a plain value (so the "Reverse Direction" button may write to it) rather than a wire.
transform_is_value: bool, transform_is_value: bool,
@@ -2433,7 +2433,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
Some(gradient) => ResolvedFill::Gradient { Some(gradient) => ResolvedFill::Gradient {
gradient: gradient.stops, gradient: gradient.stops,
gradient_type: gradient.gradient_type, gradient_type: gradient.gradient_type,
spread_method: gradient.spread_method, gradient_spread: gradient.gradient_spread,
transform: gradient.transform, transform: gradient.transform,
transform_is_value: gradient.transform_is_value, transform_is_value: gradient.transform_is_value,
}, },
@@ -2461,9 +2461,9 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
}; };
match &fill { match &fill {
ResolvedFill::Gradient { gradient: stops, spread_method, .. } => { ResolvedFill::Gradient { gradient: stops, gradient_spread, .. } => {
let stops = stops.clone(); let stops = stops.clone();
let spread_method = *spread_method; let gradient_spread = *gradient_spread;
let reverse_button = IconButton::new("Reverse", 24) let reverse_button = IconButton::new("Reverse", 24)
.tooltip_label("Reverse Stops") .tooltip_label("Reverse Stops")
@@ -2471,7 +2471,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
.on_update(update_value( .on_update(update_value(
move |_| { move |_| {
TaggedValue::GradientRamp(GradientRamp { TaggedValue::GradientRamp(GradientRamp {
spread_method, gradient_spread,
..GradientRamp::from(stops.reversed()) ..GradientRamp::from(stops.reversed())
}) })
}, },
@@ -2493,8 +2493,8 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
FillChoice::<SRGBA8>::None FillChoice::<SRGBA8>::None
} }
} }
ResolvedFill::Gradient { gradient: stops, spread_method, .. } => FillChoice::<SRGBA8>::Gradient(GradientRamp { ResolvedFill::Gradient { gradient: stops, gradient_spread, .. } => FillChoice::<SRGBA8>::Gradient(GradientRamp {
spread_method: *spread_method, gradient_spread: *gradient_spread,
..GradientRamp::from(stops) ..GradientRamp::from(stops)
}), }),
ResolvedFill::Other => FillChoice::<SRGBA8>::None, ResolvedFill::Other => FillChoice::<SRGBA8>::None,
@@ -813,11 +813,11 @@ async fn legacy_four_input_fill_migrates_to_the_split_transform_shape() {
} }
#[tokio::test] #[tokio::test]
async fn eight_input_fill_migrates_spread_method_into_the_ramp() { async fn eight_input_fill_migrates_the_spread_input_into_the_ramp() {
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graphene_std::vector::style::GradientSpreadMethod; use graphene_std::vector::style::GradientSpread;
// A minimal document from the era when spread method was the Fill node's own sixth input, here set to Repeat // A minimal document from the era when the gradient spread was the Fill node's own sixth input (tagged `GradientSpreadMethod`), here set to Repeat
const EIGHT_INPUT_DOCUMENT: &str = r#"{"network_interface":{"network":{"exports":[{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"nodes":[[1,{"inputs":[{"Value":{"tagged_value":{"GraphicGroup":{"instance":[],"transform":[],"alpha_blending":[],"source_node_id":[]}},"exposed":true}},{"Value":{"tagged_value":{"GradientRamp":{"stops":{"color":[{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0},{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]}}},"exposed":false}},{"Value":{"tagged_value":{"Color":{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientRamp":{"stops":{"color":[{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0},{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]}}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"GradientSpreadMethod":"Repeat"},"exposed":false}},{"Value":{"tagged_value":{"Bool":false},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[1.0,0.0,0.0,1.0,0.0,0.0]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::vector::FillNode"}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]},"network_metadata":{"persistent_metadata":{"node_metadata":[[1,{"persistent_metadata":{"reference":"Fill","display_name":"","input_properties":[{"input_data":{"input_name":"Content"},"widget_override":null},{"input_data":{"input_name":"Fill"},"widget_override":null},{"input_data":{"input_name":"Backup Color"},"widget_override":null},{"input_data":{"input_name":"Backup Gradient"},"widget_override":null},{"input_data":{"input_name":"Gradient Type"},"widget_override":null},{"input_data":{"input_name":"Spread Method"},"widget_override":null},{"input_data":{"input_name":"Has Transform"},"widget_override":null},{"input_data":{"input_name":"Transform"},"widget_override":null}],"output_names":[""],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,0]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}},"collapsed":[],"name":"eight_input_fill.graphite","commit_hash":"0000000000000000000000000000000000000000","document_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"document_mode":"DesignMode","view_mode":"Normal","overlays_visibility_settings":{"all":true,"artboard_name":true,"compass_rose":true,"quick_measurement":true,"transform_measurement":true,"transform_cage":true,"hover_outline":true,"selection_outline":true,"pivot":true,"path":true,"anchors":true,"handles":true},"rulers_visible":true,"snapping_state":{"snapping_enabled":true,"grid_snapping":false,"artboards":true,"tolerance":8.0,"bounding_box":{"center_point":true,"corner_point":true,"edge_midpoint":true,"align_with_edges":true,"distribute_evenly":true},"path":{"anchor_point":true,"line_midpoint":true,"along_path":true,"normal_to_path":true,"tangent_to_path":true,"path_intersection_point":true,"align_with_anchor_point":true,"perpendicular_from_endpoint":true},"grid":{"origin":[0.0,0.0],"grid_type":{"Rectangular":{"spacing":[1.0,1.0]}},"grid_color":{"red":0.6,"green":0.6,"blue":0.6,"alpha":1.0},"dot_display":false}},"graph_view_overlay_open":false,"graph_fade_artwork_percentage":80.0}"#; const EIGHT_INPUT_DOCUMENT: &str = r#"{"network_interface":{"network":{"exports":[{"Node":{"node_id":1,"output_index":0,"lambda":false}}],"nodes":[[1,{"inputs":[{"Value":{"tagged_value":{"GraphicGroup":{"instance":[],"transform":[],"alpha_blending":[],"source_node_id":[]}},"exposed":true}},{"Value":{"tagged_value":{"GradientRamp":{"stops":{"color":[{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0},{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]}}},"exposed":false}},{"Value":{"tagged_value":{"Color":{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0}},"exposed":false}},{"Value":{"tagged_value":{"GradientRamp":{"stops":{"color":[{"red":0.0,"green":0.0,"blue":0.0,"alpha":1.0},{"red":1.0,"green":1.0,"blue":1.0,"alpha":1.0}]}}},"exposed":false}},{"Value":{"tagged_value":{"GradientType":"Linear"},"exposed":false}},{"Value":{"tagged_value":{"GradientSpreadMethod":"Repeat"},"exposed":false}},{"Value":{"tagged_value":{"Bool":false},"exposed":false}},{"Value":{"tagged_value":{"DAffine2":[1.0,0.0,0.0,1.0,0.0,0.0]},"exposed":false}}],"manual_composition":{"Concrete":{"name":"core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>","alias":null}},"implementation":{"ProtoNode":{"name":"graphene_core::vector::FillNode"}},"visible":true,"skip_deduplication":false}]],"scope_injections":[]},"network_metadata":{"persistent_metadata":{"node_metadata":[[1,{"persistent_metadata":{"reference":"Fill","display_name":"","input_properties":[{"input_data":{"input_name":"Content"},"widget_override":null},{"input_data":{"input_name":"Fill"},"widget_override":null},{"input_data":{"input_name":"Backup Color"},"widget_override":null},{"input_data":{"input_name":"Backup Gradient"},"widget_override":null},{"input_data":{"input_name":"Gradient Type"},"widget_override":null},{"input_data":{"input_name":"Spread Method"},"widget_override":null},{"input_data":{"input_name":"Has Transform"},"widget_override":null},{"input_data":{"input_name":"Transform"},"widget_override":null}],"output_names":[""],"has_primary_output":true,"locked":false,"pinned":false,"node_type_metadata":{"Node":{"position":{"Absolute":[0,0]}}},"network_metadata":null}}]],"previewing":"No","navigation_metadata":{"node_graph_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"node_graph_to_viewport":[1.0,0.0,0.0,1.0,0.0,0.0],"node_graph_top_right":[0.0,0.0]},"selection_undo_history":[],"selection_redo_history":[]}}},"collapsed":[],"name":"eight_input_fill.graphite","commit_hash":"0000000000000000000000000000000000000000","document_ptz":{"pan":[0.0,0.0],"tilt":0.0,"zoom":1.0,"flip":false},"document_mode":"DesignMode","view_mode":"Normal","overlays_visibility_settings":{"all":true,"artboard_name":true,"compass_rose":true,"quick_measurement":true,"transform_measurement":true,"transform_cage":true,"hover_outline":true,"selection_outline":true,"pivot":true,"path":true,"anchors":true,"handles":true},"rulers_visible":true,"snapping_state":{"snapping_enabled":true,"grid_snapping":false,"artboards":true,"tolerance":8.0,"bounding_box":{"center_point":true,"corner_point":true,"edge_midpoint":true,"align_with_edges":true,"distribute_evenly":true},"path":{"anchor_point":true,"line_midpoint":true,"along_path":true,"normal_to_path":true,"tangent_to_path":true,"path_intersection_point":true,"align_with_anchor_point":true,"perpendicular_from_endpoint":true},"grid":{"origin":[0.0,0.0],"grid_type":{"Rectangular":{"spacing":[1.0,1.0]}},"grid_color":{"red":0.6,"green":0.6,"blue":0.6,"alpha":1.0},"dot_display":false}},"graph_view_overlay_open":false,"graph_fade_artwork_percentage":80.0}"#;
// Deserializing alone must succeed, so a failure below is attributable to the migrations // Deserializing alone must succeed, so a failure below is attributable to the migrations
@@ -843,17 +843,13 @@ async fn eight_input_fill_migrates_spread_method_into_the_ramp() {
let Some(TaggedValue::GradientRamp(ramp)) = paint else { let Some(TaggedValue::GradientRamp(ramp)) = paint else {
panic!("the fill input should keep its gradient ramp value, but became {paint:?}"); panic!("the fill input should keep its gradient ramp value, but became {paint:?}");
}; };
assert_eq!(ramp.spread_method, GradientSpreadMethod::Repeat, "the spread method input's value should fold into the fill ramp"); assert_eq!(ramp.gradient_spread, GradientSpread::Repeat, "the spread input's value should fold into the fill ramp");
let backup = fill_node.input_value(graphene_std::vector::fill::BackupGradientInput); let backup = fill_node.input_value(graphene_std::vector::fill::BackupGradientInput);
let Some(TaggedValue::GradientRamp(backup_ramp)) = backup else { let Some(TaggedValue::GradientRamp(backup_ramp)) = backup else {
panic!("the backup gradient input should keep its gradient ramp value, but became {backup:?}"); panic!("the backup gradient input should keep its gradient ramp value, but became {backup:?}");
}; };
assert_eq!( assert_eq!(backup_ramp.gradient_spread, GradientSpread::Repeat, "the spread input's value should fold into the backup ramp");
backup_ramp.spread_method,
GradientSpreadMethod::Repeat,
"the spread method input's value should fold into the backup ramp"
);
let has_transform = fill_node.input_value(graphene_std::vector::fill::HasTransformInput); let has_transform = fill_node.input_value(graphene_std::vector::fill::HasTransformInput);
assert!( assert!(
@@ -17,7 +17,7 @@ use graphene_std::text::{TextAlign, TypesettingConfig};
use graphene_std::transform::ScaleType; use graphene_std::transform::ScaleType;
use graphene_std::uuid::NodeId; use graphene_std::uuid::NodeId;
use graphene_std::vector::graphic_types; use graphene_std::vector::graphic_types;
use graphene_std::vector::style::{GradientRamp, GradientSpreadMethod, PaintOrder, StrokeAlign}; use graphene_std::vector::style::{GradientRamp, GradientSpread, PaintOrder, StrokeAlign};
use std::collections::HashMap; use std::collections::HashMap;
use std::f64::consts::PI; use std::f64::consts::PI;
use std::ops::Range; use std::ops::Range;
@@ -168,6 +168,10 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
"graphic_nodes::graphic::ItemAtIndexNode", "graphic_nodes::graphic::ItemAtIndexNode",
], ],
}, },
NodeReplacement {
node: graphene_std::graphic::read_gradient_spread_attribute::IDENTIFIER,
aliases: &["graphic_nodes::graphic::ReadAttributeSpreadMethodNode", "graphic_nodes::graphic::ReadSpreadMethodAttributeNode"],
},
NodeReplacement { NodeReplacement {
node: graphene_std::list::remove_at_index::IDENTIFIER, node: graphene_std::list::remove_at_index::IDENTIFIER,
aliases: &["graphic_nodes::graphic::OmitElementNode", "graphic_nodes::graphic::RemoveAtIndexNode"], aliases: &["graphic_nodes::graphic::OmitElementNode", "graphic_nodes::graphic::RemoveAtIndexNode"],
@@ -265,6 +269,10 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
node: graphene_std::math_nodes::footprint_value::IDENTIFIER, node: graphene_std::math_nodes::footprint_value::IDENTIFIER,
aliases: &["graphene_math_nodes::FootprintValueNode", "graphene_core::ops::FootprintValueNode"], aliases: &["graphene_math_nodes::FootprintValueNode", "graphene_core::ops::FootprintValueNode"],
}, },
NodeReplacement {
node: graphene_std::math_nodes::gradient_spread::IDENTIFIER,
aliases: &["math_nodes::SpreadMethodNode"],
},
NodeReplacement { NodeReplacement {
node: graphene_std::math_nodes::gradient_value::IDENTIFIER, node: graphene_std::math_nodes::gradient_value::IDENTIFIER,
aliases: &[ aliases: &[
@@ -1413,10 +1421,10 @@ fn migrate_corner_radius_input(input: &NodeInput) -> Option<NodeInput> {
Some(NodeInput::value(TaggedValue::BoxCorners(values), *exposed)) Some(NodeInput::value(TaggedValue::BoxCorners(values), *exposed))
} }
/// Rewrites a gradient ramp value input to carry the given spread method, which used to live in the Fill node's retired `_spread_method` input. /// Rewrites a gradient ramp value input to carry the given gradient spread, which used to live in the Fill node's retired `_spread_method` input.
fn fold_spread_method_into_ramp_input(input: &NodeInput, spread_method: GradientSpreadMethod) -> NodeInput { fn fold_gradient_spread_into_ramp_input(input: &NodeInput, gradient_spread: GradientSpread) -> NodeInput {
match input.as_value() { match input.as_value() {
Some(TaggedValue::GradientRamp(ramp)) => NodeInput::value(TaggedValue::GradientRamp(GradientRamp { spread_method, ..ramp.clone() }), input.is_exposed()), Some(TaggedValue::GradientRamp(ramp)) => NodeInput::value(TaggedValue::GradientRamp(GradientRamp { gradient_spread, ..ramp.clone() }), input.is_exposed()),
_ => input.clone(), _ => input.clone(),
} }
} }
@@ -1682,7 +1690,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
graphic_types::migrations::legacy::LegacyFill::None => TaggedValue::no_paint(), graphic_types::migrations::legacy::LegacyFill::None => TaggedValue::no_paint(),
graphic_types::migrations::legacy::LegacyFill::Solid(color) => TaggedValue::Color(*color), graphic_types::migrations::legacy::LegacyFill::Solid(color) => TaggedValue::Color(*color),
graphic_types::migrations::legacy::LegacyFill::Gradient(gradient) => TaggedValue::GradientRamp(GradientRamp { graphic_types::migrations::legacy::LegacyFill::Gradient(gradient) => TaggedValue::GradientRamp(GradientRamp {
spread_method: gradient.spread_method, gradient_spread: gradient.spread_method,
..gradient.stops.clone() ..gradient.stops.clone()
}), }),
}; };
@@ -1729,7 +1737,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
&InputConnector::node_at_index(*node_id, 3), &InputConnector::node_at_index(*node_id, 3),
NodeInput::value( NodeInput::value(
TaggedValue::GradientRamp(GradientRamp { TaggedValue::GradientRamp(GradientRamp {
spread_method: g.spread_method, gradient_spread: g.spread_method,
..g.stops.clone() ..g.stops.clone()
}), }),
false, false,
@@ -1770,20 +1778,20 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
// Fill split its `Option<DAffine2>` placement into a `_has_transform` bool immediately before the `_transform` matrix. The modern // Fill split its `Option<DAffine2>` placement into a `_has_transform` bool immediately before the `_transform` matrix. The modern
// shape is also 7 inputs, so this era is identified by its `_spread_method` input at 5 or its optional transform at 6. // shape is also 7 inputs, so this era is identified by its `_spread_method` input at 5 or its optional transform at 6.
let is_pre_transform_split_fill = inputs_count == 7 let is_pre_transform_split_fill = inputs_count == 7
&& (matches!(node.inputs.get(5).and_then(|input| input.as_value()), Some(TaggedValue::GradientSpreadMethod(_))) && (matches!(node.inputs.get(5).and_then(|input| input.as_value()), Some(TaggedValue::GradientSpread(_)))
|| matches!(node.inputs.get(6).and_then(|input| input.as_value()), Some(TaggedValue::LegacyOptionalDAffine2(_)))); || matches!(node.inputs.get(6).and_then(|input| input.as_value()), Some(TaggedValue::LegacyOptionalDAffine2(_))));
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::fill::IDENTIFIER) && is_pre_transform_split_fill { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::fill::IDENTIFIER) && is_pre_transform_split_fill {
let mut node_template = resolve_document_node_type(&reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
let spread_method = match old_inputs.get(5).and_then(|input| input.as_value()) { let gradient_spread = match old_inputs.get(5).and_then(|input| input.as_value()) {
Some(&TaggedValue::GradientSpreadMethod(value)) => value, Some(&TaggedValue::GradientSpread(value)) => value,
_ => GradientSpreadMethod::default(), _ => GradientSpread::default(),
}; };
for (index, input) in old_inputs.iter().enumerate().take(5) { for (index, input) in old_inputs.iter().enumerate().take(5) {
let input = if index == 1 || index == 3 { let input = if index == 1 || index == 3 {
fold_spread_method_into_ramp_input(input, spread_method) fold_gradient_spread_into_ramp_input(input, gradient_spread)
} else { } else {
input.clone() input.clone()
}; };
@@ -1814,19 +1822,19 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
inputs_count = 7; inputs_count = 7;
} }
// The Fill node's `_spread_method` input moved into the `GradientRamp` value's own `spread_method` field // The Fill node's `_spread_method` input moved into the `GradientRamp` value's own `gradient_spread` field
if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::fill::IDENTIFIER) && inputs_count == 8 { if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::fill::IDENTIFIER) && inputs_count == 8 {
let mut node_template = resolve_document_node_type(&reference)?.default_node_template(); let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?; let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut node_template)?;
let spread_method = match old_inputs.get(5).and_then(|input| input.as_value()) { let gradient_spread = match old_inputs.get(5).and_then(|input| input.as_value()) {
Some(&TaggedValue::GradientSpreadMethod(value)) => value, Some(&TaggedValue::GradientSpread(value)) => value,
_ => GradientSpreadMethod::default(), _ => GradientSpread::default(),
}; };
for (index, input) in old_inputs.iter().enumerate().take(5) { for (index, input) in old_inputs.iter().enumerate().take(5) {
let input = if index == 1 || index == 3 { let input = if index == 1 || index == 3 {
fold_spread_method_into_ramp_input(input, spread_method) fold_gradient_spread_into_ramp_input(input, gradient_spread)
} else { } else {
input.clone() input.clone()
}; };
@@ -14,7 +14,7 @@ use graphene_std::subpath::Subpath;
use graphene_std::text::{Font, TypesettingConfig}; use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::misc::ManipulatorPointId; use graphene_std::vector::misc::ManipulatorPointId;
use graphene_std::vector::style::{FillChoice, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, initial_gradient_transform_for_bounding_box}; use graphene_std::vector::style::{FillChoice, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, initial_gradient_transform_for_bounding_box};
use graphene_std::vector::{Gradient, GradientSpreadMethod, GradientType, PointId, SegmentId, VectorModificationType}; use graphene_std::vector::{Gradient, GradientSpread, GradientType, PointId, SegmentId, VectorModificationType};
use graphene_std::{NodeParameter, ParameterRef}; use graphene_std::{NodeParameter, ParameterRef};
use std::collections::VecDeque; use std::collections::VecDeque;
@@ -660,7 +660,7 @@ pub fn set_stroke_weight_for_selected_layers(weight: f64, document: &DocumentMes
pub struct FillNodeGradient { pub struct FillNodeGradient {
pub stops: Gradient, pub stops: Gradient,
pub gradient_type: GradientType, pub gradient_type: GradientType,
pub spread_method: GradientSpreadMethod, pub gradient_spread: GradientSpread,
pub transform: DAffine2, pub transform: DAffine2,
/// Whether the transform input holds a plain value (so it may be written to) rather than a wire. /// Whether the transform input holds a plain value (so it may be written to) rather than a wire.
pub transform_is_value: bool, pub transform_is_value: bool,
@@ -673,7 +673,7 @@ pub fn read_fill_node_gradient(fill_node: &DocumentNode, bounding_box: impl FnOn
let TaggedValue::GradientRamp(ramp) = fill_node.input(fill::FillInput)?.as_value()? else { let TaggedValue::GradientRamp(ramp) = fill_node.input(fill::FillInput)?.as_value()? else {
return None; return None;
}; };
let spread_method = ramp.spread_method; let gradient_spread = ramp.gradient_spread;
let stops = Gradient::from(ramp); let stops = Gradient::from(ramp);
let gradient_type = match fill_node.input(fill::GradientTypeInput).and_then(|input| input.as_value()) { let gradient_type = match fill_node.input(fill::GradientTypeInput).and_then(|input| input.as_value()) {
Some(&TaggedValue::GradientType(value)) => value, Some(&TaggedValue::GradientType(value)) => value,
@@ -690,7 +690,7 @@ pub fn read_fill_node_gradient(fill_node: &DocumentNode, bounding_box: impl FnOn
Some(FillNodeGradient { Some(FillNodeGradient {
stops, stops,
gradient_type, gradient_type,
spread_method, gradient_spread,
transform, transform,
transform_is_value: transform_input.is_some(), transform_is_value: transform_input.is_some(),
}) })
@@ -839,7 +839,7 @@ pub fn set_fill_for_selected_layers(fill_choice: FillChoice, document: &Document
layer, layer,
gradient: Gradient::from(ramp), gradient: Gradient::from(ramp),
gradient_type, gradient_type,
spread_method: ramp.spread_method, gradient_spread: ramp.gradient_spread,
transform, transform,
}); });
} }
@@ -16,7 +16,7 @@ use glam::DMat2;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graphene_std::color::SRGBA8; use graphene_std::color::SRGBA8;
use graphene_std::raster::color::Color; use graphene_std::raster::color::Color;
use graphene_std::vector::style::{FillChoice, Gradient, GradientRamp, GradientSpreadMethod, GradientStop, GradientType, build_transform_with_y_preservation}; use graphene_std::vector::style::{FillChoice, Gradient, GradientRamp, GradientSpread, GradientStop, GradientType, build_transform_with_y_preservation};
#[derive(Default, ExtractField)] #[derive(Default, ExtractField)]
pub struct GradientTool { pub struct GradientTool {
@@ -28,7 +28,7 @@ pub struct GradientTool {
#[derive(Default)] #[derive(Default)]
pub struct GradientOptions { pub struct GradientOptions {
gradient_type: GradientType, gradient_type: GradientType,
spread_method: GradientSpreadMethod, gradient_spread: GradientSpread,
} }
#[impl_message(Message, ToolMessage, Gradient)] #[impl_message(Message, ToolMessage, Gradient)]
@@ -136,8 +136,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Grad
} }
ToolMessage::Gradient(GradientToolMessage::UpdateRamp { ramp }) => { ToolMessage::Gradient(GradientToolMessage::UpdateRamp { ramp }) => {
let ramp = GradientRamp::from(&ramp); let ramp = GradientRamp::from(&ramp);
self.options.spread_method = ramp.spread_method; self.options.gradient_spread = ramp.gradient_spread;
apply_stops_update(&mut self.data, context, responses, Gradient::from(&ramp), ramp.spread_method); apply_stops_update(&mut self.data, context, responses, Gradient::from(&ramp), ramp.gradient_spread);
} }
ToolMessage::Gradient(GradientToolMessage::CloseStopColorPicker) => { ToolMessage::Gradient(GradientToolMessage::CloseStopColorPicker) => {
if self.data.color_picker_transaction_open { if self.data.color_picker_transaction_open {
@@ -171,8 +171,8 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Grad
self.options.gradient_type = appearance.gradient_type; self.options.gradient_type = appearance.gradient_type;
needs_refresh = true; needs_refresh = true;
} }
if self.options.spread_method != appearance.spread_method { if self.options.gradient_spread != appearance.gradient_spread {
self.options.spread_method = appearance.spread_method; self.options.gradient_spread = appearance.gradient_spread;
needs_refresh = true; needs_refresh = true;
} }
} }
@@ -263,7 +263,7 @@ impl LayoutHolder for GradientTool {
]) ])
}); });
let stops_widget = ColorInput::new(FillChoice::Gradient(GradientRamp { let stops_widget = ColorInput::new(FillChoice::Gradient(GradientRamp {
spread_method: self.options.spread_method, gradient_spread: self.options.gradient_spread,
..GradientRamp::from(&stops_value) ..GradientRamp::from(&stops_value)
})) }))
.allow_none(false) .allow_none(false)
@@ -363,7 +363,7 @@ fn resolve_gradient(layer: LayerNodeIdentifier, network_interface: &NodeNetworkI
gradient.stops, gradient.stops,
GradientAppearance { GradientAppearance {
gradient_type: gradient.gradient_type, gradient_type: gradient.gradient_type,
spread_method: gradient.spread_method, gradient_spread: gradient.gradient_spread,
transform: gradient.transform, transform: gradient.transform,
}, },
GradientSource::Direct, GradientSource::Direct,
@@ -382,22 +382,22 @@ fn resolve_gradient(layer: LayerNodeIdentifier, network_interface: &NodeNetworkI
struct GradientAppearance { struct GradientAppearance {
transform: DAffine2, transform: DAffine2,
gradient_type: GradientType, gradient_type: GradientType,
spread_method: GradientSpreadMethod, gradient_spread: GradientSpread,
} }
/// Resolve the gradient transform, type, and spread method by walking the chain feeding the layer. Transform composes all /// Resolve the gradient transform, type, and gradient spread by walking the chain feeding the layer. Transform composes all
/// 'Transform' nodes. Type and spread method come from the closest-to-layer node of each kind, or the type default. /// 'Transform' nodes. Type and gradient spread come from the closest-to-layer node of each kind, or the type default.
fn read_gradient_chain_state(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> GradientAppearance { fn read_gradient_chain_state(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> GradientAppearance {
let target_input = gradient_chain_target_input(layer, network_interface); let target_input = gradient_chain_target_input(layer, network_interface);
let walk_from = network_interface.upstream_output_connector(&target_input, &[]).and_then(|out| out.node_id()).unwrap_or(layer.to_node()); let walk_from = network_interface.upstream_output_connector(&target_input, &[]).and_then(|out| out.node_id()).unwrap_or(layer.to_node());
let transform_reference = DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::transform::IDENTIFIER); let transform_reference = DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::transform::IDENTIFIER);
let gradient_type_reference = DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::gradient_type::IDENTIFIER); let gradient_type_reference = DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::gradient_type::IDENTIFIER);
let spread_method_reference = DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::spread_method::IDENTIFIER); let gradient_spread_reference = DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::gradient_spread::IDENTIFIER);
let mut transforms_downstream_to_upstream: Vec<DAffine2> = Vec::new(); let mut transforms_downstream_to_upstream: Vec<DAffine2> = Vec::new();
let mut gradient_type: Option<GradientType> = None; let mut gradient_type: Option<GradientType> = None;
let mut spread_method: Option<GradientSpreadMethod> = None; let mut gradient_spread: Option<GradientSpread> = None;
for node_id in network_interface for node_id in network_interface
.upstream_flow_back_from_nodes(vec![walk_from], &[], FlowType::HorizontalFlow) .upstream_flow_back_from_nodes(vec![walk_from], &[], FlowType::HorizontalFlow)
@@ -416,11 +416,11 @@ fn read_gradient_chain_state(layer: LayerNodeIdentifier, network_interface: &Nod
&& let Some(TaggedValue::GradientType(value)) = document_node.inputs.get(1).and_then(|input| input.as_value()) && let Some(TaggedValue::GradientType(value)) = document_node.inputs.get(1).and_then(|input| input.as_value())
{ {
gradient_type = Some(*value); gradient_type = Some(*value);
} else if reference == spread_method_reference } else if reference == gradient_spread_reference
&& spread_method.is_none() && gradient_spread.is_none()
&& let Some(TaggedValue::GradientSpreadMethod(value)) = document_node.inputs.get(1).and_then(|input| input.as_value()) && let Some(TaggedValue::GradientSpread(value)) = document_node.inputs.get(1).and_then(|input| input.as_value())
{ {
spread_method = Some(*value); gradient_spread = Some(*value);
} }
} }
@@ -430,7 +430,7 @@ fn read_gradient_chain_state(layer: LayerNodeIdentifier, network_interface: &Nod
GradientAppearance { GradientAppearance {
transform: composed_transform, transform: composed_transform,
gradient_type: gradient_type.unwrap_or_default(), gradient_type: gradient_type.unwrap_or_default(),
spread_method: spread_method.unwrap_or_default(), gradient_spread: gradient_spread.unwrap_or_default(),
} }
} }
@@ -766,7 +766,7 @@ impl SelectedGradient {
layer, layer,
gradient: self.gradient.clone(), gradient: self.gradient.clone(),
gradient_type: self.appearance.gradient_type, gradient_type: self.appearance.gradient_type,
spread_method: self.appearance.spread_method, gradient_spread: self.appearance.gradient_spread,
transform: self.appearance.transform, transform: self.appearance.transform,
}); });
} }
@@ -801,9 +801,9 @@ fn dispatch_gradient_chain_writes(layer: LayerNodeIdentifier, gradient: &Gradien
layer, layer,
gradient_type: appearance.gradient_type, gradient_type: appearance.gradient_type,
}); });
responses.add(GraphOperationMessage::GradientSpreadMethodSet { responses.add(GraphOperationMessage::GradientSpreadSet {
layer, layer,
spread_method: appearance.spread_method, gradient_spread: appearance.gradient_spread,
}); });
} }
@@ -1483,7 +1483,7 @@ impl Fsm for GradientToolFsmState {
GradientAppearance { GradientAppearance {
transform: DAffine2::IDENTITY, transform: DAffine2::IDENTITY,
gradient_type: tool_options.gradient_type, gradient_type: tool_options.gradient_type,
spread_method: tool_options.spread_method, gradient_spread: tool_options.gradient_spread,
}, },
GradientSource::Direct, GradientSource::Direct,
), ),
@@ -1836,7 +1836,7 @@ fn apply_gradient_update(
layer, layer,
gradient, gradient,
gradient_type: appearance.gradient_type, gradient_type: appearance.gradient_type,
spread_method: appearance.spread_method, gradient_spread: appearance.gradient_spread,
transform: appearance.transform, transform: appearance.transform,
}); });
} }
@@ -1860,7 +1860,7 @@ fn apply_gradient_update(
/// Set new gradient stops on every selected layer's gradient. Unlike `apply_gradient_update`, this doesn't open its own /// Set new gradient stops on every selected layer's gradient. Unlike `apply_gradient_update`, this doesn't open its own
/// transaction so it can be called repeatedly during a color picker drag and have all the changes coalesced into a /// transaction so it can be called repeatedly during a color picker drag and have all the changes coalesced into a
/// single undo entry by the surrounding 'on_commit' callback. /// single undo entry by the surrounding 'on_commit' callback.
fn apply_stops_update(data: &mut GradientToolData, context: &mut ToolActionMessageContext, responses: &mut VecDeque<Message>, new_gradient: Gradient, spread_method: GradientSpreadMethod) { fn apply_stops_update(data: &mut GradientToolData, context: &mut ToolActionMessageContext, responses: &mut VecDeque<Message>, new_gradient: Gradient, gradient_spread: GradientSpread) {
let selected_layers: Vec<_> = context let selected_layers: Vec<_> = context
.document .document
.network_interface .network_interface
@@ -1876,14 +1876,14 @@ fn apply_stops_update(data: &mut GradientToolData, context: &mut ToolActionMessa
if get_upstream_gradient_value_node_id(layer, &context.document.network_interface).is_some() { if get_upstream_gradient_value_node_id(layer, &context.document.network_interface).is_some() {
responses.add(GraphOperationMessage::GradientStopsSet { layer, stops: new_gradient.clone() }); responses.add(GraphOperationMessage::GradientStopsSet { layer, stops: new_gradient.clone() });
responses.add(GraphOperationMessage::GradientSpreadMethodSet { layer, spread_method }); responses.add(GraphOperationMessage::GradientSpreadSet { layer, gradient_spread });
updated_any_layer = true; updated_any_layer = true;
} else if let Some((_gradient, appearance, _source)) = resolve_gradient(layer, &context.document.network_interface) { } else if let Some((_gradient, appearance, _source)) = resolve_gradient(layer, &context.document.network_interface) {
responses.add(GraphOperationMessage::FillGradientSet { responses.add(GraphOperationMessage::FillGradientSet {
layer, layer,
gradient: new_gradient.clone(), gradient: new_gradient.clone(),
gradient_type: appearance.gradient_type, gradient_type: appearance.gradient_type,
spread_method, gradient_spread,
transform: appearance.transform, transform: appearance.transform,
}); });
updated_any_layer = true; updated_any_layer = true;
@@ -1892,7 +1892,7 @@ fn apply_stops_update(data: &mut GradientToolData, context: &mut ToolActionMessa
if let Some(selected_gradient) = &mut data.selected_gradient { if let Some(selected_gradient) = &mut data.selected_gradient {
selected_gradient.gradient = new_gradient.clone(); selected_gradient.gradient = new_gradient.clone();
selected_gradient.appearance.spread_method = spread_method; selected_gradient.appearance.gradient_spread = gradient_spread;
} }
// When no selected layer had a gradient to update, the user is editing the tool's default gradient instead. // When no selected layer had a gradient to update, the user is editing the tool's default gradient instead.
@@ -1989,14 +1989,14 @@ mod test_gradient {
use graph_craft::document::NodeInput; use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graphene_std::color::SRGBA8; use graphene_std::color::SRGBA8;
use graphene_std::vector::style::{GradientSpreadMethod, build_transform_with_y_preservation}; use graphene_std::vector::style::{GradientSpread, build_transform_with_y_preservation};
use graphene_std::vector::{Gradient, GradientRamp, GradientStop, fill}; use graphene_std::vector::{Gradient, GradientRamp, GradientStop, fill};
use super::gradient_space_transform; use super::gradient_space_transform;
struct ResolvedGradient { struct ResolvedGradient {
stops: Gradient, stops: Gradient,
spread_method: GradientSpreadMethod, gradient_spread: GradientSpread,
transform: DAffine2, transform: DAffine2,
} }
@@ -2004,7 +2004,7 @@ mod test_gradient {
fn new(stops: Gradient, appearance: super::GradientAppearance) -> Self { fn new(stops: Gradient, appearance: super::GradientAppearance) -> Self {
Self { Self {
stops, stops,
spread_method: appearance.spread_method, gradient_spread: appearance.gradient_spread,
transform: appearance.transform, transform: appearance.transform,
} }
} }
@@ -2032,8 +2032,8 @@ mod test_gradient {
let fill_node_id = get_fill_node_id_with_direct_fill_input(layer, &document.network_interface)?; let fill_node_id = get_fill_node_id_with_direct_fill_input(layer, &document.network_interface)?;
let fill_node = document.network_interface.document_network().nodes.get(&fill_node_id)?; let fill_node = document.network_interface.document_network().nodes.get(&fill_node_id)?;
let (stops, spread_method) = match fill_node.input(fill::FillInput)?.as_value()? { let (stops, gradient_spread) = match fill_node.input(fill::FillInput)?.as_value()? {
TaggedValue::GradientRamp(ramp) => (Gradient::from(ramp), ramp.spread_method), TaggedValue::GradientRamp(ramp) => (Gradient::from(ramp), ramp.gradient_spread),
_ => return None, _ => return None,
}; };
@@ -2045,7 +2045,7 @@ mod test_gradient {
let gradient = ResolvedGradient { let gradient = ResolvedGradient {
stops, stops,
spread_method, gradient_spread,
transform: local_transform, transform: local_transform,
}; };
@@ -2573,77 +2573,77 @@ mod test_gradient {
assert_eq!(editor.active_document().metadata().all_layers().count(), 0, "Expected the layer to be deleted after drawing a gradient"); assert_eq!(editor.active_document().metadata().all_layers().count(), 0, "Expected the layer to be deleted after drawing a gradient");
} }
/// Build the JS-boundary ramp the stops swatch's picker would send when choosing a new spread method. /// Build the JS-boundary ramp the stops swatch's picker would send when choosing a new gradient spread.
fn ramp_with_spread(stops: &Gradient, spread_method: GradientSpreadMethod) -> GradientRamp<SRGBA8> { fn ramp_with_spread(stops: &Gradient, gradient_spread: GradientSpread) -> GradientRamp<SRGBA8> {
GradientRamp::<SRGBA8>::from(&GradientRamp { GradientRamp::<SRGBA8>::from(&GradientRamp {
spread_method, gradient_spread,
..GradientRamp::from(stops) ..GradientRamp::from(stops)
}) })
} }
#[tokio::test] #[tokio::test]
async fn change_spread_method() { async fn change_gradient_spread() {
let mut editor = EditorTestUtils::create(); let mut editor = EditorTestUtils::create();
editor.new_document().await; editor.new_document().await;
editor.drag_tool(ToolType::Rectangle, 0., 0., 100., 100., ModifierKeys::empty()).await; editor.drag_tool(ToolType::Rectangle, 0., 0., 100., 100., ModifierKeys::empty()).await;
editor.drag_tool(ToolType::Gradient, 10., 10., 90., 90., ModifierKeys::empty()).await; editor.drag_tool(ToolType::Gradient, 10., 10., 90., 90., ModifierKeys::empty()).await;
// Verify default spread method is Pad // Verify default gradient spread is Pad
let (gradient, _) = get_gradient_from_fill(&mut editor).await; let (gradient, _) = get_gradient_from_fill(&mut editor).await;
assert_eq!(gradient.spread_method, GradientSpreadMethod::Pad); assert_eq!(gradient.gradient_spread, GradientSpread::Pad);
// Update spread method to Repeat // Update the gradient spread to Repeat
editor editor
.handle_message(GradientToolMessage::UpdateRamp { .handle_message(GradientToolMessage::UpdateRamp {
ramp: ramp_with_spread(&gradient.stops, GradientSpreadMethod::Repeat), ramp: ramp_with_spread(&gradient.stops, GradientSpread::Repeat),
}) })
.await; .await;
let (gradient, _) = get_gradient_from_fill(&mut editor).await; let (gradient, _) = get_gradient_from_fill(&mut editor).await;
assert_eq!(gradient.spread_method, GradientSpreadMethod::Repeat); assert_eq!(gradient.gradient_spread, GradientSpread::Repeat);
// Update spread method to Reflect // Update the gradient spread to Reflect
editor editor
.handle_message(GradientToolMessage::UpdateRamp { .handle_message(GradientToolMessage::UpdateRamp {
ramp: ramp_with_spread(&gradient.stops, GradientSpreadMethod::Reflect), ramp: ramp_with_spread(&gradient.stops, GradientSpread::Reflect),
}) })
.await; .await;
let (gradient, _) = get_gradient_from_fill(&mut editor).await; let (gradient, _) = get_gradient_from_fill(&mut editor).await;
assert_eq!(gradient.spread_method, GradientSpreadMethod::Reflect); assert_eq!(gradient.gradient_spread, GradientSpread::Reflect);
} }
#[tokio::test] #[tokio::test]
async fn change_spread_method_chain() { async fn change_gradient_spread_chain() {
let mut editor = EditorTestUtils::create(); let mut editor = EditorTestUtils::create();
editor.new_document().await; editor.new_document().await;
let layer = create_fill_gradient_chain_layer(&mut editor).await; let layer = create_fill_gradient_chain_layer(&mut editor).await;
editor.handle_message(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] }).await; editor.handle_message(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] }).await;
editor.select_tool(ToolType::Gradient).await; editor.select_tool(ToolType::Gradient).await;
// Verify default spread method is Pad // Verify default gradient spread is Pad
let (gradient, _) = get_gradient_from_chain(&mut editor).await; let (gradient, _) = get_gradient_from_chain(&mut editor).await;
assert_eq!(gradient.spread_method, GradientSpreadMethod::Pad); assert_eq!(gradient.gradient_spread, GradientSpread::Pad);
// Update spread method to Repeat // Update the gradient spread to Repeat
editor editor
.handle_message(GradientToolMessage::UpdateRamp { .handle_message(GradientToolMessage::UpdateRamp {
ramp: ramp_with_spread(&gradient.stops, GradientSpreadMethod::Repeat), ramp: ramp_with_spread(&gradient.stops, GradientSpread::Repeat),
}) })
.await; .await;
let (gradient, _) = get_gradient_from_chain(&mut editor).await; let (gradient, _) = get_gradient_from_chain(&mut editor).await;
assert_eq!(gradient.spread_method, GradientSpreadMethod::Repeat); assert_eq!(gradient.gradient_spread, GradientSpread::Repeat);
// Update spread method to Reflect // Update the gradient spread to Reflect
editor editor
.handle_message(GradientToolMessage::UpdateRamp { .handle_message(GradientToolMessage::UpdateRamp {
ramp: ramp_with_spread(&gradient.stops, GradientSpreadMethod::Reflect), ramp: ramp_with_spread(&gradient.stops, GradientSpread::Reflect),
}) })
.await; .await;
let (gradient, _) = get_gradient_from_chain(&mut editor).await; let (gradient, _) = get_gradient_from_chain(&mut editor).await;
assert_eq!(gradient.spread_method, GradientSpreadMethod::Reflect); assert_eq!(gradient.gradient_spread, GradientSpread::Reflect);
} }
#[tokio::test] #[tokio::test]
@@ -2795,7 +2795,7 @@ mod test_gradient {
// graph space rather than stranded at the origin. // graph space rather than stranded at the origin.
#[tokio::test] #[tokio::test]
async fn gradient_chain_node_on_fill_secondary_input_takes_feeder_slot() { async fn gradient_chain_node_on_fill_secondary_input_takes_feeder_slot() {
use graphene_std::vector::style::GradientSpreadMethod; use graphene_std::vector::style::GradientSpread;
let mut editor = EditorTestUtils::create(); let mut editor = EditorTestUtils::create();
editor.new_document().await; editor.new_document().await;
@@ -2852,17 +2852,17 @@ mod test_gradient {
.await; .await;
let feeder_position = editor.active_document_mut().network_interface.position(&gradient_value_id, &[]).expect("Gradient Value position"); let feeder_position = editor.active_document_mut().network_interface.position(&gradient_value_id, &[]).expect("Gradient Value position");
// Set the spread method through the tool, which splices a 'Spread Method' node onto the Fill's fill input wire. // Set the gradient spread through the tool, which splices a 'Gradient Spread' node onto the Fill's fill input wire.
editor.handle_message(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] }).await; editor.handle_message(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] }).await;
editor.select_tool(ToolType::Gradient).await; editor.select_tool(ToolType::Gradient).await;
let stops = get_gradient_stops(layer, &editor.active_document().network_interface).expect("the chain layer should resolve its gradient stops"); let stops = get_gradient_stops(layer, &editor.active_document().network_interface).expect("the chain layer should resolve its gradient stops");
editor editor
.handle_message(GradientToolMessage::UpdateRamp { .handle_message(GradientToolMessage::UpdateRamp {
ramp: ramp_with_spread(&stops, GradientSpreadMethod::Reflect), ramp: ramp_with_spread(&stops, GradientSpread::Reflect),
}) })
.await; .await;
let spread_reference = DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::spread_method::IDENTIFIER); let spread_reference = DefinitionIdentifier::ProtoNode(graphene_std::math_nodes::gradient_spread::IDENTIFIER);
let spread_node_id = { let spread_node_id = {
let network_interface = &editor.active_document().network_interface; let network_interface = &editor.active_document().network_interface;
network_interface network_interface
@@ -2871,10 +2871,10 @@ mod test_gradient {
.keys() .keys()
.copied() .copied()
.find(|node_id| network_interface.reference(node_id, &[]).as_ref() == Some(&spread_reference)) .find(|node_id| network_interface.reference(node_id, &[]).as_ref() == Some(&spread_reference))
.expect("Spread Method node should have been inserted") .expect("Gradient Spread node should have been inserted")
}; };
let spread_position = editor.active_document_mut().network_interface.position(&spread_node_id, &[]).expect("Spread Method position"); let spread_position = editor.active_document_mut().network_interface.position(&spread_node_id, &[]).expect("Gradient Spread position");
let feeder_position_after = editor.active_document_mut().network_interface.position(&gradient_value_id, &[]).expect("Gradient Value position after"); let feeder_position_after = editor.active_document_mut().network_interface.position(&gradient_value_id, &[]).expect("Gradient Value position after");
assert_eq!(spread_position, feeder_position, "the inserted node should occupy the feeder's former slot, not the graph origin"); assert_eq!(spread_position, feeder_position, "the inserted node should occupy the feeder's former slot, not the graph origin");
+12 -11
View File
@@ -168,11 +168,11 @@ macro_rules! tagged_value {
Self::BoxCorners(values) => Box::new(BoxCorners::from(values)), Self::BoxCorners(values) => Box::new(BoxCorners::from(values)),
Self::Color(color) => Box::new(List::<Color>::new_from_element(color)), Self::Color(color) => Box::new(List::<Color>::new_from_element(color)),
Self::GradientRamp(ramp) => { Self::GradientRamp(ramp) => {
// The ramp's spread method rides the served list as its attribute, as `Item<Gradient>::from` does on master. // The ramp's gradient spread rides the served list as its attribute, as `Item<Gradient>::from` does on master.
let spread_method = ramp.spread_method; let gradient_spread = ramp.gradient_spread;
let mut list = List::<Gradient>::new_from_element(Gradient::from(ramp)); let mut list = List::<Gradient>::new_from_element(Gradient::from(ramp));
if !spread_method.is_default() { if !gradient_spread.is_default() {
list.set_attribute(graphic_types::vector_types::ATTR_SPREAD_METHOD, 0, spread_method); list.set_attribute(graphic_types::vector_types::ATTR_GRADIENT_SPREAD, 0, gradient_spread);
} }
Box::new(list) Box::new(list)
} }
@@ -222,11 +222,11 @@ macro_rules! tagged_value {
Self::BoxCorners(values) => Arc::new(BoxCorners::from(values)), Self::BoxCorners(values) => Arc::new(BoxCorners::from(values)),
Self::Color(color) => Arc::new(List::<Color>::new_from_element(color)), Self::Color(color) => Arc::new(List::<Color>::new_from_element(color)),
Self::GradientRamp(ramp) => { Self::GradientRamp(ramp) => {
// The ramp's spread method rides the served list as its attribute, as `Item<Gradient>::from` does on master. // The ramp's gradient spread rides the served list as its attribute, as `Item<Gradient>::from` does on master.
let spread_method = ramp.spread_method; let gradient_spread = ramp.gradient_spread;
let mut list = List::<Gradient>::new_from_element(Gradient::from(ramp)); let mut list = List::<Gradient>::new_from_element(Gradient::from(ramp));
if !spread_method.is_default() { if !gradient_spread.is_default() {
list.set_attribute(graphic_types::vector_types::ATTR_SPREAD_METHOD, 0, spread_method); list.set_attribute(graphic_types::vector_types::ATTR_GRADIENT_SPREAD, 0, gradient_spread);
} }
Arc::new(list) Arc::new(list)
} }
@@ -627,7 +627,8 @@ tagged_value! {
StrokeAlign(vector::style::StrokeAlign), StrokeAlign(vector::style::StrokeAlign),
PaintOrder(vector::style::PaintOrder), PaintOrder(vector::style::PaintOrder),
GradientType(vector::style::GradientType), GradientType(vector::style::GradientType),
GradientSpreadMethod(vector::style::GradientSpreadMethod), #[serde(alias = "GradientSpreadMethod")] // TODO: Eventually remove this document upgrade code
GradientSpread(vector::style::GradientSpread),
ReferencePoint(vector::ReferencePoint), ReferencePoint(vector::ReferencePoint),
CentroidType(vector::misc::CentroidType), CentroidType(vector::misc::CentroidType),
BooleanOperation(vector::misc::BooleanOperation), BooleanOperation(vector::misc::BooleanOperation),
@@ -1102,7 +1103,7 @@ mod paint_default_parsing {
#[cfg(test)] #[cfg(test)]
mod gradient_shape_migration { mod gradient_shape_migration {
use graphic_types::vector_types::GradientSpreadMethod; use graphic_types::vector_types::GradientSpread;
use super::*; use super::*;
@@ -1123,7 +1124,7 @@ mod gradient_shape_migration {
let mut gradient = Gradient::from(vec![Color::BLACK, Color::WHITE]); let mut gradient = Gradient::from(vec![Color::BLACK, Color::WHITE]);
gradient.set_positions(&[0.2, 0.9]); gradient.set_positions(&[0.2, 0.9]);
let value = TaggedValue::GradientRamp(GradientRamp { let value = TaggedValue::GradientRamp(GradientRamp {
spread_method: GradientSpreadMethod::Reflect, gradient_spread: GradientSpread::Reflect,
..GradientRamp::from(gradient) ..GradientRamp::from(gradient)
}); });
+1 -1
View File
@@ -1164,7 +1164,7 @@ impl<T: CacheHash> CacheHash for List<T> {
self.element.cache_hash(state); self.element.cache_hash(state);
// Hash every attribute attribute (key + values) rather than just the well-known ones, so changes to user-defined keys // Hash every attribute attribute (key + values) rather than just the well-known ones, so changes to user-defined keys
// (e.g., gradient_type, spread_method) invalidate downstream graph caches as expected // (e.g., gradient_type, gradient_spread) invalidate downstream graph caches as expected
for (key, attribute) in &self.attributes.attributes { for (key, attribute) in &self.attributes.attributes {
std::hash::Hash::hash(key.as_str(), state); std::hash::Hash::hash(key.as_str(), state);
attribute.cache_hash_dyn(state); attribute.cache_hash_dyn(state);
@@ -36,7 +36,7 @@ pub mod migrations {
pub start: DVec2, pub start: DVec2,
pub end: DVec2, pub end: DVec2,
#[serde(default)] #[serde(default)]
pub spread_method: vector::style::GradientSpreadMethod, pub spread_method: vector::style::GradientSpread,
#[serde(default)] #[serde(default)]
pub absolute: bool, pub absolute: bool,
#[serde(default)] #[serde(default)]
@@ -8,11 +8,11 @@ use core_types::uuid::generate_uuid;
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use graphic_types::Graphic; use graphic_types::Graphic;
use graphic_types::vector_types::gradient::GradientType; use graphic_types::vector_types::gradient::GradientType;
use graphic_types::vector_types::markers::{GradientType as GradientTypeAttr, SpreadMethod}; use graphic_types::vector_types::markers::{GradientSpread as GradientSpreadAttr, GradientType as GradientTypeAttr};
use graphic_types::vector_types::vector::style::{PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin}; use graphic_types::vector_types::vector::style::{PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
use std::fmt::Write; use std::fmt::Write;
use vector_types::Gradient; use vector_types::Gradient;
use vector_types::gradient::GradientSpreadMethod; use vector_types::gradient::GradientSpread;
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum PaintTarget { pub enum PaintTarget {
@@ -110,7 +110,7 @@ pub fn render_gradient_paint<S: core_types::lane::LaneSource<Element = Gradient>
let Some(stops) = source.element(0) else { return 0 }; let Some(stops) = source.element(0) else { return 0 };
let gradient_type: GradientType = source.attr::<GradientTypeAttr>(0); let gradient_type: GradientType = source.attr::<GradientTypeAttr>(0);
let local_gradient_transform: DAffine2 = source.attr::<Transform>(0); let local_gradient_transform: DAffine2 = source.attr::<Transform>(0);
let spread_method: GradientSpreadMethod = source.attr::<SpreadMethod>(0); let gradient_spread: GradientSpread = source.attr::<GradientSpreadAttr>(0);
for (position, color, original_midpoint) in stops.interpolated_samples() { for (position, color, original_midpoint) in stops.interpolated_samples() {
stop.push_str("<stop"); stop.push_str("<stop");
@@ -149,10 +149,10 @@ pub fn render_gradient_paint<S: core_types::lane::LaneSource<Element = Gradient>
format!(r#" gradientTransform="{gradient_transform}""#) format!(r#" gradientTransform="{gradient_transform}""#)
}; };
let spread_method = if spread_method == GradientSpreadMethod::Pad { let gradient_spread = if gradient_spread == GradientSpread::Pad {
String::new() String::new()
} else { } else {
format!(r#" spreadMethod="{}""#, spread_method.svg_name()) format!(r#" spreadMethod="{}""#, gradient_spread.svg_name())
}; };
let gradient_id = generate_uuid(); let gradient_id = generate_uuid();
@@ -161,14 +161,14 @@ pub fn render_gradient_paint<S: core_types::lane::LaneSource<Element = Gradient>
GradientType::Linear => { GradientType::Linear => {
let _ = write!( let _ = write!(
svg_defs, svg_defs,
r#"<linearGradient id="{}" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="1" y2="0"{spread_method}{gradient_transform}>{}</linearGradient>"#, r#"<linearGradient id="{}" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="1" y2="0"{gradient_spread}{gradient_transform}>{}</linearGradient>"#,
gradient_id, stop gradient_id, stop
); );
} }
GradientType::Radial => { GradientType::Radial => {
let _ = write!( let _ = write!(
svg_defs, svg_defs,
r#"<radialGradient id="{}" gradientUnits="userSpaceOnUse" cx="0" cy="0" r="1"{spread_method}{gradient_transform}>{}</radialGradient>"#, r#"<radialGradient id="{}" gradientUnits="userSpaceOnUse" cx="0" cy="0" r="1"{gradient_spread}{gradient_transform}>{}</radialGradient>"#,
gradient_id, stop gradient_id, stop
); );
} }
+15 -15
View File
@@ -27,7 +27,7 @@ use graphic_types::graphic::{PaintColumns, PaintOverlay, PaintReach, has_paint,
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke}; use graphic_types::markers::{EditorMergedLayers, Fill, Stroke};
use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster, Texture}; use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster, Texture};
use graphic_types::vector_types::gradient::{Gradient, GradientType}; use graphic_types::vector_types::gradient::{Gradient, GradientType};
use graphic_types::vector_types::markers::{GradientType as GradientTypeAttr, SpreadMethod}; use graphic_types::vector_types::markers::{GradientSpread as GradientSpreadAttr, GradientType as GradientTypeAttr};
use graphic_types::vector_types::subpath::Subpath; use graphic_types::vector_types::subpath::Subpath;
use graphic_types::vector_types::vector::click_target::{ClickTarget, FreePoint}; use graphic_types::vector_types::vector::click_target::{ClickTarget, FreePoint};
use graphic_types::vector_types::vector::style::{PaintOrder, RenderMode, StrokeAlign, StrokeCap, StrokeJoin}; use graphic_types::vector_types::vector::style::{PaintOrder, RenderMode, StrokeAlign, StrokeCap, StrokeJoin};
@@ -44,7 +44,7 @@ use std::hash::Hash;
use std::ops::Deref; use std::ops::Deref;
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
use text_nodes::markers::{Font, TextAlign}; use text_nodes::markers::{Font, TextAlign};
use vector_types::gradient::GradientSpreadMethod; use vector_types::gradient::GradientSpread;
use vector_types::markers::EditorClickTarget; use vector_types::markers::EditorClickTarget;
use vello::*; use vello::*;
@@ -430,7 +430,7 @@ fn create_peniko_gradient_brush<S: LaneSource<Element = Gradient>>(gradient_list
let gradient_type: GradientType = gradient_list.attr::<GradientTypeAttr>(0); let gradient_type: GradientType = gradient_list.attr::<GradientTypeAttr>(0);
let gradient_transform: DAffine2 = gradient_list.attr::<Transform>(0); let gradient_transform: DAffine2 = gradient_list.attr::<Transform>(0);
let spread_method: GradientSpreadMethod = gradient_list.attr::<SpreadMethod>(0); let gradient_spread: GradientSpread = gradient_list.attr::<GradientSpreadAttr>(0);
let peniko_stops = peniko_color_stops(stops); let peniko_stops = peniko_color_stops(stops);
@@ -452,10 +452,10 @@ fn create_peniko_gradient_brush<S: LaneSource<Element = Gradient>>(gradient_list
} }
.into(), .into(),
}, },
extend: match spread_method { extend: match gradient_spread {
GradientSpreadMethod::Pad => peniko::Extend::Pad, GradientSpread::Pad => peniko::Extend::Pad,
GradientSpreadMethod::Reflect => peniko::Extend::Reflect, GradientSpread::Reflect => peniko::Extend::Reflect,
GradientSpreadMethod::Repeat => peniko::Extend::Repeat, GradientSpread::Repeat => peniko::Extend::Repeat,
}, },
stops: peniko_stops, stops: peniko_stops,
interpolation_alpha_space: peniko::InterpolationAlphaSpace::Premultiplied, interpolation_alpha_space: peniko::InterpolationAlphaSpace::Premultiplied,
@@ -2327,7 +2327,7 @@ fn render_gradient_svg<S: LaneSource<Element = Gradient>>(source: &S, render: &m
let blend_mode: BlendMode = source.attr::<BlendModeAttr>(index); let blend_mode: BlendMode = source.attr::<BlendModeAttr>(index);
let opacity_attr: f64 = source.attr::<Opacity>(index); let opacity_attr: f64 = source.attr::<Opacity>(index);
let opacity_fill_attr: f64 = source.attr::<OpacityFill>(index); let opacity_fill_attr: f64 = source.attr::<OpacityFill>(index);
let spread_method: GradientSpreadMethod = source.attr::<SpreadMethod>(index); let gradient_spread: GradientSpread = source.attr::<GradientSpreadAttr>(index);
let gradient_type: GradientType = source.attr::<GradientTypeAttr>(index); let gradient_type: GradientType = source.attr::<GradientTypeAttr>(index);
let tag = if thumbnail_rect.is_some() { "rect" } else { "polyline" }; let tag = if thumbnail_rect.is_some() { "rect" } else { "polyline" };
render.leaf_tag(tag, |attributes| { render.leaf_tag(tag, |attributes| {
@@ -2366,10 +2366,10 @@ fn render_gradient_svg<S: LaneSource<Element = Gradient>>(source: &S, render: &m
}; };
let gradient_id = generate_uuid(); let gradient_id = generate_uuid();
let spread_method_attribute = if spread_method == GradientSpreadMethod::Pad { let spread_method_attribute = if gradient_spread == GradientSpread::Pad {
String::new() String::new()
} else { } else {
format!(r#" spreadMethod="{}""#, spread_method.svg_name()) format!(r#" spreadMethod="{}""#, gradient_spread.svg_name())
}; };
// The unit gradient line is the +X unit vector in local space, before the item's transform is applied // The unit gradient line is the +X unit vector in local space, before the item's transform is applied
@@ -2411,7 +2411,7 @@ fn render_gradient_vello<S: LaneSource<Element = Gradient>>(source: &S, scene: &
for index in 0..source.lane_count() { for index in 0..source.lane_count() {
let Some(gradient) = source.element(index) else { continue }; let Some(gradient) = source.element(index) else { continue };
let spread_method: GradientSpreadMethod = source.attr::<SpreadMethod>(index); let gradient_spread: GradientSpread = source.attr::<GradientSpreadAttr>(index);
let gradient_type: GradientType = source.attr::<GradientTypeAttr>(index); let gradient_type: GradientType = source.attr::<GradientTypeAttr>(index);
let transform: DAffine2 = source.attr::<Transform>(index); let transform: DAffine2 = source.attr::<Transform>(index);
let blend_mode_attr: BlendMode = source.attr::<BlendModeAttr>(index); let blend_mode_attr: BlendMode = source.attr::<BlendModeAttr>(index);
@@ -2424,10 +2424,10 @@ fn render_gradient_vello<S: LaneSource<Element = Gradient>>(source: &S, scene: &
let stops = peniko_color_stops(gradient); let stops = peniko_color_stops(gradient);
let extend = match spread_method { let extend = match gradient_spread {
GradientSpreadMethod::Pad => peniko::Extend::Pad, GradientSpread::Pad => peniko::Extend::Pad,
GradientSpreadMethod::Reflect => peniko::Extend::Reflect, GradientSpread::Reflect => peniko::Extend::Reflect,
GradientSpreadMethod::Repeat => peniko::Extend::Repeat, GradientSpread::Repeat => peniko::Extend::Repeat,
}; };
// The unit gradient line is the +X unit vector in local space, before the item's transform is applied. // The unit gradient line is the +X unit vector in local space, before the item's transform is applied.
@@ -1,4 +1,4 @@
use crate::ATTR_SPREAD_METHOD; use crate::markers::ATTR_GRADIENT_SPREAD;
use core_types::Color; use core_types::Color;
use core_types::color::SRGBA8; use core_types::color::SRGBA8;
use core_types::list::{ATTR_MIDPOINT, ATTR_POSITION, Item, List}; use core_types::list::{ATTR_MIDPOINT, ATTR_POSITION, Item, List};
@@ -103,9 +103,9 @@ impl GradientStops<SRGBA8> {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GradientRamp<C = Color> { pub struct GradientRamp<C = Color> {
pub stops: GradientStops<C>, pub stops: GradientStops<C>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "GradientSpreadMethod::is_default"))] #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "GradientSpread::is_default"))]
#[cfg_attr(feature = "wasm", tsify(optional))] #[cfg_attr(feature = "wasm", tsify(optional))]
pub spread_method: GradientSpreadMethod, pub gradient_spread: GradientSpread,
} }
unsafe impl<C: dyn_any::StaticTypeSized> dyn_any::StaticType for GradientRamp<C> { unsafe impl<C: dyn_any::StaticTypeSized> dyn_any::StaticType for GradientRamp<C> {
@@ -116,7 +116,7 @@ impl<C> From<GradientStops<C>> for GradientRamp<C> {
fn from(stops: GradientStops<C>) -> Self { fn from(stops: GradientStops<C>) -> Self {
Self { Self {
stops, stops,
spread_method: Default::default(), gradient_spread: Default::default(),
} }
} }
} }
@@ -125,7 +125,7 @@ impl From<&Gradient> for GradientRamp {
fn from(gradient: &Gradient) -> Self { fn from(gradient: &Gradient) -> Self {
Self { Self {
stops: gradient.into(), stops: gradient.into(),
spread_method: Default::default(), gradient_spread: Default::default(),
} }
} }
} }
@@ -153,8 +153,8 @@ impl From<&GradientRamp> for Gradient {
impl From<GradientRamp> for Item<Gradient> { impl From<GradientRamp> for Item<Gradient> {
fn from(ramp: GradientRamp) -> Self { fn from(ramp: GradientRamp) -> Self {
let mut item = Item::new_from_element(Gradient::from(ramp.stops)); let mut item = Item::new_from_element(Gradient::from(ramp.stops));
if !ramp.spread_method.is_default() { if !ramp.gradient_spread.is_default() {
item.set_attribute(ATTR_SPREAD_METHOD, ramp.spread_method); item.set_attribute(ATTR_GRADIENT_SPREAD, ramp.gradient_spread);
} }
item item
} }
@@ -164,7 +164,7 @@ impl From<&Item<Gradient>> for GradientRamp {
fn from(item: &Item<Gradient>) -> Self { fn from(item: &Item<Gradient>) -> Self {
Self { Self {
stops: item.element().into(), stops: item.element().into(),
spread_method: item.attribute_cloned_or_default(ATTR_SPREAD_METHOD), gradient_spread: item.attribute_cloned_or_default(ATTR_GRADIENT_SPREAD),
} }
} }
} }
@@ -190,7 +190,7 @@ impl From<&GradientRamp> for GradientRamp<SRGBA8> {
fn from(ramp: &GradientRamp) -> Self { fn from(ramp: &GradientRamp) -> Self {
Self { Self {
stops: ramp.into(), stops: ramp.into(),
spread_method: ramp.spread_method, gradient_spread: ramp.gradient_spread,
} }
} }
} }
@@ -199,7 +199,7 @@ impl From<&Gradient> for GradientRamp<SRGBA8> {
fn from(gradient: &Gradient) -> Self { fn from(gradient: &Gradient) -> Self {
Self { Self {
stops: gradient.into(), stops: gradient.into(),
spread_method: Default::default(), gradient_spread: Default::default(),
} }
} }
} }
@@ -207,7 +207,7 @@ impl From<&Gradient> for GradientRamp<SRGBA8> {
impl From<&GradientRamp<SRGBA8>> for GradientRamp { impl From<&GradientRamp<SRGBA8>> for GradientRamp {
fn from(ramp: &GradientRamp<SRGBA8>) -> Self { fn from(ramp: &GradientRamp<SRGBA8>) -> Self {
Self { Self {
spread_method: ramp.spread_method, gradient_spread: ramp.gradient_spread,
..Self::from(&ramp.stops) ..Self::from(&ramp.stops)
} }
} }
@@ -601,12 +601,12 @@ impl Gradient {
stops stops
} }
/// Samples the gradient's color at `t`. Given a `t` outside the 0 to 1 range, the `spread_method` determines how the gradient extends. /// Samples the gradient's color at `t`. Given a `t` outside the 0 to 1 range, the `gradient_spread` determines how the gradient extends.
pub fn evaluate(&self, t: f64, spread_method: GradientSpreadMethod) -> Color { pub fn evaluate(&self, t: f64, gradient_spread: GradientSpread) -> Color {
let t = match spread_method { let t = match gradient_spread {
GradientSpreadMethod::Pad => t.clamp(0., 1.), GradientSpread::Pad => t.clamp(0., 1.),
GradientSpreadMethod::Repeat => t.rem_euclid(1.), GradientSpread::Repeat => t.rem_euclid(1.),
GradientSpreadMethod::Reflect => { GradientSpread::Reflect => {
let cycle = t.rem_euclid(2.); let cycle = t.rem_euclid(2.);
if cycle > 1. { 2. - cycle } else { cycle } if cycle > 1. { 2. - cycle } else { cycle }
} }
@@ -785,7 +785,7 @@ impl Gradient {
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, graphene_hash::CacheHash, DynAny, node_macro::ChoiceType)] #[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, graphene_hash::CacheHash, DynAny, node_macro::ChoiceType)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[widget(Radio)] #[widget(Radio)]
pub enum GradientSpreadMethod { pub enum GradientSpread {
/// Extends the end colors outward. /// Extends the end colors outward.
#[default] #[default]
Pad, Pad,
@@ -796,12 +796,12 @@ pub enum GradientSpreadMethod {
// TODO: Add a "Clear" variant that returns transparent black outside the gradient's range // TODO: Add a "Clear" variant that returns transparent black outside the gradient's range
} }
impl GradientSpreadMethod { impl GradientSpread {
pub fn svg_name(&self) -> &'static str { pub fn svg_name(&self) -> &'static str {
match self { match self {
GradientSpreadMethod::Pad => "pad", GradientSpread::Pad => "pad",
GradientSpreadMethod::Reflect => "reflect", GradientSpread::Reflect => "reflect",
GradientSpreadMethod::Repeat => "repeat", GradientSpread::Repeat => "repeat",
} }
} }
@@ -906,39 +906,39 @@ mod tests {
} }
#[test] #[test]
fn spread_method_serializes_only_when_not_default() { fn gradient_spread_serializes_only_when_not_default() {
let default_spread = GradientRamp::from(Gradient::from(vec![Color::BLACK, Color::WHITE])); let default_spread = GradientRamp::from(Gradient::from(vec![Color::BLACK, Color::WHITE]));
let json = serde_json::to_string(&default_spread).unwrap(); let json = serde_json::to_string(&default_spread).unwrap();
assert!(!json.contains("spread_method"), "the default Pad spread method must not serialize: {json}"); assert!(!json.contains("gradient_spread"), "the default Pad gradient spread must not serialize: {json}");
assert_eq!(serde_json::from_str::<GradientRamp>(&json).unwrap(), default_spread); assert_eq!(serde_json::from_str::<GradientRamp>(&json).unwrap(), default_spread);
let repeating = GradientRamp { let repeating = GradientRamp {
spread_method: GradientSpreadMethod::Repeat, gradient_spread: GradientSpread::Repeat,
..default_spread.clone() ..default_spread.clone()
}; };
let json = serde_json::to_string(&repeating).unwrap(); let json = serde_json::to_string(&repeating).unwrap();
assert!(json.contains(r#""spread_method":"Repeat""#), "a non-default spread method must serialize: {json}"); assert!(json.contains(r#""gradient_spread":"Repeat""#), "a non-default gradient spread must serialize: {json}");
assert_eq!(serde_json::from_str::<GradientRamp>(&json).unwrap(), repeating); assert_eq!(serde_json::from_str::<GradientRamp>(&json).unwrap(), repeating);
} }
#[test] #[test]
fn spread_method_round_trips_through_the_item_attribute() { fn gradient_spread_round_trips_through_the_item_attribute() {
let ramp = GradientRamp { let ramp = GradientRamp {
spread_method: GradientSpreadMethod::Repeat, gradient_spread: GradientSpread::Repeat,
..GradientRamp::from(Gradient::from(vec![Color::BLACK, Color::WHITE])) ..GradientRamp::from(Gradient::from(vec![Color::BLACK, Color::WHITE]))
}; };
let item = Item::<Gradient>::from(ramp.clone()); let item = Item::<Gradient>::from(ramp.clone());
assert_eq!( assert_eq!(
item.attribute_cloned_or_default::<GradientSpreadMethod>(ATTR_SPREAD_METHOD), item.attribute_cloned_or_default::<GradientSpread>(ATTR_GRADIENT_SPREAD),
GradientSpreadMethod::Repeat, GradientSpread::Repeat,
"the runtime item should carry the spread method as its attribute" "the runtime item should carry the gradient spread as its attribute"
); );
assert_eq!(GradientRamp::from(&item), ramp); assert_eq!(GradientRamp::from(&item), ramp);
let padded = Item::<Gradient>::from(GradientRamp::from(Gradient::from(vec![Color::BLACK, Color::WHITE]))); let padded = Item::<Gradient>::from(GradientRamp::from(Gradient::from(vec![Color::BLACK, Color::WHITE])));
assert!( assert!(
padded.attribute::<GradientSpreadMethod>(ATTR_SPREAD_METHOD).is_none(), padded.attribute::<GradientSpread>(ATTR_GRADIENT_SPREAD).is_none(),
"the default Pad must stay absent rather than materialize" "the default Pad must stay absent rather than materialize"
); );
} }
+2 -2
View File
@@ -9,8 +9,8 @@ pub mod vector;
// Re-export commonly used types at the crate root // Re-export commonly used types at the crate root
pub use core_types as gcore; pub use core_types as gcore;
pub use gradient::{Gradient, GradientRamp, GradientSpreadMethod, GradientStop, GradientType}; pub use gradient::{Gradient, GradientRamp, GradientSpread, GradientStop, GradientType};
pub use markers::{ATTR_EDITOR_CLICK_TARGET, ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD}; pub use markers::{ATTR_EDITOR_CLICK_TARGET, ATTR_GRADIENT_SPREAD, ATTR_GRADIENT_TYPE};
pub use math::{QuadExt, RectExt}; pub use math::{QuadExt, RectExt};
pub use subpath::Subpath; pub use subpath::Subpath;
pub use vector::Vector; pub use vector::Vector;
@@ -5,7 +5,7 @@ use core_types::attribute::Attribute;
core_types::attribute! { core_types::attribute! {
/// Gradient's spread behavior past its endpoints (`Pad`, `Reflect`, or `Repeat`). /// Gradient's spread behavior past its endpoints (`Pad`, `Reflect`, or `Repeat`).
pub SpreadMethod("spread_method"): crate::gradient::GradientSpreadMethod; pub GradientSpread("gradient_spread"): crate::gradient::GradientSpread;
/// Gradient's shape (`Linear` or `Radial`). /// Gradient's shape (`Linear` or `Radial`).
pub GradientType("gradient_type"): crate::gradient::GradientType; pub GradientType("gradient_type"): crate::gradient::GradientType;
/// Optional `Vector` that overrides the item's own geometry for click-target generation. /// Optional `Vector` that overrides the item's own geometry for click-target generation.
@@ -18,11 +18,11 @@ core_types::attribute! {
// The value types a name-generic attribute can name here, so a compile-time // The value types a name-generic attribute can name here, so a compile-time
// named write or read reaches this crate's enums like any other plain value. // named write or read reaches this crate's enums like any other plain value.
core_types::named_value! { core_types::named_value! {
for crate::gradient::GradientSpreadMethod; for crate::gradient::GradientSpread;
for crate::gradient::GradientType; for crate::gradient::GradientType;
} }
pub const ATTR_SPREAD_METHOD: &str = SpreadMethod::NAME; pub const ATTR_GRADIENT_SPREAD: &str = GradientSpread::NAME;
pub const ATTR_GRADIENT_TYPE: &str = GradientType::NAME; pub const ATTR_GRADIENT_TYPE: &str = GradientType::NAME;
pub const ATTR_EDITOR_CLICK_TARGET: &str = EditorClickTarget::NAME; pub const ATTR_EDITOR_CLICK_TARGET: &str = EditorClickTarget::NAME;
@@ -35,7 +35,7 @@ mod tests {
#[test] #[test]
fn the_census_carries_this_crates_names() { fn the_census_carries_this_crates_names() {
assert_eq!(info("gradient_type").unwrap().value_type, TypeId::of::<crate::gradient::GradientType>()); assert_eq!(info("gradient_type").unwrap().value_type, TypeId::of::<crate::gradient::GradientType>());
assert_eq!(info("spread_method").unwrap().value_type, TypeId::of::<crate::gradient::GradientSpreadMethod>()); assert_eq!(info("gradient_spread").unwrap().value_type, TypeId::of::<crate::gradient::GradientSpread>());
assert_eq!(info("editor:click_target").unwrap().value_type, TypeId::of::<Option<&'static crate::Vector>>()); assert_eq!(info("editor:click_target").unwrap().value_type, TypeId::of::<Option<&'static crate::Vector>>());
} }
+3 -3
View File
@@ -14,7 +14,7 @@ use graphic_types::graphic::{Graphic, GraphicLevel, RowStep, TryFromGraphic, wal
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke as StrokeAttr}; use graphic_types::markers::{EditorMergedLayers, Fill, Stroke as StrokeAttr};
use graphic_types::{ATTR_FILL, ATTR_STROKE, Vector}; use graphic_types::{ATTR_FILL, ATTR_STROKE, Vector};
use raster_types::{CPU, GPU, Raster}; use raster_types::{CPU, GPU, Raster};
use vector_types::gradient::{GradientSpreadMethod, GradientType as GradientTypeValue}; use vector_types::gradient::{GradientSpread, GradientType as GradientTypeValue};
use vector_types::{Gradient, ReferencePoint}; use vector_types::{Gradient, ReferencePoint};
fn arena_exhausted() -> Interrupt { fn arena_exhausted() -> Interrupt {
@@ -255,8 +255,8 @@ attribute_reads! {
read_blend_mode_attribute: core_types::blending::BlendMode => core_types::blending::BlendMode; read_blend_mode_attribute: core_types::blending::BlendMode => core_types::blending::BlendMode;
/// Reads a named gradient-shape attribute, such as `gradient_type`. /// Reads a named gradient-shape attribute, such as `gradient_type`.
read_gradient_type_attribute: GradientTypeValue => GradientTypeValue; read_gradient_type_attribute: GradientTypeValue => GradientTypeValue;
/// Reads a named gradient-spread attribute, such as `spread_method`. /// Reads a named gradient-spread attribute, such as `gradient_spread`.
read_spread_method_attribute: GradientSpreadMethod => GradientSpreadMethod; read_gradient_spread_attribute: GradientSpread => GradientSpread;
} }
/// Nests the input graphical content in a wrapper graphic. This essentially "groups" the input. /// Nests the input graphical content in a wrapper graphic. This essentially "groups" the input.
+5 -5
View File
@@ -11,7 +11,7 @@ use math_parser::value::{Number, Value};
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use std::ops::{Add, Mul, Rem, Sub}; use std::ops::{Add, Mul, Rem, Sub};
use vector_types::Gradient; use vector_types::Gradient;
use vector_types::markers::{GradientType as GradientTypeAttr, SpreadMethod as SpreadMethodAttr}; use vector_types::markers::{GradientSpread as GradientSpreadAttr, GradientType as GradientTypeAttr};
/// The struct that stores the context for the maths parser. /// The struct that stores the context for the maths parser.
/// This is currently just limited to supplying `a` and `b` until we add better node graph support and UI for variadic inputs. /// This is currently just limited to supplying `a` and `b` until we add better node graph support and UI for variadic inputs.
@@ -1210,8 +1210,8 @@ fn gradient_type(_: impl Ctx, gradient: Gradient, gradient_type: vector_types::G
/// Sets how each gradient in the input list extends past its endpoints: Pad, Reflect, or Repeat. /// Sets how each gradient in the input list extends past its endpoints: Pad, Reflect, or Repeat.
#[node_macro::node(category("Gradient"))] #[node_macro::node(category("Gradient"))]
fn spread_method(_: impl Ctx, gradient: Gradient, spread_method: vector_types::GradientSpreadMethod) -> (Gradient, Attr<SpreadMethodAttr>) { fn gradient_spread(_: impl Ctx, gradient: Gradient, gradient_spread: vector_types::GradientSpread) -> (Gradient, Attr<GradientSpreadAttr>) {
(gradient, Attr(spread_method)) (gradient, Attr(gradient_spread))
} }
/// Sets the position of each of a gradient's stops, a factor from 0 to 1 along the gradient. /// Sets the position of each of a gradient's stops, a factor from 0 to 1 along the gradient.
@@ -1236,7 +1236,7 @@ fn gradient_midpoints(_: impl Ctx, mut gradient: Gradient, midpoints: IList<f64>
gradient gradient
} }
/// Evaluates the color at the specified position along the gradient, given a position from 0 (left) to 1 (right). Positions beyond that range follow the gradient's `spread_method` attribute: Pad (default), Reflect, or Repeat. /// Evaluates the color at the specified position along the gradient, given a position from 0 (left) to 1 (right). Positions beyond that range follow the gradient's `gradient_spread` attribute: Pad (default), Reflect, or Repeat.
#[node_macro::node(category("Color"))] #[node_macro::node(category("Color"))]
fn sample_gradient( fn sample_gradient(
ctx: impl Ctx + ExtractIndex + InjectIndex + Copy, ctx: impl Ctx + ExtractIndex + InjectIndex + Copy,
@@ -1249,7 +1249,7 @@ fn sample_gradient(
return Err(GraphError::past_end().into()); return Err(GraphError::past_end().into());
} }
let spread_method = gradient.lane(0).attr::<SpreadMethodAttr>(); let spread_method = gradient.lane(0).attr::<GradientSpreadAttr>();
Ok(gradient.element_ref(0).evaluate(position, spread_method)) Ok(gradient.element_ref(0).evaluate(position, spread_method))
} }
+4 -4
View File
@@ -8,11 +8,11 @@ use graphic_types::graphic::{GraphicLevel, PaintColumns, PaintReach, bake_paint_
use graphic_types::markers::{EditorMergedLayers, Fill, Stroke}; use graphic_types::markers::{EditorMergedLayers, Fill, Stroke};
use graphic_types::raster_types::{CPU, GPU, Raster}; use graphic_types::raster_types::{CPU, GPU, Raster};
use graphic_types::vector_types::Gradient; use graphic_types::vector_types::Gradient;
use graphic_types::vector_types::gradient::{GradientSpreadMethod, GradientType}; use graphic_types::vector_types::gradient::{GradientSpread, GradientType};
use graphic_types::vector_types::subpath::{ManipulatorGroup, Subpath}; use graphic_types::vector_types::subpath::{ManipulatorGroup, Subpath};
use graphic_types::vector_types::vector::PointId; use graphic_types::vector_types::vector::PointId;
use graphic_types::vector_types::vector::algorithms::merge_by_distance::MergeByDistanceExt; use graphic_types::vector_types::vector::algorithms::merge_by_distance::MergeByDistanceExt;
use graphic_types::vector_types::{ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD}; use graphic_types::vector_types::{ATTR_GRADIENT_SPREAD, ATTR_GRADIENT_TYPE};
use graphic_types::{ATTR_FILL, ATTR_STROKE, Graphic, IntoGraphicList, Vector}; use graphic_types::{ATTR_FILL, ATTR_STROKE, Graphic, IntoGraphicList, Vector};
use linesweeper::topology::Topology; use linesweeper::topology::Topology;
use linesweeper::{BinaryOp, FillRule, binary_op}; use linesweeper::{BinaryOp, FillRule, binary_op};
@@ -291,8 +291,8 @@ fn gradient_paint_row(stops: Gradient, mut attributes: core_types::list::ItemAtt
if let Some(gradient_type) = attributes.remove::<GradientType>(ATTR_GRADIENT_TYPE) { if let Some(gradient_type) = attributes.remove::<GradientType>(ATTR_GRADIENT_TYPE) {
gradient_paint.set_attribute(ATTR_GRADIENT_TYPE, 0, gradient_type); gradient_paint.set_attribute(ATTR_GRADIENT_TYPE, 0, gradient_type);
} }
if let Some(spread_method) = attributes.remove::<GradientSpreadMethod>(ATTR_SPREAD_METHOD) { if let Some(spread_method) = attributes.remove::<GradientSpread>(ATTR_GRADIENT_SPREAD) {
gradient_paint.set_attribute(ATTR_SPREAD_METHOD, 0, spread_method); gradient_paint.set_attribute(ATTR_GRADIENT_SPREAD, 0, spread_method);
} }
attributes.insert(ATTR_FILL, Some(gradient_paint)); attributes.insert(ATTR_FILL, Some(gradient_paint));
+2 -2
View File
@@ -23,13 +23,13 @@ fn gradient_map<T: Adjust<Color> + Clone + Send + Sync + core_types::CacheHash +
if gradient.is_empty() { if gradient.is_empty() {
return image; return image;
} }
let spread_method = gradient.lane(0).attr::<vector_types::markers::SpreadMethod>(); let gradient_spread = gradient.lane(0).attr::<vector_types::markers::GradientSpread>();
let gradient = gradient.element_ref(0); let gradient = gradient.element_ref(0);
image.adjust(|color| { image.adjust(|color| {
let intensity = color.luminance_rec_709(); let intensity = color.luminance_rec_709();
let intensity = if reverse { 1. - intensity } else { intensity }; let intensity = if reverse { 1. - intensity } else { intensity };
gradient.evaluate(intensity as f64, spread_method) gradient.evaluate(intensity as f64, gradient_spread)
}); });
image image