diff --git a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs index ba9c8e39f1..5ae169e2a5 100644 --- a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs +++ b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs @@ -11,8 +11,8 @@ use graph_craft::application_io::resource::ResourceId; use graph_craft::document::value::TaggedValue; use graph_craft::document::{NodeId, NodeInput}; use graph_craft::{ProtoNodeIdentifier, concrete, descriptor}; -use graphene_std::list::List; use graphene_std::brush::brush_stroke::BrushStroke; +use graphene_std::list::List; use graphene_std::raster::BlendMode; use graphene_std::raster_types::Image; use graphene_std::subpath::Subpath; @@ -675,7 +675,7 @@ impl<'a> ModifyInputsContext<'a> { let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::PaintOrderInput); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::PaintOrder(stroke.paint_order), false), false); let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashPatternInput); - self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::DashPattern(stroke.dash_lengths.into()), false), true); + self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::DashPattern(stroke.dash_lengths), false), true); let input_connector = InputConnector::node(stroke_node_id, graphene_std::vector::stroke::DashOffsetInput); self.set_input_with_refresh(input_connector, NodeInput::value(TaggedValue::F64(stroke.dash_offset), false), true); } diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_properties.rs index 36739712e8..54636c2149 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -33,9 +33,7 @@ use graphene_std::vector::misc::BooleanOperation; use graphene_std::vector::misc::{ ArcType, BoxCorners, CentroidType, ExtrudeJoiningAlgorithm, GridType, InterpolationDistribution, MergeByDistanceAlgorithm, PointSpacingType, RowsOrColumns, SpiralType, }; -use graphene_std::vector::style::{ - DashPattern, FillChoiceUI, Gradient, GradientSpreadMethod, GradientType, GradientUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation, -}; +use graphene_std::vector::style::{FillChoiceUI, Gradient, GradientSpreadMethod, GradientType, GradientUI, PaintOrder, StrokeAlign, StrokeCap, StrokeJoin, build_transform_with_y_preservation}; use graphene_std::vector::{QRCodeErrorCorrectionLevel, VectorModification}; use graphene_std::{NodeParameter, ParameterRef}; @@ -863,12 +861,12 @@ pub fn dash_pattern_widget(parameter_widgets_info: ParameterWidgetsInfo, text_in log::warn!("A widget failed to be built because its node's input index is invalid."); return vec![]; }; - if let Some(TaggedValue::DashPattern(pattern)) = &input.as_non_exposed_value() { + if let Some(TaggedValue::DashPattern(lengths)) = &input.as_non_exposed_value() { widgets.extend_from_slice(&[ Separator::new(SeparatorStyle::Unrelated).widget_instance(), text_input - .value(pattern.0.iter_element_values().map(|length| length.to_string()).collect::>().join(", ")) - .on_update(parameter_widgets_info.optionally_update_value(move |input: &TextInput| Some(TaggedValue::DashPattern(DashPattern::from(input.value.as_str()))))) + .value(lengths.iter().map(|length| length.to_string()).collect::>().join(", ")) + .on_update(parameter_widgets_info.optionally_update_value(move |input: &TextInput| Some(TaggedValue::DashPattern(graphene_std::core_types::misc::parse_f64_list(&input.value))))) .widget_instance(), ]) } @@ -2197,7 +2195,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties return vec![]; }; let corner_values = match input.as_non_exposed_value() { - Some(TaggedValue::BoxCorners(corners)) => corners.to_corner_values(), + Some(TaggedValue::BoxCorners(values)) => BoxCorners::from(values.clone()).to_corner_values(), _ => [0.; 4], }; let uniform_val = corner_values[0]; @@ -2216,7 +2214,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties NodeGraphMessage::SetInputValue { node_id, input_index: CornerRadiusInput::INDEX, - value: Box::new(TaggedValue::BoxCorners(BoxCorners::from(uniform_val))), + value: Box::new(TaggedValue::BoxCorners(vec![uniform_val])), } .into(), ]), @@ -2235,7 +2233,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties NodeGraphMessage::SetInputValue { node_id, input_index: CornerRadiusInput::INDEX, - value: Box::new(TaggedValue::BoxCorners(BoxCorners::from(corner_values.to_vec()))), + value: Box::new(TaggedValue::BoxCorners(corner_values.to_vec())), } .into(), ]), @@ -2249,7 +2247,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties TextInput::default() .value(corner_values.iter().map(|v| v.to_string()).collect::>().join(", ")) .on_update(optionally_update_value( - move |x: &TextInput| Some(TaggedValue::BoxCorners(BoxCorners::from(x.value.as_str()))), + move |x: &TextInput| Some(TaggedValue::BoxCorners(graphene_std::core_types::misc::parse_f64_list(&x.value))), node_id, CornerRadiusInput, )) @@ -2258,11 +2256,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties NumberInput::default() .value(Some(uniform_val)) .unit(" px") - .on_update(update_value( - move |x: &NumberInput| TaggedValue::BoxCorners(BoxCorners::from(x.value.unwrap())), - node_id, - CornerRadiusInput, - )) + .on_update(update_value(move |x: &NumberInput| TaggedValue::BoxCorners(vec![x.value.unwrap()]), node_id, CornerRadiusInput)) .on_commit(commit_value) .widget_instance() }; @@ -2725,7 +2719,7 @@ pub fn stroke_properties(node_id: NodeId, context: &mut NodePropertiesContext) - }; let has_dash_lengths = match &document_node.input_value(DashPatternInput) { - Some(TaggedValue::DashPattern(pattern)) => pattern.0.is_empty(), + Some(TaggedValue::DashPattern(lengths)) => lengths.is_empty(), _ => true, }; let miter_limit_disabled = join_value != &StrokeJoin::Miter; diff --git a/editor/src/messages/portfolio/document_migration.rs b/editor/src/messages/portfolio/document_migration.rs index 8e64bef3ba..100674f0e0 100644 --- a/editor/src/messages/portfolio/document_migration.rs +++ b/editor/src/messages/portfolio/document_migration.rs @@ -17,8 +17,7 @@ use graphene_std::text::{TextAlign, TypesettingConfig}; use graphene_std::transform::ScaleType; use graphene_std::uuid::NodeId; use graphene_std::vector::graphic_types; -use graphene_std::vector::misc::BoxCorners; -use graphene_std::vector::style::{DashPattern, PaintOrder, StrokeAlign}; +use graphene_std::vector::style::{PaintOrder, StrokeAlign}; use std::collections::HashMap; use std::f64::consts::PI; use std::ops::Range; @@ -1386,24 +1385,24 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_ /// Converts a legacy stroke dash input (a `List`, single `f64`, or comma/space separated `String`) to the `DashPattern` value type. fn migrate_dash_input(input: &NodeInput) -> Option { let NodeInput::Value { tagged_value, exposed } = input else { return None }; - let pattern = match &*tagged_value.clone().into_inner() { - TaggedValue::F64Array(lengths) => DashPattern::from(lengths.clone()), - TaggedValue::F64(length) => DashPattern::from(*length), - TaggedValue::String(text) => DashPattern::from(text.as_str()), + let lengths = match &*tagged_value.clone().into_inner() { + TaggedValue::F64Array(lengths) => lengths.clone(), + TaggedValue::F64(length) => vec![*length], + TaggedValue::String(text) => graphene_std::core_types::misc::parse_f64_list(text), _ => return None, }; - Some(NodeInput::value(TaggedValue::DashPattern(pattern), *exposed)) + Some(NodeInput::value(TaggedValue::DashPattern(lengths), *exposed)) } /// Converts a legacy rectangle corner radius input (a single `f64` or a `List` of up to four values) to the `BoxCorners` value type. fn migrate_corner_radius_input(input: &NodeInput) -> Option { let NodeInput::Value { tagged_value, exposed } = input else { return None }; - let corners = match &*tagged_value.clone().into_inner() { - TaggedValue::F64Array(values) => BoxCorners::from(values.clone()), - TaggedValue::F64(value) => BoxCorners::from(*value), + let values = match &*tagged_value.clone().into_inner() { + TaggedValue::F64Array(values) => values.clone(), + TaggedValue::F64(value) => vec![*value], _ => return None, }; - Some(NodeInput::value(TaggedValue::BoxCorners(corners), *exposed)) + Some(NodeInput::value(TaggedValue::BoxCorners(values), *exposed)) } fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], document: &mut DocumentMessageHandler, reset_node_definitions_on_open: bool) -> Option<()> { @@ -1850,6 +1849,18 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId], .set_input(&InputConnector::node(*node_id, graphene_std::vector::stroke::DashPatternInput), migrated, network_path); } + // The corner radius became the `BoxCorners` value type; convert any already-shaped rectangle that still stores a legacy corner input + if reference == DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::rectangle::IDENTIFIER) + && let Some(corner_input) = node.input(graphene_std::vector::generator_nodes::rectangle::CornerRadiusInput) + && let Some(migrated) = migrate_corner_radius_input(corner_input) + { + document.network_interface.set_input( + &InputConnector::node(*node_id, graphene_std::vector::generator_nodes::rectangle::CornerRadiusInput), + migrated, + network_path, + ); + } + // The rectangle's corner radius became the `BoxCorners` value type and its hidden individual-radii toggle moved after the // user-visible inputs. A legacy rectangle stores that toggle (a plain `bool`) at index 3, where the new shape stores the corner // radius, so a `bool` value there identifies the old input order: [width, height, individual, corner_radius, clamped]. diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index 7c330c8da7..a5dc3f034d 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -557,7 +557,7 @@ pub fn get_stroke_options(layer: LayerNodeIdentifier, network_interface: &NodeNe _ => PaintOrder::default(), }; let dash_lengths = match parameters.value(stroke::DashPatternInput) { - Some(TaggedValue::DashPattern(value)) => value.0.iter_element_values().copied().collect(), + Some(TaggedValue::DashPattern(lengths)) => lengths.clone(), _ => Vec::new(), }; let dash_offset = match parameters.value(stroke::DashOffsetInput) { diff --git a/editor/src/messages/tool/common_functionality/stroke_options.rs b/editor/src/messages/tool/common_functionality/stroke_options.rs index 5069f7627e..95d5b9d59a 100644 --- a/editor/src/messages/tool/common_functionality/stroke_options.rs +++ b/editor/src/messages/tool/common_functionality/stroke_options.rs @@ -213,7 +213,7 @@ pub fn apply_paint_order(drawing: &mut DrawingToolState, order: PaintOrder, docu pub fn apply_dash_lengths(drawing: &mut DrawingToolState, lengths: Vec, document: &DocumentMessageHandler, responses: &mut VecDeque) { drawing.dash_lengths = Some(lengths.clone()); - graph_modification_utils::set_parameter_for_selected_layers(document, graphene_std::vector::stroke::DashPatternInput, TaggedValue::DashPattern(lengths.into()), responses); + graph_modification_utils::set_parameter_for_selected_layers(document, graphene_std::vector::stroke::DashPatternInput, TaggedValue::DashPattern(lengths), responses); } pub fn apply_dash_offset(drawing: &mut DrawingToolState, offset: f64, document: &DocumentMessageHandler, responses: &mut VecDeque) { diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index cd95a33622..900b414b90 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -2,7 +2,7 @@ use super::DocumentNode; use crate::application_io::PlatformEditorApi; use crate::application_io::resource::Resource; use crate::proto::Any as DAny; -use brush_nodes::brush_stroke::BrushStroke; +use brush_nodes::brush_stroke::{BrushStroke, BrushTrace}; use core_types::color::SRGBA8; use core_types::context::Context; use core_types::gpoll::GPoll; @@ -66,6 +66,10 @@ macro_rules! tagged_value { #[serde(deserialize_with = "core_types::misc::migrate_to_f64_array")] // TODO: Eventually remove this migration document upgrade code #[serde(alias = "F64Table", alias = "VecF64", alias = "VecF32", alias = "F64Array4")] F64Array(Vec), + /// Stored compactly as a `Vec` of dash lengths, materializes as an `Item` at runtime via `to_dynany`/`to_any`. + DashPattern(Vec), + /// Stored compactly as a `Vec` of corner values, materializes as an `Item` at runtime via `to_dynany`/`to_any`. + BoxCorners(Vec), /// A plain, always-present color. Aliases recover legacy on-disk shapes; a legacy `null` payload (the old "no color") /// is routed to [`TaggedValue::no_paint`] by `deserialize_tagged_value_with_legacy_migration`. #[serde(deserialize_with = "core_types::misc::migrate_to_color")] // TODO: Eventually remove this migration document upgrade code @@ -119,6 +123,8 @@ macro_rules! tagged_value { // ======================= $( Self::$identifier(x) => { x.cache_hash(state) }),* Self::F64Array(values) => values.cache_hash(state), + Self::DashPattern(lengths) => lengths.cache_hash(state), + Self::BoxCorners(values) => values.cache_hash(state), Self::Color(color) => color.cache_hash(state), Self::Gradient(stops) => stops.cache_hash(state), Self::BrushStrokes(strokes) => strokes.cache_hash(state), @@ -160,6 +166,8 @@ macro_rules! tagged_value { let list: List = values.into_iter().map(core_types::list::Item::new_from_element).collect(); Box::new(list) } + Self::DashPattern(lengths) => Box::new(DashPattern::from(lengths)), + Self::BoxCorners(values) => Box::new(BoxCorners::from(values)), Self::Color(color) => Box::new(List::::new_from_element(color)), Self::Gradient(stops) => Box::new(List::::new_from_element(stops)), Self::BrushStrokes(strokes) => { @@ -204,6 +212,8 @@ macro_rules! tagged_value { let list: List = values.into_iter().map(core_types::list::Item::new_from_element).collect(); Arc::new(list) } + Self::DashPattern(lengths) => Arc::new(DashPattern::from(lengths)), + Self::BoxCorners(values) => Arc::new(BoxCorners::from(values)), Self::Color(color) => Arc::new(List::::new_from_element(color)), Self::Gradient(stops) => Arc::new(List::::new_from_element(stops)), Self::BrushStrokes(strokes) => { @@ -245,6 +255,8 @@ macro_rules! tagged_value { Type::Concrete(td.clone()) } Self::F64Array(_) => concrete!(f64), + Self::DashPattern(_) => concrete!(DashPattern), + Self::BoxCorners(_) => concrete!(BoxCorners), Self::Color(_) => concrete!(Color), Self::Gradient(_) => concrete!(Gradient), Self::BrushStrokes(_) => concrete!(BrushStroke), @@ -294,6 +306,8 @@ macro_rules! tagged_value { None } Self::F64Array(_) => leveled::(), + Self::DashPattern(_) => scalar::(), + Self::BoxCorners(_) => scalar::(), Self::Color(_) => leveled::(), Self::Gradient(_) => leveled::(), Self::BrushStrokes(_) => leveled::(), @@ -337,6 +351,8 @@ macro_rules! tagged_value { Self::from_type_or_none(&Type::Concrete(td)).to_edge() } Self::F64Array(values) => Ok(leveled_record_value_source(values)), + Self::DashPattern(lengths) => Ok(record_value_source(DashPattern::from(lengths))), + Self::BoxCorners(values) => Ok(record_value_source(BoxCorners::from(values))), Self::Color(color) => Ok(leveled_record_value_source(vec![color])), Self::Gradient(stops) => Ok(leveled_record_value_source(vec![stops])), Self::BrushStrokes(strokes) => Ok(leveled_record_value_source(strokes)), @@ -392,7 +408,20 @@ macro_rules! tagged_value { // =============== // MANUAL VARIANTS // =============== + // The manual variants convert from both their payload and wire forms, with the newtypes flattening to their stored `Vec` form x if x == TypeId::of::<()>() => Ok(TaggedValue::None), + x if x == TypeId::of::>() => Ok(TaggedValue::F64Array(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::F64Array(downcast::>(input).unwrap().iter_element_values().copied().collect())), + x if x == TypeId::of::() => Ok(TaggedValue::DashPattern(downcast::(input).unwrap().0.iter_element_values().copied().collect())), + x if x == TypeId::of::>() => Ok(TaggedValue::DashPattern(downcast::>(input).unwrap().into_element().0.iter_element_values().copied().collect())), + x if x == TypeId::of::() => Ok(TaggedValue::BoxCorners(downcast::(input).unwrap().0.iter_element_values().copied().collect())), + x if x == TypeId::of::>() => Ok(TaggedValue::BoxCorners(downcast::>(input).unwrap().into_element().0.iter_element_values().copied().collect())), + x if x == TypeId::of::() => Ok(TaggedValue::Color(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::Color(downcast::>(input).unwrap().into_element())), + x if x == TypeId::of::() => Ok(TaggedValue::Gradient(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::Gradient(downcast::>(input).unwrap().into_element())), + x if x == TypeId::of::>() => Ok(TaggedValue::BrushStrokes(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::BrushStrokes(downcast::>(input).unwrap().into_element().0.iter_element_values().cloned().collect())), // ======================= // AUTO-GENERATED VARIANTS // ======================= @@ -415,7 +444,20 @@ macro_rules! tagged_value { // =============== // MANUAL VARIANTS // =============== + // The manual variants convert from both their payload and wire forms, with the newtypes flattening to their stored `Vec` form x if x == TypeId::of::<()>() => Ok(TaggedValue::None), + x if x == TypeId::of::>() => Ok(TaggedValue::F64Array(input.downcast_ref::>().unwrap().clone())), + x if x == TypeId::of::>() => Ok(TaggedValue::F64Array(input.downcast_ref::>().unwrap().iter_element_values().copied().collect())), + x if x == TypeId::of::() => Ok(TaggedValue::DashPattern(input.downcast_ref::().unwrap().0.iter_element_values().copied().collect())), + x if x == TypeId::of::>() => Ok(TaggedValue::DashPattern(input.downcast_ref::>().unwrap().element().0.iter_element_values().copied().collect())), + x if x == TypeId::of::() => Ok(TaggedValue::BoxCorners(input.downcast_ref::().unwrap().0.iter_element_values().copied().collect())), + x if x == TypeId::of::>() => Ok(TaggedValue::BoxCorners(input.downcast_ref::>().unwrap().element().0.iter_element_values().copied().collect())), + x if x == TypeId::of::() => Ok(TaggedValue::Color(*input.downcast_ref::().unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::Color(*input.downcast_ref::>().unwrap().element())), + x if x == TypeId::of::() => Ok(TaggedValue::Gradient(input.downcast_ref::().unwrap().clone())), + x if x == TypeId::of::>() => Ok(TaggedValue::Gradient(input.downcast_ref::>().unwrap().element().clone())), + x if x == TypeId::of::>() => Ok(TaggedValue::BrushStrokes(input.downcast_ref::>().unwrap().clone())), + x if x == TypeId::of::>() => Ok(TaggedValue::BrushStrokes(input.downcast_ref::>().unwrap().element().0.iter_element_values().cloned().collect())), // ======================= // AUTO-GENERATED VARIANTS // ======================= @@ -446,6 +488,8 @@ macro_rules! tagged_value { if name == core_types::normalize_type_name(std::any::type_name::>()) { return Some(TaggedValue::Gradient(Gradient::default())) } $( if name == core_types::normalize_type_name(std::any::type_name::<$ty>()) { return Some(TaggedValue::$identifier(Default::default())) } )* if name == core_types::normalize_type_name(std::any::type_name::>()) { return Some(TaggedValue::F64Array(Vec::new())) } + if name == core_types::normalize_type_name(std::any::type_name::()) { return Some(TaggedValue::DashPattern(Vec::new())) } + if name == core_types::normalize_type_name(std::any::type_name::()) { return Some(TaggedValue::BoxCorners(Vec::new())) } if name == core_types::normalize_type_name(std::any::type_name::>()) { return Some(TaggedValue::BrushStrokes(Vec::new())) } // Leveled inputs type by their element; each element name maps to the // same tagged default as its legacy list form. @@ -482,6 +526,8 @@ macro_rules! tagged_value { Self::None => "()".to_string(), Self::TypeDefault(td) => format!("TypeDefault({})", td.name), Self::F64Array(values) => format!("F64Array({values:?})"), + Self::DashPattern(lengths) => format!("DashPattern({lengths:?})"), + Self::BoxCorners(values) => format!("BoxCorners({values:?})"), Self::Color(color) => format!("Color({color:?})"), Self::Gradient(stops) => format!("Gradient({stops:?})"), Self::BrushStrokes(strokes) => format!("BrushStrokes({strokes:?})"), @@ -580,8 +626,6 @@ tagged_value! { StrokeJoin(vector::style::StrokeJoin), StrokeAlign(vector::style::StrokeAlign), PaintOrder(vector::style::PaintOrder), - DashPattern(vector::style::DashPattern), - BoxCorners(vector::misc::BoxCorners), GradientType(vector::style::GradientType), GradientSpreadMethod(vector::style::GradientSpreadMethod), ReferencePoint(vector::ReferencePoint), @@ -736,8 +780,8 @@ impl TaggedValue { () if ty == TypeId::of::() => to_color(string).map(TaggedValue::Color)?, () if ty == TypeId::of::() => to_gradient(string).map(TaggedValue::Gradient)?, () if ty == TypeId::of::() => to_reference_point(string).map(TaggedValue::ReferencePoint)?, - () if ty == TypeId::of::() => TaggedValue::DashPattern(DashPattern::from(string)), - () if ty == TypeId::of::() => TaggedValue::BoxCorners(BoxCorners::from(string)), + () if ty == TypeId::of::() => TaggedValue::DashPattern(core_types::misc::parse_f64_list(string)), + () if ty == TypeId::of::() => TaggedValue::BoxCorners(core_types::misc::parse_f64_list(string)), _ => return None, }; Some(ty) diff --git a/node-graph/graph-craft/src/proto.rs b/node-graph/graph-craft/src/proto.rs index 35bad9a287..742c826f42 100644 --- a/node-graph/graph-craft/src/proto.rs +++ b/node-graph/graph-craft/src/proto.rs @@ -1320,7 +1320,7 @@ mod test { // If this assert fails: These NodeIds seem to be changing when you modify TaggedValue, just update them. assert_eq!( ids, - vec![NodeId(12815475172301479638), NodeId(13251389748338817266), NodeId(7166921994790432021), NodeId(15318519137317483318)] + vec![NodeId(12331852515109999872), NodeId(5084548161767585362), NodeId(14635346976242256925), NodeId(16015195863711239715)] ); } diff --git a/node-graph/libraries/core-types/src/misc.rs b/node-graph/libraries/core-types/src/misc.rs index f6d3421719..ca85291508 100644 --- a/node-graph/libraries/core-types/src/misc.rs +++ b/node-graph/libraries/core-types/src/misc.rs @@ -124,6 +124,11 @@ pub fn migrate_to_f64_array<'de, D: serde::Deserializer<'de>>(deserializer: D) - }) } +/// Parses a comma or space separated list of numbers, skipping any pieces that fail to parse. +pub fn parse_f64_list(text: &str) -> Vec { + text.split([',', ' ']).filter(|piece| !piece.is_empty()).filter_map(|piece| piece.parse::().ok()).collect() +} + /// Parse a CSS color string (named color, hex, `rgb(...)`, `hsl(...)`, etc.) into a linear-light [`Color`] using the `color` crate's CSS Color 4 parser. /// Tries the input as-is first (catches CSS named colors like `red`, `rgb(...)`, and well-formed hex like `#abcdef`), then falls back to treating the input as bare hex with length-based expansion to a CSS-parseable form: /// - 1 char `f` → `#fff` (CSS 3-char shorthand) diff --git a/node-graph/libraries/vector-types/src/vector/misc.rs b/node-graph/libraries/vector-types/src/vector/misc.rs index ea0ba630bf..f7a201f783 100644 --- a/node-graph/libraries/vector-types/src/vector/misc.rs +++ b/node-graph/libraries/vector-types/src/vector/misc.rs @@ -77,21 +77,6 @@ impl BoxCorners { } } -// `List` is a runtime-only wire type, so serialize the corners as their bare values to keep documents stable -#[cfg(feature = "serde")] -impl serde::Serialize for BoxCorners { - fn serialize(&self, serializer: S) -> Result { - serializer.collect_seq(self.0.iter_element_values()) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for BoxCorners { - fn deserialize>(deserializer: D) -> Result { - Ok(Self::from( as serde::Deserialize>::deserialize(deserializer)?)) - } -} - impl From for BoxCorners { fn from(value: f64) -> Self { Self(List::new_from_element(value)) @@ -106,12 +91,7 @@ impl From> for BoxCorners { impl From<&str> for BoxCorners { fn from(text: &str) -> Self { - Self::from( - text.split([',', ' ']) - .filter(|piece| !piece.is_empty()) - .filter_map(|piece| piece.parse::().ok()) - .collect::>(), - ) + Self::from(core_types::misc::parse_f64_list(text)) } } diff --git a/node-graph/libraries/vector-types/src/vector/style.rs b/node-graph/libraries/vector-types/src/vector/style.rs index 34bdcef8c3..8a3b3ed13c 100644 --- a/node-graph/libraries/vector-types/src/vector/style.rs +++ b/node-graph/libraries/vector-types/src/vector/style.rs @@ -118,18 +118,18 @@ pub enum StrokeCap { #[default] #[icon("StrokeCapButt")] Butt, - #[icon("StrokeCapRound")] - Round, #[icon("StrokeCapSquare")] Square, + #[icon("StrokeCapRound")] + Round, } impl StrokeCap { pub fn svg_name(&self) -> &'static str { match self { StrokeCap::Butt => "butt", - StrokeCap::Round => "round", StrokeCap::Square => "square", + StrokeCap::Round => "round", } } } @@ -218,21 +218,6 @@ impl DashPattern { } } -// `List` is a runtime-only wire type, so serialize the pattern as its bare lengths to keep documents stable -#[cfg(feature = "serde")] -impl serde::Serialize for DashPattern { - fn serialize(&self, serializer: S) -> Result { - serializer.collect_seq(self.0.iter_element_values()) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for DashPattern { - fn deserialize>(deserializer: D) -> Result { - Ok(Self::from( as serde::Deserialize>::deserialize(deserializer)?)) - } -} - impl From for DashPattern { fn from(length: f64) -> Self { Self(List::new_from_element(length)) @@ -247,12 +232,7 @@ impl From> for DashPattern { impl From<&str> for DashPattern { fn from(text: &str) -> Self { - Self::from( - text.split([',', ' ']) - .filter(|piece| !piece.is_empty()) - .filter_map(|piece| piece.parse::().ok()) - .collect::>(), - ) + Self::from(core_types::misc::parse_f64_list(text)) } }