Convert the node catalog to rank-polymorphic Item and List kernels, materialize stored values as ranked wires, and display wire rank in the graph

This commit is contained in:
Keavon Chambers
2026-07-20 21:27:13 -07:00
committed by Dennis Kobert
parent ead622b969
commit d63ea46bd4
45 changed files with 1278 additions and 456 deletions

View File

@@ -6,7 +6,7 @@ use brush_nodes::brush_stroke::BrushStroke;
use core_types::color::SRGBA8;
use core_types::context::Context;
use core_types::gpoll::GPoll;
use core_types::list::List;
use core_types::list::{Item, List};
use core_types::registry::SourceHandle;
use core_types::transform::Footprint;
use core_types::uuid::NodeId;
@@ -125,7 +125,7 @@ macro_rules! tagged_value {
// =======================
// NON-SERIALIZED VARIANTS
// =======================
Self::NodeIdPath(path) => path.hash(state),
Self::NodeIdPath(path) => path.cache_hash(state),
Self::DocumentNode(node) => node.cache_hash(state),
Self::ContextModification(modification) => modification.cache_hash(state),
Self::RenderOutput(x) => x.cache_hash(state),
@@ -169,7 +169,7 @@ macro_rules! tagged_value {
// =======================
// AUTO-GENERATED VARIANTS
// =======================
$( Self::$identifier(x) => Box::new(x), )*
$( Self::$identifier(x) => Box::new(Item::new_from_element(x)), )*
// =======================
// NON-SERIALIZED VARIANTS
// =======================
@@ -178,7 +178,7 @@ macro_rules! tagged_value {
Self::DocumentNode(node) => Box::new(node),
Self::ContextModification(modification) => Box::new(modification),
Self::EditorApi(x) => Box::new(x),
Self::ResourceHash(x) => Box::new(x),
Self::ResourceHash(x) => Box::new(Item::new_from_element(x)),
}
}
@@ -213,7 +213,7 @@ macro_rules! tagged_value {
// =======================
// AUTO-GENERATED VARIANTS
// =======================
$( Self::$identifier(x) => Arc::new(x), )*
$( Self::$identifier(x) => Arc::new(Item::new_from_element(x)), )*
// =======================
// NON-SERIALIZED VARIANTS
// =======================
@@ -222,7 +222,7 @@ macro_rules! tagged_value {
Self::DocumentNode(node) => Arc::new(node),
Self::ContextModification(modification) => Arc::new(modification),
Self::EditorApi(x) => Arc::new(x),
Self::ResourceHash(x) => Arc::new(x),
Self::ResourceHash(x) => Arc::new(Item::new_from_element(x)),
}
}
@@ -397,10 +397,11 @@ macro_rules! tagged_value {
// AUTO-GENERATED VARIANTS
// =======================
$( x if x == TypeId::of::<$ty>() => Ok(TaggedValue::$identifier(*downcast(input).unwrap())), )*
$( x if x == TypeId::of::<Item<$ty>>() => Ok(TaggedValue::$identifier(downcast::<Item<$ty>>(input).unwrap().into_element())), )*
// =======================
// NON-SERIALIZED VARIANTS
// =======================
x if x == TypeId::of::<RenderOutput>() => Ok(TaggedValue::RenderOutput(*downcast(input).unwrap())),
x if x == TypeId::of::<Item<RenderOutput>>() => Ok(TaggedValue::RenderOutput(downcast::<Item<RenderOutput>>(input).unwrap().into_element())),
_ => Err(format!("Cannot convert {:?} to TaggedValue", DynAny::type_name(input.as_ref()))),
}
@@ -419,10 +420,11 @@ macro_rules! tagged_value {
// AUTO-GENERATED VARIANTS
// =======================
$( x if x == TypeId::of::<$ty>() => Ok(TaggedValue::$identifier(<$ty as Clone>::clone(input.downcast_ref().unwrap()))), )*
$( x if x == TypeId::of::<Item<$ty>>() => Ok(TaggedValue::$identifier(Item::<$ty>::clone(input.downcast_ref().unwrap()).into_element())), )*
// =======================
// NON-SERIALIZED VARIANTS
// =======================
x if x == TypeId::of::<RenderOutput>() => Ok(TaggedValue::RenderOutput(RenderOutput::clone(input.downcast_ref().unwrap()))),
x if x == TypeId::of::<Item<RenderOutput>>() => Ok(TaggedValue::RenderOutput(Item::<RenderOutput>::clone(input.downcast_ref().unwrap()).into_element())),
_ => Err(format!("Cannot convert {:?} to TaggedValue", std::any::type_name_of_val(input))),
}
}
@@ -546,8 +548,6 @@ tagged_value! {
LegacyOptionalDAffine2(Option<DAffine2>),
#[serde(alias = "FillGradient")]
LegacyGradient(graphic_types::migrations::legacy::LegacyGradient),
#[serde(alias = "Fill")]
LegacyFill(graphic_types::migrations::legacy::LegacyFill),
// ==========
// ENUM TYPES
// ==========
@@ -589,6 +589,9 @@ tagged_value! {
BooleanOperation(vector::misc::BooleanOperation),
TextAlign(text_nodes::TextAlign),
ScaleType(core_types::transform::ScaleType),
// Legacy
#[serde(alias = "Fill")]
LegacyFill(graphic_types::migrations::legacy::LegacyFill),
}
impl TaggedValue {
@@ -729,6 +732,9 @@ impl TaggedValue {
// The Fill and Stroke nodes' paint connectors default to `List<Graphic>`, their first registered implementation row
() if ty == TypeId::of::<List<Graphic>>() => to_color(string).map(TaggedValue::Color)?,
() if ty == TypeId::of::<List<Gradient>>() => to_gradient(string).map(TaggedValue::Gradient)?,
// A paint default also parses against the bare element forms, as a color or gradient literal
() if ty == TypeId::of::<Graphic>() => to_color(string).map(TaggedValue::Color)?,
() if ty == TypeId::of::<Gradient>() => to_gradient(string).map(TaggedValue::Gradient)?,
() if ty == TypeId::of::<ReferencePoint>() => to_reference_point(string).map(TaggedValue::ReferencePoint)?,
() if ty == TypeId::of::<DashPattern>() => TaggedValue::DashPattern(DashPattern::from(string)),
() if ty == TypeId::of::<BoxCorners>() => TaggedValue::BoxCorners(BoxCorners::from(string)),
@@ -1018,6 +1024,23 @@ mod record_defaults {
mod paint_default_parsing {
use super::*;
/// A Fill/Stroke paint wire carries `Graphic` elements, so its `Color::BLACK` default must parse
/// into a `Color` for a fresh Fill node's paint to resolve.
#[test]
fn paint_wire_parses_color_default_through_its_element() {
let black = Some(TaggedValue::Color(Color::BLACK));
assert_eq!(
TaggedValue::from_primitive_string("Color::BLACK", &concrete!(List<Graphic>)),
black,
"a `List<Graphic>` paint wire should resolve its color default"
);
assert_eq!(
TaggedValue::from_primitive_string("Color::BLACK", &concrete!(Graphic)),
black,
"a bare `Graphic` paint element should resolve its color default"
);
}
/// Table-era documents stored the red-slash "no paint" fill as an empty color table, which must keep
/// deserializing to [`TaggedValue::no_paint`] rather than collapsing to a transparent color.
#[test]