Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ use crate::messages::prelude::*;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::{Affine2, DAffine2, Vec2};
use graph_craft::document::NodeId;
use graphene_std::Color;
use graphene_std::Context;
use graphene_std::Graphic;
use graphene_std::blending::BlendMode;
use graphene_std::gradient::GradientStops;
use graphene_std::memo::IORecord;
use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::table::Table;
use graphene_std::vector::Vector;
use graphene_std::vector::style::{Fill, FillChoice};
use graphene_std::{Artboard, Color, Context, Graphic};
use std::any::Any;
use std::sync::Arc;

Expand Down Expand Up @@ -183,7 +181,7 @@ fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'st
return Some(table_node_id_path_layout_with_breadcrumb(&io.output, data));
}
generate_layout_downcast!(introspected_data, data, [
Table<Table<Graphic>>,
Table<Artboard>,
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Expand Down Expand Up @@ -301,6 +299,22 @@ impl<T: TableRowLayout> TableRowLayout for Table<T> {
}
}

impl TableRowLayout for Artboard {
fn type_name() -> &'static str {
"Artboard"
}
fn identifier(&self) -> String {
self.as_graphic_table().identifier()
}
// Don't put a breadcrumb for Artboard
fn layout_with_breadcrumb(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
self.value_page(data)
}
fn value_page(&self, data: &mut LayoutData) -> Vec<LayoutGroup> {
self.as_graphic_table().layout_with_breadcrumb(data)
}
}

impl TableRowLayout for Graphic {
fn type_name() -> &'static str {
"Graphic"
Expand Down Expand Up @@ -871,7 +885,7 @@ impl TableRowLayout for NodeId {
macro_rules! known_table_row_types {
($apply:ident) => {
$apply!(
Table<Table<Graphic>>,
Table<Artboard>,
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Expand Down Expand Up @@ -900,6 +914,7 @@ macro_rules! known_table_row_types {
Raster<CPU>,
Raster<GPU>,
Graphic,
Artboard,
);
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
},
DocumentNode {
inputs: vec![
NodeInput::import(graphene_std::Type::Fn(Box::new(concrete!(Context)), Box::new(concrete!(Table<Table<Graphic>>))), 0),
NodeInput::import(graphene_std::Type::Fn(Box::new(concrete!(Context)), Box::new(concrete!(Table<Artboard>))), 0),
NodeInput::node(NodeId(3), 0),
],
implementation: DocumentNodeImplementation::ProtoNode(graphic::extend::IDENTIFIER),
Expand Down
9 changes: 3 additions & 6 deletions editor/src/messages/tool/tool_messages/artboard_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,18 +611,15 @@ impl Fsm for ArtboardToolFsmState {
#[cfg(test)]
mod test_artboard {
pub use crate::test_utils::test_prelude::*;
use graphene_std::Graphic;
use graphene_std::Artboard;
use graphene_std::table::Table;

async fn get_artboards(editor: &mut EditorTestUtils) -> Table<Table<Graphic>> {
async fn get_artboards(editor: &mut EditorTestUtils) -> Table<Artboard> {
let instrumented = match editor.eval_graph().await {
Ok(instrumented) => instrumented,
Err(e) => panic!("Failed to evaluate graph: {e}"),
};
instrumented
.grab_all_input::<graphene_std::graphic::extend::NewInput<Table<graphene_std::Graphic>>>(&editor.runtime)
.flatten()
.collect()
instrumented.grab_all_input::<graphene_std::graphic::extend::NewInput<Artboard>>(&editor.runtime).flatten().collect()
}

#[derive(Debug, PartialEq)]
Expand Down
6 changes: 3 additions & 3 deletions editor/src/node_graph_executor/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use graphene_std::text::FontCache;
use graphene_std::transform::RenderQuality;
use graphene_std::vector::Vector;
use graphene_std::vector::style::RenderMode;
use graphene_std::{Context, Graphic};
use graphene_std::{Artboard, Context, Graphic};
use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta};
use interpreted_executor::util::wrap_network_in_scope;
use spin::Mutex;
Expand Down Expand Up @@ -441,7 +441,7 @@ impl NodeRuntime {
}
// Artboard thumbnail bounds come from the clipping rectangles, not the content union, since the renderer
// clips content to those rectangles so anything outside isn't visible
else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Table<Graphic>>>>() {
else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, Table<Artboard>>>() {
if update_thumbnails {
let bounds = artboard_clip_bounds(&io.output);
Self::render_thumbnail(&mut self.thumbnail_renders, parent_network_node_id, &io.output, bounds, responses)
Expand Down Expand Up @@ -522,7 +522,7 @@ impl NodeRuntime {

/// Returns the union of the artboards' clipping rectangles, used as the thumbnail bounds for an artboard layer so the
/// framing matches what's actually visible after clipping rather than the unclipped content extents.
fn artboard_clip_bounds(artboards: &Table<Table<Graphic>>) -> RenderBoundingBox {
fn artboard_clip_bounds(artboards: &Table<Artboard>) -> RenderBoundingBox {
let mut combined: Option<[DVec2; 2]> = None;
for index in 0..artboards.len() {
let location: DVec2 = artboards.attribute_cloned_or_default(graphene_std::ATTR_LOCATION, index);
Expand Down
4 changes: 2 additions & 2 deletions node-graph/graph-craft/src/document/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub use glam::{DAffine2, DVec2, IVec2, UVec2};
use graphic_types::raster_types::{CPU, Image, Raster};
use graphic_types::vector_types::vector::style::{Fill, Gradient, GradientStops, Stroke};
use graphic_types::vector_types::vector::{self, ReferencePoint};
use graphic_types::{Graphic, Vector};
use graphic_types::{Artboard, Graphic, Vector};
use raster_nodes::curve::Curve;
use rendering::RenderMetadata;
use std::fmt::Display;
Expand Down Expand Up @@ -184,7 +184,7 @@ tagged_value! {
Graphic(Table<Graphic>),
#[serde(deserialize_with = "graphic_types::artboard::migrate_artboard")] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "ArtboardGroup")]
Artboard(Table<Table<Graphic>>),
Artboard(Table<Artboard>),
#[serde(deserialize_with = "core_types::misc::migrate_color")] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "ColorTable", alias = "OptionalColor", alias = "ColorNotInTable")]
Color(Table<Color>),
Expand Down
6 changes: 3 additions & 3 deletions node-graph/interpreted-executor/src/node_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use graphene_std::table::Table;
use graphene_std::transform::Footprint;
use graphene_std::uuid::NodeId;
use graphene_std::vector::Vector;
use graphene_std::{Context, Graphic, NodeIO, NodeIOTypes, ProtoNodeIdentifier, concrete, fn_type_fut, future};
use graphene_std::{Artboard, Context, Graphic, NodeIO, NodeIOTypes, ProtoNodeIdentifier, concrete, fn_type_fut, future};
use node_registry_macros::{async_node, convert_node, into_node};
use std::collections::HashMap;
#[cfg(feature = "gpu")]
Expand Down Expand Up @@ -63,7 +63,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
// MONITOR NODES
// =============
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ()]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Table<Graphic>>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Artboard>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Graphic>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Vector>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<CPU>>]),
Expand Down Expand Up @@ -145,7 +145,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
// ==========
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => ()]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => bool]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Table<Graphic>>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Artboard>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Graphic>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Vector>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Table<Raster<CPU>>]),
Expand Down
86 changes: 65 additions & 21 deletions node-graph/libraries/graphic-types/src/artboard.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,71 @@
use crate::graphic::Graphic;
use core_types::blending::BlendMode;
use core_types::bounds::{BoundingBox, RenderBoundingBox};
use core_types::graphene_hash::CacheHash;
use core_types::render_complexity::RenderComplexity;
use core_types::table::{Table, TableRow};
use core_types::uuid::NodeId;
use core_types::{ATTR_BACKGROUND, ATTR_CLIP, ATTR_DIMENSIONS, ATTR_LOCATION, Color};
use dyn_any::DynAny;
use glam::{DAffine2, IVec2};

// An artboard table is `Table<Table<Graphic>>`: each row's element is the artboard's content
// (a `Table<Graphic>`), with the artboard's metadata stored alongside on the row as attributes
// (see `ATTR_LOCATION`, `ATTR_DIMENSIONS`, `ATTR_BACKGROUND`, `ATTR_CLIP`).
//
// The artboard's user-visible name is the parent layer's display name (resolved live from the
// network interface via the row's `ATTR_EDITOR_LAYER_PATH` attribute) — not stored here, so it
// can never go stale.
//
// These metadata attributes are populated at runtime by the `Artboard` proto node from its
// inputs and therefore aren't persisted in document files; the proto node's input values are
// what get serialized.
/// Nominal wrapper around `Table<Graphic>` representing a single artboard's content.
///
/// Per-artboard metadata (location, dimensions, background, clip) lives as row attributes on the
/// enclosing `Table<Artboard>`, not as fields here. This keeps `Artboard` a pure type-system boundary
/// that prevents arbitrary `Table<Table<...<Graphic>>>` nesting.
#[derive(Clone, Debug, Default, CacheHash, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Artboard(Table<Graphic>);

impl Artboard {
pub fn new(content: Table<Graphic>) -> Self {
Self(content)
}

pub fn as_graphic_table(&self) -> &Table<Graphic> {
&self.0
}

pub fn as_graphic_table_mut(&mut self) -> &mut Table<Graphic> {
&mut self.0
}

pub fn into_graphic_table(self) -> Table<Graphic> {
self.0
}
}

impl From<Table<Graphic>> for Artboard {
fn from(content: Table<Graphic>) -> Self {
Self(content)
}
}

impl From<Artboard> for Table<Graphic> {
fn from(artboard: Artboard) -> Self {
artboard.0
}
}

impl BoundingBox for Artboard {
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
self.0.bounding_box(transform, include_stroke)
}

fn thumbnail_bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
self.0.thumbnail_bounding_box(transform, include_stroke)
}
}

impl RenderComplexity for Artboard {
fn render_complexity(&self) -> usize {
self.0.render_complexity()
}
}

// TODO: Eventually remove this migration document upgrade code
pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Table<Graphic>>, D::Error> {
pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Artboard>, D::Error> {
use serde::Deserialize;

/// Mirrors the removed `AlphaBlending` struct for legacy document deserialization.
Expand All @@ -33,9 +79,7 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
pub clip: bool,
}

/// Pre-migration shape of the artboard's stored data: the struct that used to live as the element
/// of `Table<Artboard>`. Kept as a private type so we can deserialize legacy documents into the new
/// `Table<Table<Graphic>>` (element = `content`, other fields → row attributes).
/// Legacy artboard struct shape, kept for deserializing old documents into `Table<Artboard>`.
#[derive(Clone, Debug, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LegacyArtboard {
Expand Down Expand Up @@ -68,14 +112,14 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
ArtboardGroup(LegacyArtboardGroup),
OldArtboardTable(OldTable<LegacyArtboard>),
LegacyArtboardTable(Table<LegacyArtboard>),
// Note: this variant must come last so older formats above are tried first; an empty
// `Table<Table<Graphic>>` would otherwise match (since `Table<T>` has the same shell across `T`).
ArtboardTable(Table<Table<Graphic>>),
// NOTE: Must come last so older tagged formats above are tried first.
// Also covers the intermediate `Table<Table<Graphic>>` shape since `Artboard` deserializes transparently.
ArtboardTable(Table<Artboard>),
}

fn legacy_to_row(legacy: LegacyArtboard) -> TableRow<Table<Graphic>> {
// Legacy `label` field is dropped the artboard's name now comes from its parent layer's display name.
TableRow::new_from_element(legacy.content)
fn legacy_to_row(legacy: LegacyArtboard) -> TableRow<Artboard> {
// Legacy `label` field is dropped (the artboard's name comes from its parent layer's display name)
TableRow::new_from_element(Artboard::new(legacy.content))
.with_attribute(ATTR_LOCATION, legacy.location.as_dvec2())
.with_attribute(ATTR_DIMENSIONS, legacy.dimensions.as_dvec2())
.with_attribute(ATTR_BACKGROUND, legacy.background)
Expand Down
1 change: 1 addition & 0 deletions node-graph/libraries/graphic-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub use raster_types;
pub use vector_types;

// Re-export commonly used types at the crate root
pub use artboard::Artboard;
pub use graphic::{Graphic, IntoGraphicTable, TryFromGraphic, Vector};

pub mod migrations {
Expand Down
15 changes: 7 additions & 8 deletions node-graph/libraries/rendering/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@ use core_types::{
use dyn_any::DynAny;
use glam::{DAffine2, DVec2};
use graphene_hash::CacheHashWrapper;
use graphic_types::Graphic;
use graphic_types::Vector;
use graphic_types::raster_types::{BitmapMut, CPU, GPU, Image, Raster};
use graphic_types::vector_types::gradient::{GradientStops, GradientType};
use graphic_types::vector_types::subpath::Subpath;
use graphic_types::vector_types::vector::click_target::{ClickTarget, FreePoint};
use graphic_types::vector_types::vector::style::{Fill, PaintOrder, RenderMode, Stroke, StrokeAlign};
use graphic_types::{Artboard, Graphic, Vector};
use kurbo::{Affine, Cap, Join, Shape};
use num_traits::Zero;
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -514,19 +513,19 @@ impl Render for Graphic {
}
}

/// Reads the artboard metadata for the row at `index` from a `Table<Table<Graphic>>` of artboards.
fn read_artboard_attributes(table: &Table<Table<Graphic>>, index: usize) -> (DVec2, DVec2, Color, bool) {
/// Reads the artboard metadata for the row at `index` from a `Table<Artboard>`.
fn read_artboard_attributes(table: &Table<Artboard>, index: usize) -> (DVec2, DVec2, Color, bool) {
let location: DVec2 = table.attribute_cloned_or_default(ATTR_LOCATION, index);
let dimensions: DVec2 = table.attribute_cloned_or_default(ATTR_DIMENSIONS, index);
let background: Color = table.attribute_cloned_or_default(ATTR_BACKGROUND, index);
let clip: bool = table.attribute_cloned_or_default(ATTR_CLIP, index);
(location, dimensions, background, clip)
}

impl Render for Table<Table<Graphic>> {
impl Render for Table<Artboard> {
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
for index in 0..self.len() {
let Some(content) = self.element(index) else { continue };
let Some(content) = self.element(index).map(Artboard::as_graphic_table) else { continue };
let (location, dimensions, background, clip) = read_artboard_attributes(self, index);

let x = location.x.min(location.x + dimensions.x);
Expand Down Expand Up @@ -584,7 +583,7 @@ impl Render for Table<Table<Graphic>> {
use vello::peniko;

for index in 0..self.len() {
let Some(content) = self.element(index) else { continue };
let Some(content) = self.element(index).map(Artboard::as_graphic_table) else { continue };
let (location, dimensions, background, clip) = read_artboard_attributes(self, index);

let [a, b] = [location, location + dimensions];
Expand Down Expand Up @@ -614,7 +613,7 @@ impl Render for Table<Table<Graphic>> {

fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, _element_id: Option<NodeId>) {
for index in 0..self.len() {
let Some(content) = self.element(index) else { continue };
let Some(content) = self.element(index).map(Artboard::as_graphic_table) else { continue };
let (location, dimensions, _background, clip) = read_artboard_attributes(self, index);

let layer_path: Table<NodeId> = self.attribute_cloned_or_default(ATTR_EDITOR_LAYER_PATH, index);
Expand Down
6 changes: 3 additions & 3 deletions node-graph/nodes/gcore/src/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core_types::transform::Footprint;
use core_types::{CacheHash, CloneVarArgs, Color, Context, Ctx, ExtractAll, ExtractAnimationTime, ExtractPointerPosition, ExtractRealTime, OwnedContextImpl};
use glam::{DAffine2, DVec2};
use graphic_types::vector_types::GradientStops;
use graphic_types::{Graphic, Vector};
use graphic_types::{Artboard, Graphic, Vector};
use raster_types::{CPU, GPU, Raster};

const DAY: f64 = 1000. * 3600. * 24.;
Expand Down Expand Up @@ -78,7 +78,7 @@ async fn quantize_real_time<T>(
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Table<Color>,
Context -> Table<Table<Graphic>>,
Context -> Table<Artboard>,
Context -> Table<GradientStops>,
Context -> Table<String>,
Context -> Table<f64>,
Expand Down Expand Up @@ -118,7 +118,7 @@ async fn quantize_animation_time<T>(
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Table<Color>,
Context -> Table<Table<Graphic>>,
Context -> Table<Artboard>,
Context -> Table<GradientStops>,
Context -> Table<String>,
Context -> Table<f64>,
Expand Down
4 changes: 2 additions & 2 deletions node-graph/nodes/gcore/src/context_modification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use core_types::uuid::NodeId;
use core_types::{Color, OwnedContextImpl};
use glam::{DAffine2, DVec2};
use graphic_types::vector_types::GradientStops;
use graphic_types::{Graphic, Vector};
use graphic_types::{Artboard, Graphic, Vector};
use raster_types::{CPU, GPU, Raster};

/// Filters out what should be unused components of the context based on the specified requirements.
Expand Down Expand Up @@ -35,7 +35,7 @@ async fn context_modification<T>(
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Table<Color>,
Context -> Table<Table<Graphic>>,
Context -> Table<Artboard>,
Context -> Table<GradientStops>,
)]
value: impl Node<Context<'static>, Output = T>,
Expand Down
Loading
Loading