Skip to content

Commit

Permalink
Instance tables refactor part 4: replace ArtboardGroups with multi-ro…
Browse files Browse the repository at this point in the history
…w Instances<Artboard> (#2265)

* Clean up dyn_any usages

* Migrate ArtboardGroup to ArtboardGroupTable (not yet flattened)

* Reorder graphical data imports

* Flatten and remove ArtboardGroup in favor of ArtboardGroupTable

* Fix test
  • Loading branch information
Keavon authored Mar 3, 2025
1 parent 9ae6562 commit 0158be3
Show file tree
Hide file tree
Showing 32 changed files with 233 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2408,7 +2408,7 @@ impl DocumentMessageHandler {
/// Create a network interface with a single export
fn default_document_network_interface() -> NodeNetworkInterface {
let mut network_interface = NodeNetworkInterface::default();
network_interface.add_export(TaggedValue::ArtboardGroup(graphene_core::ArtboardGroup::default()), -1, "", &[]);
network_interface.add_export(TaggedValue::ArtboardGroup(graphene_core::ArtboardGroupTable::default()), -1, "", &[]);
network_interface
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ impl<'a> ModifyInputsContext<'a> {
/// Creates an artboard as the primary export for the document network
pub fn create_artboard(&mut self, new_id: NodeId, artboard: Artboard) -> LayerNodeIdentifier {
let artboard_node_template = resolve_document_node_type("Artboard").expect("Node").node_template_input_override([
Some(NodeInput::value(TaggedValue::ArtboardGroup(graphene_std::ArtboardGroup::default()), true)),
Some(NodeInput::value(TaggedValue::ArtboardGroup(graphene_std::ArtboardGroupTable::default()), true)),
Some(NodeInput::value(TaggedValue::GraphicGroup(graphene_core::GraphicGroupTable::default()), true)),
Some(NodeInput::value(TaggedValue::IVec2(artboard.location), false)),
Some(NodeInput::value(TaggedValue::IVec2(artboard.dimensions), false)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
manual_composition: Some(concrete!(Context)),
inputs: vec![
NodeInput::network(graphene_core::Type::Fn(Box::new(concrete!(Context)), Box::new(concrete!(ArtboardGroup))), 0),
NodeInput::network(graphene_core::Type::Fn(Box::new(concrete!(Context)), Box::new(concrete!(ArtboardGroupTable))), 0),
NodeInput::node(NodeId(1), 0),
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::DocumentNodePath),
],
Expand All @@ -310,7 +310,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::ArtboardGroup(ArtboardGroup::default()), true),
NodeInput::value(TaggedValue::ArtboardGroup(ArtboardGroupTable::default()), true),
NodeInput::value(TaggedValue::GraphicGroup(GraphicGroupTable::default()), true),
NodeInput::value(TaggedValue::IVec2(glam::IVec2::ZERO), false),
NodeInput::value(TaggedValue::IVec2(glam::IVec2::new(1920, 1080)), false),
Expand Down Expand Up @@ -544,7 +544,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
nodes: [
DocumentNode {
inputs: vec![NodeInput::network(concrete!(ImageFrameTable<Color>), 0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, ImageFrame>")), // TODO: Possibly change `ImageFrame` to something else
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IntoNode<_, ImageFrameTable>")),
..Default::default()
},
DocumentNode {
Expand All @@ -571,7 +571,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.collect(),
..Default::default()
}),
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true)],
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
Expand Down Expand Up @@ -809,8 +809,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::proto("graphene_std::raster::MaskImageNode"),
inputs: vec![
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
],
..Default::default()
},
Expand All @@ -832,8 +832,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::proto("graphene_std::raster::InsertChannelNode"),
inputs: vec![
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::RedGreenBlue(RedGreenBlue::default()), false),
],
..Default::default()
Expand All @@ -856,10 +856,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
implementation: DocumentNodeImplementation::proto("graphene_std::raster::CombineChannelsNode"),
inputs: vec![
NodeInput::value(TaggedValue::None, false),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
],
..Default::default()
},
Expand Down Expand Up @@ -929,7 +929,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {

..Default::default()
}),
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true)],
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
Expand Down Expand Up @@ -1011,8 +1011,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::BrushStrokes(Vec::new()), false),
NodeInput::value(TaggedValue::BrushCache(BrushCache::new_proto()), false),
],
Expand Down Expand Up @@ -1061,7 +1061,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::proto("graphene_core::memo::MemoNode"),
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true)],
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true)],
manual_composition: Some(concrete!(Context)),
..Default::default()
},
Expand All @@ -1080,7 +1080,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::proto("graphene_core::memo::ImpureMemoNode"),
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true)],
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true)],
manual_composition: Some(concrete!(Context)),
..Default::default()
},
Expand Down Expand Up @@ -1112,7 +1112,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.collect(),
..Default::default()
}),
inputs: vec![NodeInput::value(TaggedValue::None, false), NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), false)],
inputs: vec![
NodeInput::value(TaggedValue::None, false),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), false),
],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
Expand Down Expand Up @@ -1825,7 +1828,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.collect(),
..Default::default()
}),
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true)],
inputs: vec![NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
Expand Down Expand Up @@ -1881,7 +1884,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::proto("graphene_std::executor::MapGpuSingleImageNode"),
inputs: vec![
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::DocumentNode(DocumentNode::default()), true),
],
..Default::default()
Expand Down Expand Up @@ -1923,7 +1926,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::proto("graphene_core::raster::BrightnessContrastNode"),
inputs: vec![
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::value(TaggedValue::F64(0.), false),
NodeInput::value(TaggedValue::F64(0.), false),
NodeInput::value(TaggedValue::Bool(false), false),
Expand Down Expand Up @@ -2799,7 +2802,7 @@ pub static IMAGINATE_NODE: Lazy<DocumentNodeDefinition> = Lazy::new(|| DocumentN
..Default::default()
}),
inputs: vec![
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::empty()), true),
NodeInput::value(TaggedValue::ImageFrame(ImageFrameTable::one_empty_image()), true),
NodeInput::scope("editor-api"),
NodeInput::value(TaggedValue::ImaginateController(Default::default()), false),
NodeInput::value(TaggedValue::F64(0.), false), // Remember to keep index used in `ImaginateRandom` updated with this entry's index
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,8 @@ impl NodeNetworkInterface {
InputConnector::Node { node_id, input_index } => (node_id, input_index),
InputConnector::Export(export_index) => {
let Some((encapsulating_node_id, encapsulating_node_id_path)) = network_path.split_last() else {
// The outermost network export defaults to an ArtboardGroup.
return Some((concrete!(graphene_core::ArtboardGroup), TypeSource::OuterMostExportDefault));
// The outermost network export defaults to an ArtboardGroupTable.
return Some((concrete!(graphene_core::ArtboardGroupTable), TypeSource::OuterMostExportDefault));
};

let output_type = self.output_types(encapsulating_node_id, encapsulating_node_id_path).into_iter().nth(export_index).flatten();
Expand Down
5 changes: 5 additions & 0 deletions node-graph/gcore/src/application_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ impl TransformMut for SurfaceFrame {
}
}

#[cfg(feature = "dyn-any")]
unsafe impl StaticType for SurfaceFrame {
type Static = SurfaceFrame;
}
Expand Down Expand Up @@ -90,6 +91,7 @@ impl PartialEq for ImageTexture {
}
}

#[cfg(feature = "dyn-any")]
unsafe impl StaticType for ImageTexture {
type Static = ImageTexture;
}
Expand Down Expand Up @@ -128,6 +130,7 @@ impl<S: Size> Size for SurfaceHandle<S> {
}
}

#[cfg(feature = "dyn-any")]
unsafe impl<T: 'static> StaticType for SurfaceHandle<T> {
type Static = SurfaceHandle<T>;
}
Expand All @@ -138,6 +141,7 @@ pub struct SurfaceHandleFrame<Surface> {
pub transform: DAffine2,
}

#[cfg(feature = "dyn-any")]
unsafe impl<T: 'static> StaticType for SurfaceHandleFrame<T> {
type Static = SurfaceHandleFrame<T>;
}
Expand Down Expand Up @@ -317,6 +321,7 @@ impl<T> Debug for EditorApi<T> {
}
}

#[cfg(feature = "dyn-any")]
unsafe impl<T: StaticTypeSized> StaticType for EditorApi<T> {
type Static = EditorApi<T::Static>;
}
53 changes: 36 additions & 17 deletions node-graph/gcore/src/graphic_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ pub struct Artboard {
pub clip: bool,
}

impl Default for Artboard {
fn default() -> Self {
Self::new(IVec2::ZERO, IVec2::new(1920, 1080))
}
}

impl Artboard {
pub fn new(location: IVec2, dimensions: IVec2) -> Self {
Self {
Expand All @@ -251,23 +257,38 @@ impl Artboard {
}
}

/// Contains multiple artboards.
#[derive(Clone, Default, Debug, Hash, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ArtboardGroup {
pub artboards: Vec<(Artboard, Option<NodeId>)>,
}
// TODO: Eventually remove this migration document upgrade code
pub fn migrate_artboard_group<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<ArtboardGroupTable, D::Error> {
use serde::Deserialize;

impl ArtboardGroup {
pub fn new() -> Self {
Default::default()
#[derive(Clone, Default, Debug, Hash, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ArtboardGroup {
pub artboards: Vec<(Artboard, Option<NodeId>)>,
}

fn append_artboard(&mut self, artboard: Artboard, node_id: Option<NodeId>) {
self.artboards.push((artboard, node_id));
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
enum EitherFormat {
ArtboardGroup(ArtboardGroup),
ArtboardGroupTable(ArtboardGroupTable),
}

Ok(match EitherFormat::deserialize(deserializer)? {
EitherFormat::ArtboardGroup(artboard_group) => {
let mut table = ArtboardGroupTable::empty();
for (artboard, source_node_id) in artboard_group.artboards {
table.push(artboard);
*table.instances_mut().last().unwrap().source_node_id = source_node_id;
}
table
}
EitherFormat::ArtboardGroupTable(artboard_group_table) => artboard_group_table,
})
}

pub type ArtboardGroupTable = Instances<Artboard>;

#[node_macro::node(category(""))]
async fn layer(_: impl Ctx, stack: GraphicGroupTable, mut element: GraphicElement, node_path: Vec<NodeId>) -> GraphicGroupTable {
let mut stack = stack;
Expand Down Expand Up @@ -385,15 +406,13 @@ async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
}

#[node_macro::node(category(""))]
async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroup, artboard: Artboard, node_path: Vec<NodeId>) -> ArtboardGroup {
// let mut artboards = artboards.eval(ctx.clone()).await;
// let artboard = artboard.eval(ctx).await;
// let foot = ctx.footprint();
// log::debug!("{:?}", foot);
async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artboard: Artboard, node_path: Vec<NodeId>) -> ArtboardGroupTable {
// Get the penultimate element of the node path, or None if the path is too short.
// This is used to get the ID of the user-facing "Artboard" node (which encapsulates this internal "Append Artboard" node).
let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
artboards.append_artboard(artboard, encapsulating_node_id);

artboards.push(artboard);
*artboards.instances_mut().last().unwrap().source_node_id = encapsulating_node_id;

artboards
}
Expand Down
24 changes: 12 additions & 12 deletions node-graph/gcore/src/graphic_element/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::transform::{Footprint, Transform};
use crate::uuid::{generate_uuid, NodeId};
use crate::vector::style::{Fill, Stroke, ViewMode};
use crate::vector::{PointId, VectorDataTable};
use crate::{Artboard, ArtboardGroup, Color, GraphicElement, GraphicGroupTable, RasterFrame};
use crate::{Artboard, ArtboardGroupTable, Color, GraphicElement, GraphicGroupTable, RasterFrame};

use bezier_rs::Subpath;
use dyn_any::DynAny;
Expand Down Expand Up @@ -790,38 +790,38 @@ impl GraphicElementRendered for Artboard {
}
}

impl GraphicElementRendered for ArtboardGroup {
impl GraphicElementRendered for ArtboardGroupTable {
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
for (artboard, _) in &self.artboards {
artboard.render_svg(render, render_params);
for artboard in self.instances() {
artboard.instance.render_svg(render, render_params);
}
}

fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
self.artboards.iter().filter_map(|(element, _)| element.bounding_box(transform)).reduce(Quad::combine_bounds)
self.instances().filter_map(|instance| instance.instance.bounding_box(transform)).reduce(Quad::combine_bounds)
}

fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, _element_id: Option<NodeId>) {
for (artboard, element_id) in &self.artboards {
artboard.collect_metadata(metadata, footprint, *element_id);
for instance in self.instances() {
instance.instance.collect_metadata(metadata, footprint, *instance.source_node_id);
}
}

fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
for (artboard, _) in &self.artboards {
artboard.add_upstream_click_targets(click_targets);
for instance in self.instances() {
instance.instance.add_upstream_click_targets(click_targets);
}
}

#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext) {
for (artboard, _) in &self.artboards {
artboard.render_to_vello(scene, transform, context)
for instance in self.instances() {
instance.instance.render_to_vello(scene, transform, context)
}
}

fn contains_artboard(&self) -> bool {
!self.artboards.is_empty()
self.instances().count() > 0
}
}

Expand Down
Loading

0 comments on commit 0158be3

Please sign in to comment.