Restructure node crates (#3384)

* Restructure node-graph folder

* Fix wasm compilation

* Move node definitions out of *-types crates

* Cleanup

* Fix warnings

* Fix warnings

* Start adding migrations

* Add migrations and move memo nodes to gcore

* Move nodes/gsvg-render -> rendering

* Replace some hard coded identifiers and fix automatic conversion

* Fix Vec2Value node migration

* Fix formatting

* Add more migrations

* Cleanup features

* Fix core_types::raster import

* Update demo artwork (to make profile ci work)

* Move *-types to node-graph/libraries folder

* Add missing node migrations

* Migrate more nodes

* Remove impure memo node

* More fixes and remove warning

* Migrate context and add a few missing migrations

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Dennis Kobert
2025-11-18 11:21:54 +01:00
committed by GitHub
parent 12453d2e61
commit 57b0b9c7ed
193 changed files with 3871 additions and 2720 deletions

View File

@@ -2,12 +2,12 @@ pub mod value;
use crate::document::value::TaggedValue;
use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode};
use core_types::memo::MemoHashGuard;
pub use core_types::uuid::NodeId;
pub use core_types::uuid::generate_uuid;
use core_types::{Context, ContextDependencies, Cow, MemoHash, ProtoNodeIdentifier, Type};
use dyn_any::DynAny;
use glam::IVec2;
use graphene_core::memo::MemoHashGuard;
pub use graphene_core::uuid::NodeId;
pub use graphene_core::uuid::generate_uuid;
use graphene_core::{Context, ContextDependencies, Cow, MemoHash, ProtoNodeIdentifier, Type};
use log::Metadata;
use rustc_hash::{FxBuildHasher, FxHashMap};
use std::collections::HashMap;
@@ -318,7 +318,7 @@ pub enum DocumentNodeImplementation {
impl Default for DocumentNodeImplementation {
fn default() -> Self {
Self::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode"))
Self::ProtoNode(graphene_core::ops::identity::IDENTIFIER)
}
}
@@ -475,7 +475,7 @@ pub struct OldNodeNetwork {
#[serde(alias = "outputs", deserialize_with = "deserialize_exports")] // TODO: Eventually remove this alias document upgrade code
pub exports: Vec<NodeInput>,
/// The list of all nodes in this network.
//cfg_attr(feature = "serde", #[serde(serialize_with = "graphene_core::vector::serialize_hashmap", deserialize_with = "graphene_core::vector::deserialize_hashmap"))]
//cfg_attr(feature = "serde", #[serde(serialize_with = "core_types::vector::serialize_hashmap", deserialize_with = "core_types::vector::deserialize_hashmap"))]
pub nodes: HashMap<NodeId, OldDocumentNode>,
/// Indicates whether the network is currently rendered with a particular node that is previewed, and if so, which connection should be restored when the preview ends.
#[serde(default)]
@@ -488,7 +488,7 @@ pub struct OldNodeNetwork {
/// A network may expose nodes as constants which can by used by other nodes using a `NodeInput::Scope(key)`.
#[serde(default)]
//cfg_attr(feature = "serde", #[serde(serialize_with = "graphene_core::vector::serialize_hashmap", deserialize_with = "graphene_core::vector::deserialize_hashmap"))]
//cfg_attr(feature = "serde", #[serde(serialize_with = "core_types::vector::serialize_hashmap", deserialize_with = "core_types::vector::deserialize_hashmap"))]
pub scope_injections: HashMap<String, (NodeId, Type)>,
}
@@ -520,11 +520,17 @@ pub struct NodeNetwork {
// TODO: Instead of storing import types in each NodeInput::Import connection, the types are stored here. This is similar to how types need to be defined for parameters when creating a function in Rust.
// pub import_types: Vec<Type>,
/// The list of all nodes in this network.
#[serde(serialize_with = "graphene_core::vector::serialize_hashmap", deserialize_with = "graphene_core::vector::deserialize_hashmap")]
#[serde(
serialize_with = "graphic_types::vector_types::vector::serialize_hashmap",
deserialize_with = "graphic_types::vector_types::vector::deserialize_hashmap"
)]
pub nodes: FxHashMap<NodeId, DocumentNode>,
/// A network may expose nodes as constants which can by used by other nodes using a `NodeInput::Scope(key)`.
#[serde(default)]
#[serde(serialize_with = "graphene_core::vector::serialize_hashmap", deserialize_with = "graphene_core::vector::deserialize_hashmap")]
#[serde(
serialize_with = "graphic_types::vector_types::vector::serialize_hashmap",
deserialize_with = "graphic_types::vector_types::vector::deserialize_hashmap"
)]
pub scope_injections: FxHashMap<String, (NodeId, Type)>,
#[serde(skip)]
pub generated: bool,
@@ -773,7 +779,7 @@ impl NodeNetwork {
};
// If the node is hidden, replace it with an identity node
let identity_node = DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into());
let identity_node = DocumentNodeImplementation::ProtoNode(graphene_core::ops::identity::IDENTIFIER);
if !node.visible && node.implementation != identity_node {
node.implementation = identity_node;
@@ -787,7 +793,7 @@ impl NodeNetwork {
let path = node.original_location.path.clone().unwrap_or_default();
// Replace value inputs with dedicated value nodes
if node.implementation != DocumentNodeImplementation::ProtoNode("graphene_core::value::ClonedNode".into()) {
if node.implementation != DocumentNodeImplementation::ProtoNode("core_types::value::ClonedNode".into()) {
Self::replace_value_inputs_with_nodes(&mut node.inputs, &mut self.nodes, &path, gen_id, map_ids, id);
}
@@ -923,7 +929,7 @@ impl NodeNetwork {
merged_node_id,
DocumentNode {
inputs: vec![NodeInput::Value { tagged_value, exposed }],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::value::ClonedNode".into()),
implementation: DocumentNodeImplementation::ProtoNode("core_types::value::ClonedNode".into()),
original_location,
..Default::default()
},
@@ -989,7 +995,7 @@ impl NodeNetwork {
.nodes
.iter()
.filter(|(_, node)| {
matches!(&node.implementation, DocumentNodeImplementation::ProtoNode(ident) if ident == &ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode"))
matches!(&node.implementation, DocumentNodeImplementation::ProtoNode(ident) if ident == &graphene_core::ops::identity::IDENTIFIER)
&& node.inputs.len() == 1
&& matches!(node.inputs[0], NodeInput::Node { .. })
})
@@ -1022,7 +1028,7 @@ impl NodeNetwork {
assert_eq!(output_index, 0);
// TODO: check if we can read lambda checking?
let mut input_node = self.nodes.remove(&node_id).unwrap();
node.implementation = DocumentNodeImplementation::ProtoNode("graphene_core::value::ClonedNode".into());
node.implementation = DocumentNodeImplementation::ProtoNode("core_types::value::ClonedNode".into());
if let Some(input) = input_node.inputs.get_mut(0) {
*input = match &input {
NodeInput::Node { .. } => NodeInput::import(generic!(T), 0),
@@ -1139,7 +1145,7 @@ mod test {
NodeId(0),
DocumentNode {
inputs: vec![NodeInput::import(concrete!(u32), 0), NodeInput::import(concrete!(u32), 1)],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::structural::ConsNode".into()),
implementation: DocumentNodeImplementation::ProtoNode("core_types::structural::ConsNode".into()),
..Default::default()
},
),
@@ -1147,7 +1153,7 @@ mod test {
NodeId(1),
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::ops::AddPairNode".into()),
implementation: DocumentNodeImplementation::ProtoNode("core_types::ops::AddPairNode".into()),
..Default::default()
},
),
@@ -1169,7 +1175,7 @@ mod test {
NodeId(1),
DocumentNode {
inputs: vec![NodeInput::import(concrete!(u32), 0), NodeInput::import(concrete!(u32), 1)],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::structural::ConsNode".into()),
implementation: DocumentNodeImplementation::ProtoNode("core_types::structural::ConsNode".into()),
..Default::default()
},
),
@@ -1177,7 +1183,7 @@ mod test {
NodeId(2),
DocumentNode {
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::ops::AddPairNode".into()),
implementation: DocumentNodeImplementation::ProtoNode("core_types::ops::AddPairNode".into()),
..Default::default()
},
),
@@ -1193,7 +1199,7 @@ mod test {
fn extract_node() {
let id_node = DocumentNode {
inputs: vec![],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::ops::IdentityNode".into()),
implementation: DocumentNodeImplementation::ProtoNode(graphene_core::ops::identity::IDENTIFIER),
..Default::default()
};
// TODO: Extend test cases to test nested network
@@ -1250,13 +1256,13 @@ mod test {
let document_node = DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
call_argument: concrete!(u32),
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::structural::ConsNode".into()),
implementation: DocumentNodeImplementation::ProtoNode("core_types::structural::ConsNode".into()),
..Default::default()
};
let proto_node = document_node.resolve_proto_node();
let reference = ProtoNode {
identifier: "graphene_core::structural::ConsNode".into(),
identifier: "core_types::structural::ConsNode".into(),
call_argument: concrete!(u32),
construction_args: ConstructionArgs::Nodes(vec![NodeId(0)]),
..Default::default()
@@ -1273,7 +1279,7 @@ mod test {
(
NodeId(10),
ProtoNode {
identifier: "graphene_core::structural::ConsNode".into(),
identifier: "core_types::structural::ConsNode".into(),
call_argument: concrete!(u32),
construction_args: ConstructionArgs::Nodes(vec![NodeId(14)]),
original_location: OriginalLocation {
@@ -1289,7 +1295,7 @@ mod test {
(
NodeId(11),
ProtoNode {
identifier: "graphene_core::ops::AddPairNode".into(),
identifier: "core_types::ops::AddPairNode".into(),
call_argument: concrete!(Context),
construction_args: ConstructionArgs::Nodes(vec![NodeId(10)]),
original_location: OriginalLocation {
@@ -1304,8 +1310,8 @@ mod test {
(
NodeId(14),
ProtoNode {
identifier: "graphene_core::value::ClonedNode".into(),
call_argument: concrete!(graphene_core::Context),
identifier: "core_types::value::ClonedNode".into(),
call_argument: concrete!(core_types::Context),
construction_args: ConstructionArgs::Value(TaggedValue::U32(2).into()),
original_location: OriginalLocation {
path: Some(vec![NodeId(1), NodeId(4)]),
@@ -1338,7 +1344,7 @@ mod test {
DocumentNode {
inputs: vec![NodeInput::node(NodeId(14), 0)],
call_argument: concrete!(u32),
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::structural::ConsNode".into()),
implementation: DocumentNodeImplementation::ProtoNode("core_types::structural::ConsNode".into()),
original_location: OriginalLocation {
path: Some(vec![NodeId(1), NodeId(0)]),
inputs_source: [(Source { node: vec![NodeId(1)], index: 1 }, 1)].into(),
@@ -1352,7 +1358,7 @@ mod test {
NodeId(14),
DocumentNode {
inputs: vec![NodeInput::value(TaggedValue::U32(2), false)],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::value::ClonedNode".into()),
implementation: DocumentNodeImplementation::ProtoNode("core_types::value::ClonedNode".into()),
original_location: OriginalLocation {
path: Some(vec![NodeId(1), NodeId(4)]),
inputs_source: HashMap::new(),
@@ -1366,7 +1372,7 @@ mod test {
NodeId(11),
DocumentNode {
inputs: vec![NodeInput::node(NodeId(10), 0)],
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::ops::AddPairNode".into()),
implementation: DocumentNodeImplementation::ProtoNode("core_types::ops::AddPairNode".into()),
original_location: OriginalLocation {
path: Some(vec![NodeId(1), NodeId(1)]),
inputs_source: HashMap::new(),

View File

@@ -1,23 +1,26 @@
use super::DocumentNode;
use crate::proto::{Any as DAny, FutureAny};
use crate::wasm_application_io::WasmEditorApi;
use brush_nodes::brush_cache::BrushCache;
use brush_nodes::brush_stroke::BrushStroke;
use core_types::table::Table;
use core_types::uuid::NodeId;
use core_types::{Color, ContextFeatures, MemoHash, Node, Type};
use dyn_any::DynAny;
pub use dyn_any::StaticType;
use glam::{Affine2, Vec2};
pub use glam::{DAffine2, DVec2, IVec2, UVec2};
use graphene_application_io::{ImageTexture, SurfaceFrame};
use graphene_brush::brush_cache::BrushCache;
use graphene_brush::brush_stroke::BrushStroke;
use graphene_core::raster::Image;
use graphene_core::raster_types::{CPU, Raster};
use graphene_core::table::Table;
use graphene_core::transform::ReferencePoint;
use graphene_core::uuid::NodeId;
use graphene_core::vector::Vector;
use graphene_core::vector::style::Fill;
use graphene_core::vector::style::GradientStops;
use graphene_core::{Artboard, Color, ContextFeatures, Graphic, MemoHash, Node, Type};
use graphene_svg_renderer::RenderMetadata;
use graphic_types::Artboard;
use graphic_types::Graphic;
use graphic_types::Vector;
use graphic_types::raster_types::Image;
use graphic_types::raster_types::{CPU, Raster};
use graphic_types::vector_types::vector;
use graphic_types::vector_types::vector::ReferencePoint;
use graphic_types::vector_types::vector::style::Fill;
use graphic_types::vector_types::vector::style::GradientStops;
use rendering::RenderMetadata;
use std::fmt::Display;
use std::hash::Hash;
use std::marker::PhantomData;
@@ -76,7 +79,7 @@ macro_rules! tagged_value {
Self::EditorApi(x) => Arc::new(x),
}
}
/// Creates a graphene_core::Type::Concrete(TypeDescriptor { .. }) with the type of the value inside the tagged value
/// Creates a core_types::Type::Concrete(TypeDescriptor { .. }) with the type of the value inside the tagged value
pub fn ty(&self) -> Type {
match self {
Self::None => concrete!(()),
@@ -186,19 +189,19 @@ tagged_value! {
// TABLE TYPES
// ===========
GraphicUnused(Graphic), // TODO: This is unused but removing it causes `cargo test` to infinitely recurse its type solving; figure out why and then remove this
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::vector::migrate_vector"))] // TODO: Eventually remove this migration document upgrade code
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphic_types::migrations::migrate_vector"))] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "VectorData")]
Vector(Table<Vector>),
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::raster::image::migrate_image_frame"))] // TODO: Eventually remove this migration document upgrade code
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphic_types::raster_types::image::migrate_image_frame"))] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "ImageFrame", alias = "RasterData", alias = "Image")]
Raster(Table<Raster<CPU>>),
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::graphic::migrate_graphic"))] // TODO: Eventually remove this migration document upgrade code
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphic_types::graphic::migrate_graphic"))] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "GraphicGroup", alias = "Group")]
Graphic(Table<Graphic>),
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::artboard::migrate_artboard"))] // TODO: Eventually remove this migration document upgrade code
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphic_types::artboard::migrate_artboard"))] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "ArtboardGroup")]
Artboard(Table<Artboard>),
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "graphene_core::misc::migrate_color"))] // TODO: Eventually remove this migration document upgrade code
#[cfg_attr(target_family = "wasm", serde(deserialize_with = "core_types::misc::migrate_color"))] // TODO: Eventually remove this migration document upgrade code
#[serde(alias = "ColorTable", alias = "OptionalColor")]
Color(Table<Color>),
GradientTable(Table<GradientStops>),
@@ -210,52 +213,52 @@ tagged_value! {
#[serde(alias = "IVec2", alias = "UVec2")]
DVec2(DVec2),
DAffine2(DAffine2),
Stroke(graphene_core::vector::style::Stroke),
Gradient(graphene_core::vector::style::Gradient),
Stroke(graphic_types::vector_types::vector::style::Stroke),
Gradient(graphic_types::vector_types::vector::style::Gradient),
#[serde(alias = "GradientPositions")] // TODO: Eventually remove this alias document upgrade code
GradientStops(GradientStops),
Font(graphene_core::text::Font),
Font(text_nodes::Font),
BrushStrokes(Vec<BrushStroke>),
BrushCache(BrushCache),
DocumentNode(DocumentNode),
ContextFeatures(ContextFeatures),
Curve(graphene_raster_nodes::curve::Curve),
Footprint(graphene_core::transform::Footprint),
VectorModification(Box<graphene_core::vector::VectorModification>),
Curve(raster_nodes::curve::Curve),
Footprint(core_types::transform::Footprint),
VectorModification(Box<vector::VectorModification>),
// ==========
// ENUM TYPES
// ==========
Fill(graphene_core::vector::style::Fill),
BlendMode(graphene_core::blending::BlendMode),
LuminanceCalculation(graphene_raster_nodes::adjustments::LuminanceCalculation),
Fill(vector::style::Fill),
BlendMode(core_types::blending::BlendMode),
LuminanceCalculation(raster_nodes::adjustments::LuminanceCalculation),
XY(graphene_core::extract_xy::XY),
RedGreenBlue(graphene_raster_nodes::adjustments::RedGreenBlue),
RedGreenBlueAlpha(graphene_raster_nodes::adjustments::RedGreenBlueAlpha),
RedGreenBlue(raster_nodes::adjustments::RedGreenBlue),
RedGreenBlueAlpha(raster_nodes::adjustments::RedGreenBlueAlpha),
RealTimeMode(graphene_core::animation::RealTimeMode),
NoiseType(graphene_raster_nodes::adjustments::NoiseType),
FractalType(graphene_raster_nodes::adjustments::FractalType),
CellularDistanceFunction(graphene_raster_nodes::adjustments::CellularDistanceFunction),
CellularReturnType(graphene_raster_nodes::adjustments::CellularReturnType),
DomainWarpType(graphene_raster_nodes::adjustments::DomainWarpType),
RelativeAbsolute(graphene_raster_nodes::adjustments::RelativeAbsolute),
SelectiveColorChoice(graphene_raster_nodes::adjustments::SelectiveColorChoice),
GridType(graphene_core::vector::misc::GridType),
ArcType(graphene_core::vector::misc::ArcType),
MergeByDistanceAlgorithm(graphene_core::vector::misc::MergeByDistanceAlgorithm),
PointSpacingType(graphene_core::vector::misc::PointSpacingType),
SpiralType(graphene_core::vector::misc::SpiralType),
NoiseType(raster_nodes::adjustments::NoiseType),
FractalType(raster_nodes::adjustments::FractalType),
CellularDistanceFunction(raster_nodes::adjustments::CellularDistanceFunction),
CellularReturnType(raster_nodes::adjustments::CellularReturnType),
DomainWarpType(raster_nodes::adjustments::DomainWarpType),
RelativeAbsolute(raster_nodes::adjustments::RelativeAbsolute),
SelectiveColorChoice(raster_nodes::adjustments::SelectiveColorChoice),
GridType(vector::misc::GridType),
ArcType(vector::misc::ArcType),
MergeByDistanceAlgorithm(vector::misc::MergeByDistanceAlgorithm),
PointSpacingType(vector::misc::PointSpacingType),
SpiralType(vector::misc::SpiralType),
#[serde(alias = "LineCap")]
StrokeCap(graphene_core::vector::style::StrokeCap),
StrokeCap(vector::style::StrokeCap),
#[serde(alias = "LineJoin")]
StrokeJoin(graphene_core::vector::style::StrokeJoin),
StrokeAlign(graphene_core::vector::style::StrokeAlign),
PaintOrder(graphene_core::vector::style::PaintOrder),
FillType(graphene_core::vector::style::FillType),
GradientType(graphene_core::vector::style::GradientType),
ReferencePoint(graphene_core::transform::ReferencePoint),
CentroidType(graphene_core::vector::misc::CentroidType),
BooleanOperation(graphene_path_bool::BooleanOperation),
TextAlign(graphene_core::text::TextAlign),
StrokeJoin(vector::style::StrokeJoin),
StrokeAlign(vector::style::StrokeAlign),
PaintOrder(vector::style::PaintOrder),
FillType(vector::style::FillType),
GradientType(vector::style::GradientType),
ReferencePoint(vector::ReferencePoint),
CentroidType(vector::misc::CentroidType),
BooleanOperation(path_bool_nodes::BooleanOperation),
TextAlign(text_nodes::TextAlign),
}
impl TaggedValue {

View File

@@ -1,9 +1,9 @@
#[macro_use]
extern crate log;
#[macro_use]
extern crate graphene_core;
extern crate core_types;
pub use graphene_core::{ProtoNodeIdentifier, Type, TypeDescriptor, concrete, generic};
pub use core_types::{ProtoNodeIdentifier, Type, TypeDescriptor, concrete, generic};
pub mod document;
pub mod graphene_compiler;

View File

@@ -1,8 +1,8 @@
use crate::document::value::TaggedValue;
use crate::document::{InlineRust, value};
use crate::document::{NodeId, OriginalLocation};
pub use graphene_core::registry::*;
use graphene_core::*;
pub use core_types::registry::*;
use core_types::*;
use rustc_hash::FxHashMap;
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
@@ -139,7 +139,7 @@ pub struct ProtoNode {
impl Default for ProtoNode {
fn default() -> Self {
Self {
identifier: ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode"),
identifier: graphene_core::ops::identity::IDENTIFIER,
construction_args: ConstructionArgs::Value(value::TaggedValue::U32(0).into()),
call_argument: concrete!(()),
original_location: OriginalLocation::default(),
@@ -175,7 +175,7 @@ impl ProtoNode {
_ => 2,
};
Self {
identifier: ProtoNodeIdentifier::new("graphene_core::value::ClonedNode"),
identifier: ProtoNodeIdentifier::new("core_types::value::ClonedNode"),
construction_args: value,
call_argument: concrete!(Context),
original_location: OriginalLocation {
@@ -340,7 +340,7 @@ impl ProtoNetwork {
ProtoNode {
construction_args: ConstructionArgs::Value(MemoHash::new(TaggedValue::ContextFeatures(context_deps))),
call_argument: concrete!(Context),
identifier: ProtoNodeIdentifier::new("graphene_core::value::ClonedNode"),
identifier: ProtoNodeIdentifier::new("core_types::value::ClonedNode"),
original_location: OriginalLocation {
path: path.clone(),
..Default::default()