From aba941e81d1f95b24d652385d91c44f88b224bd7 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Mon, 24 Aug 2026 01:35:32 +0000 Subject: [PATCH] Collapse typed levels in To Graphic like the pre-flip list conversion --- .../interpreted-executor/src/node_registry.rs | 10 +++- node-graph/nodes/graphic/src/graphic.rs | 46 +++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 5b15c01225..4d05cb95a6 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -110,12 +110,18 @@ fn node_registry() -> HashMap> { .map(|entry| (graphene_std::vector::morph::IDENTIFIER.clone(), entry)), ); // Element-wise coercion into `Graphic` for single-typed leveled inputs, - // served by the to_graphic rows. + // served by the hidden elementwise rows. node_types.extend( - graphene_std::graphic::to_graphic_entries() + graphene_std::graphic::to_graphic_element_entries() .into_iter() .map(|entry| (ProtoNodeIdentifier::new("graphene_core::ops::IntoNode"), entry)), ); + // The typed-level collapse rows of To Graphic, served under its identifier. + node_types.extend( + graphene_std::graphic::to_graphic_typed_entries() + .into_iter() + .map(|entry| (graphene_std::graphic::to_graphic::IDENTIFIER.clone(), entry)), + ); // The transitional level bridge: a leveled wire materializes into the legacy // list an unconverted consumer expects. The rows are keyed under the legacy // convert identifiers and die with the last legacy consumer. diff --git a/node-graph/nodes/graphic/src/graphic.rs b/node-graph/nodes/graphic/src/graphic.rs index 8fd991f7f5..51c5daaf4b 100644 --- a/node-graph/nodes/graphic/src/graphic.rs +++ b/node-graph/nodes/graphic/src/graphic.rs @@ -489,10 +489,33 @@ fn wrap_graphic_extent(_content: ListIn<'_, T>, _level: LevelIn) -> GPoll + Clone + Send + Sync + core_types::CacheHash + 'static>( + _: impl Ctx, + #[implementations( + Graphic, + List, + List, + List>, + List>, + List, + List, + List, + )] + content: T, +) -> Graphic { + content.into() +} + +/// The elementwise `Graphic` coercion the compiler-inserted converts use: each +/// lane's element converts on its own, so a typed wire feeds a graphic input +/// without changing the level's shape. Registered under the convert identifier. +#[node_macro::node(category(""))] +pub fn to_graphic_element + Clone + Send + Sync + core_types::CacheHash + 'static>( _: impl Ctx, #[implementations( Graphic, @@ -515,6 +538,22 @@ pub fn to_graphic + Clone + Send + Sync + core_types::CacheHash content.into() } +/// The typed-level conversion: the whole level nests as one graphic lane, as +/// the pre-flip `Into` list collapse did. Registered under the to +/// graphic identifier. +#[node_macro::node(category(""), extent(wrap_graphic_extent))] +pub fn to_graphic_typed( + _: impl Ctx + ExtractIndex + InjectIndex + Copy, + #[implementations(Vector, Raster, Raster, Color, GradientStops, String)] content: IList, +) -> Result, Interrupt> { + // SAFETY: a materialized input's frames are arena-resident. + let item = unsafe { core_types::record::GroupItem::from_resident(content.batch()) }; + Ok(Graphic::Group(core_types::record::Group { + row: None, + content: core_types::record::GroupContent::Run(item), + })) +} + /// The transitional level bridge: the wire's records as the legacy list an /// unconverted consumer expects, attributes copied through their erased /// reads. Registered under the legacy convert identifiers; the rows die with @@ -531,7 +570,8 @@ pub fn level_to_list( } pub use _level_to_list_mod::level_to_list_entries; -pub use _to_graphic_mod::to_graphic_entries; +pub use _to_graphic_element_mod::to_graphic_element_entries; +pub use _to_graphic_typed_mod::to_graphic_typed_entries; /// Removes a level of nesting from a `Graphic[]`, or all nesting if "Fully Flatten" is enabled. #[node_macro::node(category("General"), extent(flatten_graphic_extent))]