Skip to content

Commit 644e9cf

Browse files
authored
New nodes: 'Attach Attribute' and 'Read Attribute *' (#4100)
* New nodes: 'Zip Attribute' and 'Read Attribute {Vector, Number, Bool, String, Transform, Color, Blend Mode, Gradient Type, Spread Method} * Cleanup * Reduce type explosion
1 parent 9db91a1 commit 644e9cf

8 files changed

Lines changed: 678 additions & 84 deletions

File tree

editor/src/messages/portfolio/document/data_panel/data_panel_message_handler.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use graphene_std::memo::IORecord;
1212
use graphene_std::raster_types::{CPU, GPU, Raster};
1313
use graphene_std::table::Table;
1414
use graphene_std::vector::Vector;
15-
use graphene_std::vector::style::{Fill, FillChoice};
15+
use graphene_std::vector::style::{Fill, FillChoice, GradientSpreadMethod, GradientType};
1616
use graphene_std::{Artboard, Color, Context, Graphic};
1717
use std::any::Any;
1818
use std::sync::Arc;
@@ -191,6 +191,11 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
191191
Table<String>,
192192
Table<f64>,
193193
Table<u8>,
194+
Table<bool>,
195+
Table<DAffine2>,
196+
Table<BlendMode>,
197+
Table<GradientType>,
198+
Table<GradientSpreadMethod>,
194199
GradientStops,
195200
f64,
196201
u32,
@@ -200,6 +205,9 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
200205
Option<f64>,
201206
DVec2,
202207
DAffine2,
208+
BlendMode,
209+
GradientType,
210+
GradientSpreadMethod,
203211
])
204212
}
205213

@@ -757,6 +765,51 @@ impl TableRowLayout for Affine2 {
757765
}
758766
}
759767

768+
impl TableRowLayout for BlendMode {
769+
fn type_name() -> &'static str {
770+
"BlendMode"
771+
}
772+
fn identifier(&self) -> String {
773+
self.to_string()
774+
}
775+
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
776+
TextLabel::new(self.to_string()).narrow(true).widget_instance()
777+
}
778+
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
779+
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
780+
}
781+
}
782+
783+
impl TableRowLayout for GradientType {
784+
fn type_name() -> &'static str {
785+
"GradientType"
786+
}
787+
fn identifier(&self) -> String {
788+
self.to_string()
789+
}
790+
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
791+
TextLabel::new(self.to_string()).narrow(true).widget_instance()
792+
}
793+
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
794+
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
795+
}
796+
}
797+
798+
impl TableRowLayout for GradientSpreadMethod {
799+
fn type_name() -> &'static str {
800+
"GradientSpreadMethod"
801+
}
802+
fn identifier(&self) -> String {
803+
self.to_string()
804+
}
805+
fn value_widget(&self, _target: PathStep, _data: &LayoutData) -> WidgetInstance {
806+
TextLabel::new(self.to_string()).narrow(true).widget_instance()
807+
}
808+
fn value_page(&self, _data: &mut LayoutData) -> Vec<LayoutGroup> {
809+
vec![LayoutGroup::row(vec![self.value_widget(PathStep::Element(0), _data)])]
810+
}
811+
}
812+
760813
/// Resolves the value/breadcrumb label for a `NodeId` against `network_interface` at the given `network_path`,
761814
/// falling back to "Node {id}" if the node isn't present (e.g. an ID that no longer maps to a real node).
762815
fn node_id_display_label(node_id: NodeId, network_interface: &NodeNetworkInterface, network_path: &[NodeId]) -> String {
@@ -921,6 +974,9 @@ macro_rules! known_table_row_types {
921974

922975
/// Uses `Display` instead of `Debug` for attribute types that have a nicer human-readable format.
923976
fn display_value_override(any: &dyn Any) -> Option<String> {
977+
if let Some(value) = any.downcast_ref::<DVec2>() {
978+
return Some(format_dvec2(*value));
979+
}
924980
if let Some(value) = any.downcast_ref::<BlendMode>() {
925981
return Some(value.to_string());
926982
}

node-graph/interpreted-executor/src/node_registry.rs

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use graphene_std::raster::color::Color;
1717
use graphene_std::raster::*;
1818
use graphene_std::raster::{CPU, Raster};
1919
use graphene_std::render_node::RenderIntermediate;
20-
use graphene_std::table::Table;
20+
use graphene_std::table::{AttributeColumnDyn, AttributeValueDyn, Table, TableDyn};
2121
use graphene_std::transform::Footprint;
2222
use graphene_std::uuid::NodeId;
2323
use graphene_std::vector::Vector;
@@ -42,6 +42,54 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
4242
convert_node!(from: Table<Raster<CPU>>, to: Table<Graphic>),
4343
#[cfg(feature = "gpu")]
4444
convert_node!(from: Table<Raster<GPU>>, to: Table<Graphic>),
45+
// Type-erased attribute column conversions for the `Attach Attribute` node, so it monomorphizes only over the destination table type.
46+
convert_node!(from: Table<Artboard>, to: AttributeColumnDyn),
47+
convert_node!(from: Table<Graphic>, to: AttributeColumnDyn),
48+
convert_node!(from: Table<Vector>, to: AttributeColumnDyn),
49+
convert_node!(from: Table<Raster<CPU>>, to: AttributeColumnDyn),
50+
convert_node!(from: Table<Color>, to: AttributeColumnDyn),
51+
convert_node!(from: Table<GradientStops>, to: AttributeColumnDyn),
52+
convert_node!(from: Table<f64>, to: AttributeColumnDyn),
53+
convert_node!(from: Table<bool>, to: AttributeColumnDyn),
54+
convert_node!(from: Table<String>, to: AttributeColumnDyn),
55+
convert_node!(from: Table<DAffine2>, to: AttributeColumnDyn),
56+
convert_node!(from: Table<BlendMode>, to: AttributeColumnDyn),
57+
convert_node!(from: Table<graphene_std::vector::style::GradientType>, to: AttributeColumnDyn),
58+
convert_node!(from: Table<graphene_std::vector::style::GradientSpreadMethod>, to: AttributeColumnDyn),
59+
convert_node!(from: Table<Artboard>, to: TableDyn),
60+
convert_node!(from: Table<Graphic>, to: TableDyn),
61+
convert_node!(from: Table<Vector>, to: TableDyn),
62+
convert_node!(from: Table<Raster<CPU>>, to: TableDyn),
63+
#[cfg(feature = "gpu")]
64+
convert_node!(from: Table<Raster<GPU>>, to: TableDyn),
65+
convert_node!(from: Table<Color>, to: TableDyn),
66+
convert_node!(from: Table<GradientStops>, to: TableDyn),
67+
convert_node!(from: Table<f64>, to: TableDyn),
68+
convert_node!(from: Table<bool>, to: TableDyn),
69+
convert_node!(from: Table<String>, to: TableDyn),
70+
convert_node!(from: Table<u8>, to: TableDyn),
71+
convert_node!(from: Table<NodeId>, to: TableDyn),
72+
convert_node!(from: Table<DAffine2>, to: TableDyn),
73+
convert_node!(from: Table<BlendMode>, to: TableDyn),
74+
convert_node!(from: Table<graphene_std::vector::style::GradientType>, to: TableDyn),
75+
convert_node!(from: Table<graphene_std::vector::style::GradientSpreadMethod>, to: TableDyn),
76+
// Type-erased attribute value conversions for the `Write Attribute` node, so it monomorphizes only over the destination table type.
77+
convert_node!(from: f64, to: AttributeValueDyn),
78+
convert_node!(from: u32, to: AttributeValueDyn),
79+
convert_node!(from: u64, to: AttributeValueDyn),
80+
convert_node!(from: bool, to: AttributeValueDyn),
81+
convert_node!(from: String, to: AttributeValueDyn),
82+
convert_node!(from: DVec2, to: AttributeValueDyn),
83+
convert_node!(from: DAffine2, to: AttributeValueDyn),
84+
convert_node!(from: Color, to: AttributeValueDyn),
85+
convert_node!(from: BlendMode, to: AttributeValueDyn),
86+
convert_node!(from: graphene_std::vector::style::GradientType, to: AttributeValueDyn),
87+
convert_node!(from: graphene_std::vector::style::GradientSpreadMethod, to: AttributeValueDyn),
88+
convert_node!(from: Table<String>, to: AttributeValueDyn),
89+
convert_node!(from: Table<NodeId>, to: AttributeValueDyn),
90+
convert_node!(from: Table<Color>, to: AttributeValueDyn),
91+
convert_node!(from: Table<GradientStops>, to: AttributeValueDyn),
92+
convert_node!(from: Table<Graphic>, to: AttributeValueDyn),
4593
// into_node!(from: Table<Raster<CPU>>, to: Table<Raster<SRGBA8>>),
4694
#[cfg(feature = "gpu")]
4795
into_node!(from: &PlatformEditorApi, to: &WgpuExecutor),
@@ -99,6 +147,14 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
99147
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<NodeId>]),
100148
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<f64>]),
101149
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<u8>]),
150+
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<bool>]),
151+
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<DAffine2>]),
152+
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<BlendMode>]),
153+
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<graphene_std::vector::style::GradientType>]),
154+
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<graphene_std::vector::style::GradientSpreadMethod>]),
155+
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => AttributeColumnDyn]),
156+
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => AttributeValueDyn]),
157+
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => TableDyn]),
102158
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Graphic]),
103159
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text::Font]),
104160
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<BrushStroke>]),
@@ -138,11 +194,14 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
138194
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => &PlatformEditorApi, Context => graphene_std::ContextFeatures]),
139195
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => RenderIntermediate, Context => graphene_std::ContextFeatures]),
140196
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => RenderOutput, Context => graphene_std::ContextFeatures]),
197+
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => AttributeColumnDyn, Context => graphene_std::ContextFeatures]),
198+
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => AttributeValueDyn, Context => graphene_std::ContextFeatures]),
199+
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => TableDyn, Context => graphene_std::ContextFeatures]),
141200
#[cfg(target_family = "wasm")]
142201
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => CanvasHandle, Context => graphene_std::ContextFeatures]),
143-
// =============
144-
// MEMOIZE NODES
145-
// =============
202+
// ==========
203+
// MEMO NODES
204+
// ==========
146205
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => ()]),
147206
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => bool]),
148207
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Table<Artboard>]),
@@ -156,6 +215,14 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
156215
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Table<NodeId>]),
157216
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Table<f64>]),
158217
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Table<u8>]),
218+
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Table<bool>]),
219+
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Table<DAffine2>]),
220+
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Table<BlendMode>]),
221+
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Table<graphene_std::vector::style::GradientType>]),
222+
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Table<graphene_std::vector::style::GradientSpreadMethod>]),
223+
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => AttributeColumnDyn]),
224+
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => AttributeValueDyn]),
225+
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => TableDyn]),
159226
#[cfg(target_family = "wasm")]
160227
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => CanvasHandle]),
161228
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => f64]),

node-graph/libraries/core-types/src/ops.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::Node;
2-
use crate::table::{Table, TableRow};
2+
use crate::table::{AttributeColumnDyn, AttributeValueDyn, Column, Table, TableDyn, TableRow};
33
use crate::transform::Footprint;
44
use glam::DVec2;
5+
use graphene_hash::CacheHash;
56
use std::future::Future;
67
use std::marker::PhantomData;
78

@@ -71,6 +72,34 @@ impl<U, T: TableConvert<U> + Send> Convert<Table<U>, ()> for Table<T> {
7172
}
7273
}
7374

75+
/// Wraps each row's element into a type-erased column. Lets nodes that accept a source attribute
76+
/// from any `Table<U>` express their signature as `AttributeColumnDyn` and avoid monomorphizing
77+
/// over `U`; the compiler inserts this convert to bridge concrete-typed graph wires to the dyn input.
78+
impl<T: Clone + Send + Sync + Default + std::fmt::Debug + PartialEq + CacheHash + 'static> Convert<AttributeColumnDyn, ()> for Table<T> {
79+
async fn convert(self, _: Footprint, _: ()) -> AttributeColumnDyn {
80+
let values: Vec<T> = self.into_iter().map(|row| row.into_element()).collect();
81+
AttributeColumnDyn(Box::new(Column(values)))
82+
}
83+
}
84+
85+
/// Wraps a value into a type-erased attribute value. Lets nodes that take a per-item value source
86+
/// (such as `write_attribute`'s value-producing input) be generic over the destination table type
87+
/// alone, with the compiler-inserted convert handling each concrete value type at the wire level.
88+
impl<T: Clone + Send + Sync + Default + std::fmt::Debug + PartialEq + CacheHash + 'static> Convert<AttributeValueDyn, ()> for T {
89+
async fn convert(self, _: Footprint, _: ()) -> AttributeValueDyn {
90+
AttributeValueDyn(Box::new(self))
91+
}
92+
}
93+
94+
/// Erases a `Table<T>`'s element type, exposing only its attributes and row count. Lets nodes that
95+
/// only need attribute access (such as the `read_attribute_*` family) take a single `TableDyn` input
96+
/// instead of monomorphizing over every possible carrier table type.
97+
impl<T: Send> Convert<TableDyn, ()> for Table<T> {
98+
async fn convert(self, _: Footprint, _: ()) -> TableDyn {
99+
self.into()
100+
}
101+
}
102+
74103
impl Convert<DVec2, ()> for DVec2 {
75104
async fn convert(self, _: Footprint, _: ()) -> DVec2 {
76105
self

0 commit comments

Comments
 (0)