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

452
Cargo.lock generated
View File

@@ -506,6 +506,18 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
[[package]]
name = "blending-nodes"
version = "0.1.0"
dependencies = [
"core-types",
"glam",
"graphic-types",
"node-macro",
"serde",
"vector-types",
]
[[package]]
name = "block"
version = "0.1.6"
@@ -552,6 +564,20 @@ dependencies = [
"cfg_aliases",
]
[[package]]
name = "brush-nodes"
version = "0.1.0"
dependencies = [
"core-types",
"dyn-any",
"glam",
"node-macro",
"raster-nodes",
"raster-types",
"serde",
"tokio",
]
[[package]]
name = "built"
version = "0.7.7"
@@ -1025,6 +1051,37 @@ dependencies = [
"libc",
]
[[package]]
name = "core-types"
version = "0.1.0"
dependencies = [
"base64",
"bitflags 2.9.3",
"bytemuck",
"ctor",
"dyn-any",
"glam",
"image",
"kurbo 0.12.0",
"log",
"lyon_geom",
"no-std-types",
"node-macro",
"num-traits",
"parley",
"petgraph 0.7.1",
"polycool",
"rand 0.9.2",
"rand_chacha 0.9.0",
"rustc-hash 2.1.1",
"serde",
"serde_json",
"skrifa 0.36.0",
"specta",
"tinyvec",
"tokio",
]
[[package]]
name = "core_maths"
version = "0.1.1"
@@ -2048,25 +2105,28 @@ dependencies = [
name = "graph-craft"
version = "0.1.0"
dependencies = [
"brush-nodes",
"core-types",
"criterion",
"dyn-any",
"glam",
"graph-craft",
"graphene-application-io",
"graphene-brush",
"graphene-core",
"graphene-path-bool",
"graphene-raster-nodes",
"graphene-svg-renderer",
"graphic-types",
"iai-callgrind",
"js-sys",
"log",
"path-bool-nodes",
"pretty_assertions",
"raster-nodes",
"rendering",
"reqwest",
"rustc-hash 2.1.1",
"serde",
"serde_json",
"specta",
"text-nodes",
"tokio",
"url",
"wasm-bindgen",
@@ -2079,28 +2139,17 @@ dependencies = [
name = "graphene-application-io"
version = "0.1.0"
dependencies = [
"core-types",
"dyn-any",
"glam",
"graphene-core",
"log",
"serde",
"text-nodes",
"vector-types",
"web-sys",
"wgpu",
]
[[package]]
name = "graphene-brush"
version = "0.1.0"
dependencies = [
"dyn-any",
"glam",
"graphene-core",
"graphene-raster-nodes",
"node-macro",
"serde",
"tokio",
]
[[package]]
name = "graphene-cli"
version = "0.1.0"
@@ -2110,7 +2159,6 @@ dependencies = [
"fern",
"futures",
"graph-craft",
"graphene-core",
"graphene-std",
"interpreted-executor",
"log",
@@ -2124,120 +2172,16 @@ dependencies = [
name = "graphene-core"
version = "0.1.0"
dependencies = [
"base64",
"bitflags 2.9.3",
"bytemuck",
"ctor",
"core-types",
"dyn-any",
"glam",
"graphene-core-shaders",
"image",
"kurbo 0.12.0",
"graphic-types",
"log",
"lyon_geom",
"node-macro",
"num-traits",
"parley",
"petgraph 0.7.1",
"polycool",
"rand 0.9.2",
"rand_chacha 0.9.0",
"rustc-hash 2.1.1",
"raster-types",
"serde",
"serde_json",
"skrifa 0.36.0",
"specta",
"tinyvec",
"tokio",
"wgpu",
]
[[package]]
name = "graphene-core-shaders"
version = "0.1.0"
dependencies = [
"bytemuck",
"dyn-any",
"glam",
"graphene-core",
"half",
"log",
"node-macro",
"num-derive",
"num-traits",
"num_enum",
"serde",
"specta",
"spirv-std",
]
[[package]]
name = "graphene-math-nodes"
version = "0.1.0"
dependencies = [
"glam",
"graphene-core",
"log",
"math-parser",
"node-macro",
"rand 0.9.2",
]
[[package]]
name = "graphene-path-bool"
version = "0.1.0"
dependencies = [
"dyn-any",
"glam",
"graphene-core",
"log",
"node-macro",
"path-bool",
"serde",
"specta",
]
[[package]]
name = "graphene-raster-nodes"
version = "0.1.0"
dependencies = [
"bytemuck",
"dyn-any",
"fastnoise-lite",
"futures",
"glam",
"graphene-core",
"graphene-core-shaders",
"graphene-raster-nodes-shaders",
"image",
"kurbo 0.12.0",
"ndarray",
"node-macro",
"num-traits",
"num_enum",
"rand 0.9.2",
"rand_chacha 0.9.0",
"serde",
"specta",
"spirv-std",
"tokio",
"wgpu-executor",
]
[[package]]
name = "graphene-raster-nodes-shaders"
version = "0.1.0"
dependencies = [
"cargo-gpu",
"env_logger",
"log",
]
[[package]]
name = "graphene-raster-nodes-shaders-entrypoint"
version = "0.1.0"
dependencies = [
"graphene-raster-nodes",
]
[[package]]
@@ -2245,21 +2189,29 @@ name = "graphene-std"
version = "0.1.0"
dependencies = [
"base64",
"blending-nodes",
"brush-nodes",
"core-types",
"dyn-any",
"glam",
"graph-craft",
"graphene-application-io",
"graphene-brush",
"graphene-core",
"graphene-math-nodes",
"graphene-path-bool",
"graphene-raster-nodes",
"graphene-svg-renderer",
"graphic-nodes",
"graphic-types",
"image",
"log",
"math-nodes",
"node-macro",
"path-bool-nodes",
"raster-nodes",
"rendering",
"reqwest",
"text-nodes",
"tokio",
"transform-nodes",
"vector-nodes",
"vector-types",
"vello",
"vello_encoding",
"wasm-bindgen",
@@ -2269,19 +2221,32 @@ dependencies = [
]
[[package]]
name = "graphene-svg-renderer"
name = "graphic-nodes"
version = "0.1.0"
dependencies = [
"base64",
"core-types",
"dyn-any",
"glam",
"graphene-core",
"kurbo 0.12.0",
"log",
"num-traits",
"graphic-types",
"node-macro",
"raster-types",
"serde",
"usvg 0.45.1",
"vello",
"vector-types",
]
[[package]]
name = "graphic-types"
version = "0.1.0"
dependencies = [
"core-types",
"dyn-any",
"glam",
"node-macro",
"raster-types",
"serde",
"serde_json",
"specta",
"vector-types",
]
[[package]]
@@ -2962,17 +2927,18 @@ dependencies = [
name = "interpreted-executor"
version = "0.1.0"
dependencies = [
"core-types",
"criterion",
"dyn-any",
"futures",
"glam",
"graph-craft",
"graphene-core",
"graphene-path-bool",
"graphene-std",
"iai-callgrind",
"log",
"once_cell",
"path-bool-nodes",
"preprocessor",
"serde",
"wgpu-executor",
@@ -3347,6 +3313,19 @@ dependencies = [
"regex-automata",
]
[[package]]
name = "math-nodes"
version = "0.1.0"
dependencies = [
"core-types",
"glam",
"log",
"math-parser",
"node-macro",
"rand 0.9.2",
"vector-types",
]
[[package]]
name = "math-parser"
version = "0.0.0"
@@ -3603,14 +3582,33 @@ dependencies = [
"memoffset",
]
[[package]]
name = "no-std-types"
version = "0.1.0"
dependencies = [
"bytemuck",
"core-types",
"dyn-any",
"glam",
"half",
"log",
"node-macro",
"num-derive",
"num-traits",
"num_enum",
"serde",
"specta",
"spirv-std",
]
[[package]]
name = "node-macro"
version = "0.0.0"
dependencies = [
"convert_case 0.8.0",
"graphene-core",
"graphene-core-shaders",
"core-types",
"indoc",
"no-std-types",
"proc-macro-crate",
"proc-macro-error2",
"proc-macro2",
@@ -4074,6 +4072,22 @@ dependencies = [
"svg",
]
[[package]]
name = "path-bool-nodes"
version = "0.1.0"
dependencies = [
"core-types",
"dyn-any",
"glam",
"graphic-types",
"log",
"node-macro",
"path-bool",
"serde",
"specta",
"vector-types",
]
[[package]]
name = "pathdiff"
version = "0.2.3"
@@ -4604,6 +4618,68 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde"
[[package]]
name = "raster-nodes"
version = "0.1.0"
dependencies = [
"bytemuck",
"core-types",
"dyn-any",
"fastnoise-lite",
"futures",
"glam",
"image",
"kurbo 0.12.0",
"ndarray",
"no-std-types",
"node-macro",
"num-traits",
"num_enum",
"rand 0.9.2",
"rand_chacha 0.9.0",
"raster-nodes-shaders",
"raster-types",
"serde",
"specta",
"spirv-std",
"tokio",
"vector-types",
"wgpu-executor",
]
[[package]]
name = "raster-nodes-shaders"
version = "0.1.0"
dependencies = [
"cargo-gpu",
"env_logger",
"log",
]
[[package]]
name = "raster-nodes-shaders-entrypoint"
version = "0.1.0"
dependencies = [
"raster-nodes",
]
[[package]]
name = "raster-types"
version = "0.1.0"
dependencies = [
"base64",
"bytemuck",
"core-types",
"dyn-any",
"glam",
"image",
"node-macro",
"serde",
"serde_json",
"specta",
"wgpu",
]
[[package]]
name = "rav1e"
version = "0.7.1"
@@ -4786,6 +4862,24 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "rendering"
version = "0.1.0"
dependencies = [
"base64",
"core-types",
"dyn-any",
"glam",
"graphic-types",
"kurbo 0.12.0",
"log",
"num-traits",
"serde",
"usvg 0.45.1",
"vector-types",
"vello",
]
[[package]]
name = "reqwest"
version = "0.12.23"
@@ -5744,6 +5838,21 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "text-nodes"
version = "0.1.0"
dependencies = [
"core-types",
"dyn-any",
"glam",
"log",
"node-macro",
"parley",
"serde",
"skrifa 0.36.0",
"vector-types",
]
[[package]]
name = "thiserror"
version = "1.0.69"
@@ -6124,6 +6233,18 @@ dependencies = [
"tracing-log",
]
[[package]]
name = "transform-nodes"
version = "0.1.0"
dependencies = [
"core-types",
"glam",
"graphic-types",
"node-macro",
"serde",
"vector-types",
]
[[package]]
name = "try-lock"
version = "0.2.5"
@@ -6408,6 +6529,48 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vector-nodes"
version = "0.1.0"
dependencies = [
"core-types",
"dyn-any",
"futures",
"glam",
"graphene-core",
"graphic-types",
"kurbo 0.12.0",
"log",
"node-macro",
"rand 0.9.2",
"rustc-hash 2.1.1",
"serde",
"tokio",
"vector-types",
]
[[package]]
name = "vector-types"
version = "0.1.0"
dependencies = [
"bitflags 2.9.3",
"bytemuck",
"core-types",
"dyn-any",
"glam",
"kurbo 0.12.0",
"log",
"lyon_geom",
"node-macro",
"num-traits",
"petgraph 0.7.1",
"polycool",
"rustc-hash 2.1.1",
"serde",
"specta",
"tinyvec",
]
[[package]]
name = "vello"
version = "0.6.0"
@@ -6831,13 +6994,14 @@ version = "0.1.0"
dependencies = [
"anyhow",
"bytemuck",
"core-types",
"dyn-any",
"futures",
"glam",
"graphene-application-io",
"graphene-core",
"graphene-svg-renderer",
"node-macro",
"raster-types",
"rendering",
"vello",
"web-sys",
"wgpu",

View File

@@ -12,19 +12,28 @@ members = [
"libraries/dyn-any",
"libraries/path-bool",
"libraries/math-parser",
"node-graph/gapplication-io",
"node-graph/gbrush",
"node-graph/gcore",
"node-graph/gcore-shaders",
"node-graph/gmath-nodes",
"node-graph/gpath-bool",
"node-graph/libraries/application-io",
"node-graph/libraries/core-types",
"node-graph/libraries/no-std-types",
"node-graph/libraries/raster-types",
"node-graph/libraries/vector-types",
"node-graph/libraries/graphic-types",
"node-graph/libraries/rendering",
"node-graph/nodes/blending",
"node-graph/nodes/brush",
"node-graph/nodes/gcore",
"node-graph/nodes/graphic",
"node-graph/nodes/math",
"node-graph/nodes/path-bool",
"node-graph/nodes/raster",
"node-graph/nodes/raster/shaders",
"node-graph/nodes/raster/shaders/entrypoint",
"node-graph/nodes/text",
"node-graph/nodes/transform",
"node-graph/nodes/vector",
"node-graph/graph-craft",
"node-graph/graphene-cli",
"node-graph/graster-nodes",
"node-graph/graster-nodes/shaders",
"node-graph/graster-nodes/shaders/entrypoint",
"node-graph/gstd",
"node-graph/gsvg-renderer",
"node-graph/nodes/gstd",
"node-graph/interpreted-executor",
"node-graph/node-macro",
"node-graph/preprocessor",
@@ -37,18 +46,27 @@ default-members = [
"libraries/dyn-any",
"libraries/path-bool",
"libraries/math-parser",
"node-graph/gapplication-io",
"node-graph/gbrush",
"node-graph/gcore",
"node-graph/gcore-shaders",
"node-graph/graster-nodes/shaders",
"node-graph/gmath-nodes",
"node-graph/gpath-bool",
"node-graph/libraries/application-io",
"node-graph/libraries/core-types",
"node-graph/libraries/no-std-types",
"node-graph/libraries/raster-types",
"node-graph/libraries/vector-types",
"node-graph/libraries/graphic-types",
"node-graph/libraries/rendering",
"node-graph/nodes/blending",
"node-graph/nodes/brush",
"node-graph/nodes/gcore",
"node-graph/nodes/graphic",
"node-graph/nodes/math",
"node-graph/nodes/path-bool",
"node-graph/nodes/raster",
"node-graph/nodes/raster/shaders",
"node-graph/nodes/text",
"node-graph/nodes/transform",
"node-graph/nodes/vector",
"node-graph/graph-craft",
"node-graph/graphene-cli",
"node-graph/graster-nodes",
"node-graph/gstd",
"node-graph/gsvg-renderer",
"node-graph/nodes/gstd",
"node-graph/interpreted-executor",
"node-graph/node-macro",
"node-graph/preprocessor",
@@ -70,16 +88,25 @@ dyn-any = { path = "libraries/dyn-any", features = [
preprocessor = { path = "node-graph/preprocessor" }
math-parser = { path = "libraries/math-parser" }
path-bool = { path = "libraries/path-bool" }
graphene-application-io = { path = "node-graph/gapplication-io" }
graphene-brush = { path = "node-graph/gbrush" }
graphene-core = { path = "node-graph/gcore" }
graphene-core-shaders = { path = "node-graph/gcore-shaders" }
graphene-math-nodes = { path = "node-graph/gmath-nodes" }
graphene-path-bool = { path = "node-graph/gpath-bool" }
graphene-application-io = { path = "node-graph/libraries/application-io" }
core-types = { path = "node-graph/libraries/core-types" }
no-std-types = { path = "node-graph/libraries/no-std-types" }
raster-types = { path = "node-graph/libraries/raster-types" }
vector-types = { path = "node-graph/libraries/vector-types" }
graphic-types = { path = "node-graph/libraries/graphic-types" }
rendering = { path = "node-graph/libraries/rendering" }
brush-nodes = { path = "node-graph/nodes/brush" }
blending-nodes = { path = "node-graph/nodes/blending" }
graphene-core = { path = "node-graph/nodes/gcore" }
graphic-nodes = { path = "node-graph/nodes/graphic" }
text-nodes = { path = "node-graph/nodes/text" }
transform-nodes = { path = "node-graph/nodes/transform" }
vector-nodes = { path = "node-graph/nodes/vector" }
math-nodes = { path = "node-graph/nodes/math" }
path-bool-nodes = { path = "node-graph/nodes/path-bool" }
graph-craft = { path = "node-graph/graph-craft" }
graphene-raster-nodes = { path = "node-graph/graster-nodes" }
graphene-std = { path = "node-graph/gstd" }
graphene-svg-renderer = { path = "node-graph/gsvg-renderer" }
raster-nodes = { path = "node-graph/nodes/raster" }
graphene-std = { path = "node-graph/nodes/gstd" }
interpreted-executor = { path = "node-graph/interpreted-executor" }
node-macro = { path = "node-graph/node-macro" }
wgpu-executor = { path = "node-graph/wgpu-executor" }
@@ -209,9 +236,8 @@ unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spir
opt-level = 1
[profile.dev.package]
graphite-editor = { opt-level = 1 }
graphene-core-shaders = { opt-level = 1 }
graphene-std = { opt-level = 1 }
no-std-types = { opt-level = 1 }
core-types= { opt-level = 1 }
interpreted-executor = { opt-level = 1 } # This is a mitigation for https://github.com/rustwasm/wasm-pack/issues/981 which is needed because the node_registry function is too large
graphite-proc-macros = { opt-level = 1 }
image = { opt-level = 2 }
@@ -219,6 +245,7 @@ rustc-hash = { opt-level = 3 }
serde_derive = { opt-level = 1 }
specta-macros = { opt-level = 1 }
syn = { opt-level = 1 }
node-macro = { opt-level = 2 }
[profile.release]
lto = "thin"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -15,7 +15,6 @@ gpu = ["graphite-editor/gpu", "graphene-std/shader-nodes"]
# Local dependencies
graphite-editor = { path = "../../editor", features = [
"gpu",
"vello",
] }
graphene-std = { workspace = true }
graph-craft = { workspace = true }

View File

@@ -11,18 +11,16 @@ repository = "https://github.com/GraphiteEditor/Graphite"
license = "Apache-2.0"
[features]
default = ["wasm"]
default = ["wasm", "gpu" ]
wasm = ["wasm-bindgen", "graphene-std/wasm"]
gpu = ["interpreted-executor/gpu", "wgpu-executor"]
resvg = ["graphene-std/resvg"]
vello = ["graphene-std/vello", "resvg"]
[dependencies]
# Local dependencies
graphite-proc-macros = { workspace = true }
graph-craft = { workspace = true }
interpreted-executor = { workspace = true }
graphene-std = { workspace = true } # NOTE: `graphene-core` should not be added here because `graphene-std` re-exports its contents
graphene-std = { workspace = true } # NOTE: `core-types` should not be added here because `graphene-std` re-exports its contents
preprocessor = { workspace = true }
# Workspace dependencies
@@ -59,8 +57,3 @@ env_logger = { workspace = true }
futures = { workspace = true }
tokio = { workspace = true }
[lints.rust]
# TODO: figure out why we check these features when they do not exist
unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(feature, values("resvg", "vello"))',
] }

View File

@@ -449,7 +449,7 @@ impl TableRowLayout for Vector {
table_rows.push(vec![
TextLabel::new("Upstream Nested Layers").narrow(true).widget_holder(),
TextLabel::new(if self.upstream_nested_layers.is_some() {
TextLabel::new(if self.upstream_data.is_some() {
"Yes (this preserves references to its upstream nested layers for editing by tools)"
} else {
"No (this doesn't preserve references to its upstream nested layers for editing by tools)"

View File

@@ -881,24 +881,6 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
description: Cow::Borrowed("TODO"),
properties: None,
},
DocumentNodeDefinition {
identifier: "Memoize Impure",
category: "Debug",
node_template: NodeTemplate {
document_node: DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
inputs: vec![NodeInput::value(TaggedValue::Raster(Default::default()), true)],
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_metadata: vec![("Image", "TODO").into()],
output_names: vec!["Image".to_string()],
..Default::default()
},
},
description: Cow::Borrowed("TODO"),
properties: None,
},
#[cfg(feature = "gpu")]
DocumentNodeDefinition {
identifier: "Create GPU Surface",
@@ -915,7 +897,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
..Default::default()
},
]
@@ -986,7 +968,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
DocumentNode {
call_argument: generic!(T),
inputs: vec![NodeInput::node(NodeId(1), 0)],
implementation: DocumentNodeImplementation::ProtoNode(memo::impure_memo::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(memo::memo::IDENTIFIER),
..Default::default()
},
]
@@ -1073,7 +1055,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
// category: "Raster: Adjustment",
// node_template: NodeTemplate {
// document_node: DocumentNode {
// implementation: DocumentNodeImplementation::proto("graphene_core::raster::CurvesNode"),
// implementation: DocumentNodeImplementation::proto("core_types::raster::CurvesNode"),
// inputs: vec![
// NodeInput::value(TaggedValue::Raster(Default::default()), true),
// NodeInput::value(TaggedValue::Curve(Default::default()), false),

File diff suppressed because it is too large Load Diff

View File

@@ -150,10 +150,7 @@ impl NodeGraphExecutor {
viewport,
scale: viewport_scale,
time,
#[cfg(any(feature = "resvg", feature = "vello"))]
export_format: graphene_std::application_io::ExportFormat::Raster,
#[cfg(not(any(feature = "resvg", feature = "vello")))]
export_format: graphene_std::application_io::ExportFormat::Svg,
render_mode: document.render_mode,
hide_artboards: false,
for_export: false,

View File

@@ -302,7 +302,7 @@ impl NodeRuntime {
self.monitor_nodes = proto_network
.nodes
.iter()
.filter(|(_, node)| node.identifier == "graphene_core::memo::MonitorNode".into())
.filter(|(_, node)| node.identifier == graphene_std::memo::monitor::IDENTIFIER)
.map(|(_, node)| node.original_location.path.clone().unwrap_or_default())
.collect::<Vec<_>>();

View File

@@ -23,8 +23,6 @@ crate-type = ["cdylib", "rlib"]
# Local dependencies
editor = { path = "../../editor", package = "graphite-editor", features = [
"gpu",
"resvg",
"vello",
] }
graphene-std = { workspace = true }

View File

@@ -1,625 +0,0 @@
use crate::blending::AlphaBlending;
use crate::bounds::{BoundingBox, RenderBoundingBox};
use crate::gradient::GradientStops;
use crate::raster_types::{CPU, GPU, Raster};
use crate::table::{Table, TableRow};
use crate::uuid::NodeId;
use crate::vector::Vector;
use crate::{Artboard, Color, Ctx};
use dyn_any::DynAny;
use glam::{DAffine2, DVec2};
use std::hash::Hash;
/// The possible forms of graphical content that can be rendered by the Render node into either an image or SVG syntax.
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
pub enum Graphic {
Graphic(Table<Graphic>),
Vector(Table<Vector>),
RasterCPU(Table<Raster<CPU>>),
RasterGPU(Table<Raster<GPU>>),
Color(Table<Color>),
Gradient(Table<GradientStops>),
}
impl Default for Graphic {
fn default() -> Self {
Self::Graphic(Table::new())
}
}
// Graphic
impl From<Table<Graphic>> for Graphic {
fn from(graphic: Table<Graphic>) -> Self {
Graphic::Graphic(graphic)
}
}
// Vector
impl From<Vector> for Graphic {
fn from(vector: Vector) -> Self {
Graphic::Vector(Table::new_from_element(vector))
}
}
impl From<Table<Vector>> for Graphic {
fn from(vector: Table<Vector>) -> Self {
Graphic::Vector(vector)
}
}
impl From<Vector> for Table<Graphic> {
fn from(vector: Vector) -> Self {
Table::new_from_element(Graphic::Vector(Table::new_from_element(vector)))
}
}
impl From<Table<Vector>> for Table<Graphic> {
fn from(vector: Table<Vector>) -> Self {
Table::new_from_element(Graphic::Vector(vector))
}
}
// Raster<CPU>
impl From<Raster<CPU>> for Graphic {
fn from(raster: Raster<CPU>) -> Self {
Graphic::RasterCPU(Table::new_from_element(raster))
}
}
impl From<Table<Raster<CPU>>> for Graphic {
fn from(raster: Table<Raster<CPU>>) -> Self {
Graphic::RasterCPU(raster)
}
}
impl From<Raster<CPU>> for Table<Graphic> {
fn from(raster: Raster<CPU>) -> Self {
Table::new_from_element(Graphic::RasterCPU(Table::new_from_element(raster)))
}
}
impl From<Table<Raster<CPU>>> for Table<Graphic> {
fn from(raster: Table<Raster<CPU>>) -> Self {
Table::new_from_element(Graphic::RasterCPU(raster))
}
}
// Raster<GPU>
impl From<Raster<GPU>> for Graphic {
fn from(raster: Raster<GPU>) -> Self {
Graphic::RasterGPU(Table::new_from_element(raster))
}
}
impl From<Table<Raster<GPU>>> for Graphic {
fn from(raster: Table<Raster<GPU>>) -> Self {
Graphic::RasterGPU(raster)
}
}
impl From<Raster<GPU>> for Table<Graphic> {
fn from(raster: Raster<GPU>) -> Self {
Table::new_from_element(Graphic::RasterGPU(Table::new_from_element(raster)))
}
}
impl From<Table<Raster<GPU>>> for Table<Graphic> {
fn from(raster: Table<Raster<GPU>>) -> Self {
Table::new_from_element(Graphic::RasterGPU(raster))
}
}
// Color
impl From<Color> for Graphic {
fn from(color: Color) -> Self {
Graphic::Color(Table::new_from_element(color))
}
}
impl From<Table<Color>> for Graphic {
fn from(color: Table<Color>) -> Self {
Graphic::Color(color)
}
}
impl From<Color> for Table<Graphic> {
fn from(color: Color) -> Self {
Table::new_from_element(Graphic::Color(Table::new_from_element(color)))
}
}
impl From<Table<Color>> for Table<Graphic> {
fn from(color: Table<Color>) -> Self {
Table::new_from_element(Graphic::Color(color))
}
}
// Option<Color>
impl From<Option<Color>> for Graphic {
fn from(color: Option<Color>) -> Self {
if let Some(color) = color {
Graphic::Color(Table::new_from_element(color))
} else {
Graphic::default()
}
}
}
impl From<Option<Color>> for Table<Graphic> {
fn from(color: Option<Color>) -> Self {
if let Some(color) = color {
Table::new_from_element(Graphic::Color(Table::new_from_element(color)))
} else {
Table::new()
}
}
}
impl From<Table<Color>> for Option<Color> {
fn from(color: Table<Color>) -> Self {
color.into_iter().next().map(|row| row.element)
}
}
// GradientStops
impl From<GradientStops> for Graphic {
fn from(gradient: GradientStops) -> Self {
Graphic::Gradient(Table::new_from_element(gradient))
}
}
impl From<Table<GradientStops>> for Graphic {
fn from(gradient: Table<GradientStops>) -> Self {
Graphic::Gradient(gradient)
}
}
impl From<GradientStops> for Table<Graphic> {
fn from(gradient: GradientStops) -> Self {
Table::new_from_element(Graphic::Gradient(Table::new_from_element(gradient)))
}
}
impl From<Table<GradientStops>> for Table<Graphic> {
fn from(gradient: Table<GradientStops>) -> Self {
Table::new_from_element(Graphic::Gradient(gradient))
}
}
// DAffine2
impl From<DAffine2> for Graphic {
fn from(_: DAffine2) -> Self {
Graphic::default()
}
}
impl From<DAffine2> for Table<Graphic> {
fn from(_: DAffine2) -> Self {
Table::new()
}
}
impl Graphic {
pub fn as_graphic(&self) -> Option<&Table<Graphic>> {
match self {
Graphic::Graphic(graphic) => Some(graphic),
_ => None,
}
}
pub fn as_graphic_mut(&mut self) -> Option<&mut Table<Graphic>> {
match self {
Graphic::Graphic(graphic) => Some(graphic),
_ => None,
}
}
pub fn as_vector(&self) -> Option<&Table<Vector>> {
match self {
Graphic::Vector(vector) => Some(vector),
_ => None,
}
}
pub fn as_vector_mut(&mut self) -> Option<&mut Table<Vector>> {
match self {
Graphic::Vector(vector) => Some(vector),
_ => None,
}
}
pub fn as_raster(&self) -> Option<&Table<Raster<CPU>>> {
match self {
Graphic::RasterCPU(raster) => Some(raster),
_ => None,
}
}
pub fn as_raster_mut(&mut self) -> Option<&mut Table<Raster<CPU>>> {
match self {
Graphic::RasterCPU(raster) => Some(raster),
_ => None,
}
}
pub fn had_clip_enabled(&self) -> bool {
match self {
Graphic::Vector(vector) => vector.iter().all(|row| row.alpha_blending.clip),
Graphic::Graphic(graphic) => graphic.iter().all(|row| row.alpha_blending.clip),
Graphic::RasterCPU(raster) => raster.iter().all(|row| row.alpha_blending.clip),
Graphic::RasterGPU(raster) => raster.iter().all(|row| row.alpha_blending.clip),
Graphic::Color(color) => color.iter().all(|row| row.alpha_blending.clip),
Graphic::Gradient(gradient) => gradient.iter().all(|row| row.alpha_blending.clip),
}
}
pub fn can_reduce_to_clip_path(&self) -> bool {
match self {
Graphic::Vector(vector) => vector.iter().all(|row| {
let style = &row.element.style;
let alpha_blending = &row.alpha_blending;
(alpha_blending.opacity > 1. - f32::EPSILON) && style.fill().is_opaque() && style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke())
}),
_ => false,
}
}
}
impl BoundingBox for Graphic {
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
match self {
Graphic::Vector(vector) => vector.bounding_box(transform, include_stroke),
Graphic::RasterCPU(raster) => raster.bounding_box(transform, include_stroke),
Graphic::RasterGPU(raster) => raster.bounding_box(transform, include_stroke),
Graphic::Graphic(graphic) => graphic.bounding_box(transform, include_stroke),
Graphic::Color(color) => color.bounding_box(transform, include_stroke),
Graphic::Gradient(gradient) => gradient.bounding_box(transform, include_stroke),
}
}
}
/// Performs internal editor record-keeping that enables tools to target this network's layer.
/// This node associates the ID of the network's parent layer to every element of output data.
/// This technical detail may be ignored by users, and will be phased out in the future.
#[node_macro::node(category(""))]
async fn source_node_id<I: 'n + Send + Clone>(
_: impl Ctx,
#[implementations(
Table<Artboard>,
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Table<Raster<GPU>>,
Table<Color>,
Table<GradientStops>,
)]
content: Table<I>,
node_path: Vec<NodeId>,
) -> Table<I> {
// 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 parent layer node (whose network contains this internal node).
let source_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
let mut content = content;
for row in content.iter_mut() {
*row.source_node_id = source_node_id;
}
content
}
/// Joins two tables of the same type, extending the base table with the rows of the new table.
#[node_macro::node(category("General"))]
async fn extend<I: 'n + Send + Clone>(
_: impl Ctx,
/// The table whose rows will appear at the start of the extended table.
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>, Table<GradientStops>)]
base: Table<I>,
/// The table whose rows will appear at the end of the extended table.
#[expose]
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>, Table<GradientStops>)]
new: Table<I>,
) -> Table<I> {
let mut base = base;
base.extend(new);
base
}
// TODO: Eventually remove this document upgrade code
/// Performs an obsolete function as part of a migration from an older document format.
/// Users are advised to delete this node and replace it with a new one.
#[node_macro::node(category(""))]
async fn legacy_layer_extend<I: 'n + Send + Clone>(
_: impl Ctx,
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>, Table<GradientStops>)] base: Table<I>,
#[expose]
#[implementations(Table<Artboard>, Table<Graphic>, Table<Vector>, Table<Raster<CPU>>, Table<Raster<GPU>>, Table<Color>, Table<GradientStops>)]
new: Table<I>,
nested_node_path: Vec<NodeId>,
) -> Table<I> {
// 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 parent layer-style node (which encapsulates this internal node).
let source_node_id = nested_node_path.get(nested_node_path.len().wrapping_sub(2)).copied();
let mut base = base;
for row in new.into_iter() {
base.push(TableRow { source_node_id, ..row });
}
base
}
/// Nests the input graphical content in a wrapper graphic. This essentially "groups" the input.
/// The inverse of this node is 'Flatten Graphic'.
#[node_macro::node(category("General"))]
async fn wrap_graphic<T: Into<Graphic> + 'n>(
_: impl Ctx,
#[implementations(
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Table<Raster<GPU>>,
Table<Color>,
Table<GradientStops>,
DAffine2,
)]
content: T,
) -> Table<Graphic> {
Table::new_from_element(content.into())
}
/// Converts a table of graphical content into a graphic table by placing it into an element of a new wrapper graphic table.
/// If it is already a graphic table, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired.
#[node_macro::node(category("General"))]
async fn to_graphic<T: Into<Table<Graphic>> + 'n>(
_: impl Ctx,
#[implementations(
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Table<Raster<GPU>>,
Table<Color>,
Table<GradientStops>,
)]
content: T,
) -> Table<Graphic> {
content.into()
}
/// Removes a level of nesting from a graphic table, or all nesting if "Fully Flatten" is enabled.
#[node_macro::node(category("General"))]
async fn flatten_graphic(_: impl Ctx, content: Table<Graphic>, fully_flatten: bool) -> Table<Graphic> {
// TODO: Avoid mutable reference, instead return a new Table<Graphic>?
fn flatten_table(output_graphic_table: &mut Table<Graphic>, current_graphic_table: Table<Graphic>, fully_flatten: bool, recursion_depth: usize) {
for current_row in current_graphic_table.iter() {
let current_element = current_row.element.clone();
let reference = *current_row.source_node_id;
let recurse = fully_flatten || recursion_depth == 0;
match current_element {
// If we're allowed to recurse, flatten any graphics we encounter
Graphic::Graphic(mut current_element) if recurse => {
// Apply the parent graphic's transform to all child elements
for graphic in current_element.iter_mut() {
*graphic.transform = *current_row.transform * *graphic.transform;
}
flatten_table(output_graphic_table, current_element, fully_flatten, recursion_depth + 1);
}
// Push any leaf Graphic elements we encounter, which can be either Graphic table elements beyond the recursion depth, or table elements other than Graphic tables
_ => {
output_graphic_table.push(TableRow {
element: current_element,
transform: *current_row.transform,
alpha_blending: *current_row.alpha_blending,
source_node_id: reference,
});
}
}
}
}
let mut output = Table::new();
flatten_table(&mut output, content, fully_flatten, 0);
output
}
/// Converts a graphic table into a vector table by deeply flattening any vector content it contains, and discarding any non-vector content.
#[node_macro::node(category("Vector"))]
async fn flatten_vector(_: impl Ctx, content: Table<Graphic>) -> Table<Vector> {
// TODO: Avoid mutable reference, instead return a new Table<Graphic>?
fn flatten_table(output_vector_table: &mut Table<Vector>, current_graphic_table: Table<Graphic>) {
for current_graphic_row in current_graphic_table.iter() {
let current_graphic = current_graphic_row.element.clone();
let source_node_id = *current_graphic_row.source_node_id;
match current_graphic {
// If we're allowed to recurse, flatten any tables we encounter
Graphic::Graphic(mut current_graphic_table) => {
// Apply the parent graphic's transform to all child elements
for graphic in current_graphic_table.iter_mut() {
*graphic.transform = *current_graphic_row.transform * *graphic.transform;
}
flatten_table(output_vector_table, current_graphic_table);
}
// Push any leaf Vector elements we encounter
Graphic::Vector(vector_table) => {
for current_vector_row in vector_table.iter() {
output_vector_table.push(TableRow {
element: current_vector_row.element.clone(),
transform: *current_graphic_row.transform * *current_vector_row.transform,
alpha_blending: AlphaBlending {
blend_mode: current_vector_row.alpha_blending.blend_mode,
opacity: current_graphic_row.alpha_blending.opacity * current_vector_row.alpha_blending.opacity,
fill: current_vector_row.alpha_blending.fill,
clip: current_vector_row.alpha_blending.clip,
},
source_node_id,
});
}
}
_ => {}
}
}
}
let mut output = Table::new();
flatten_table(&mut output, content);
output
}
/// Returns the value at the specified index in the collection.
/// If no value exists at that index, the type's default value is returned.
#[node_macro::node(category("General"))]
fn index_elements<T: AtIndex + Clone + Default>(
_: impl Ctx,
/// The collection of data, such as a list or table.
#[implementations(
Vec<f64>,
Vec<u32>,
Vec<u64>,
Vec<DVec2>,
Vec<String>,
Table<Artboard>,
Table<Graphic>,
Table<Vector>,
Table<Raster<CPU>>,
Table<Raster<GPU>>,
Table<Color>,
Table<GradientStops>,
)]
collection: T,
/// The index of the item to retrieve, starting from 0 for the first item.
index: u32,
) -> T::Output
where
T::Output: Clone + Default,
{
collection.at_index(index as usize).unwrap_or_default()
}
pub trait AtIndex {
type Output;
fn at_index(&self, index: usize) -> Option<Self::Output>;
}
impl<T: Clone> AtIndex for Vec<T> {
type Output = T;
fn at_index(&self, index: usize) -> Option<Self::Output> {
self.get(index).cloned()
}
}
impl<T: Clone> AtIndex for Table<T> {
type Output = Table<T>;
fn at_index(&self, index: usize) -> Option<Self::Output> {
let mut result_table = Self::default();
if let Some(row) = self.iter().nth(index) {
result_table.push(row.into_cloned());
Some(result_table)
} else {
None
}
}
}
// TODO: Eventually remove this migration document upgrade code
pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Graphic>, D::Error> {
use serde::Deserialize;
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
pub struct OldGraphicGroup {
elements: Vec<(Graphic, Option<NodeId>)>,
transform: DAffine2,
alpha_blending: AlphaBlending,
}
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
pub struct GraphicGroup {
elements: Vec<(Graphic, Option<NodeId>)>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct OlderTable<T> {
id: Vec<u64>,
#[serde(alias = "instances", alias = "instance")]
element: Vec<T>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct OldTable<T> {
id: Vec<u64>,
#[serde(alias = "instances", alias = "instance")]
element: Vec<T>,
transform: Vec<DAffine2>,
alpha_blending: Vec<AlphaBlending>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
enum GraphicFormat {
OldGraphicGroup(OldGraphicGroup),
OlderTableOldGraphicGroup(OlderTable<OldGraphicGroup>),
OldTableOldGraphicGroup(OldTable<OldGraphicGroup>),
OldTableGraphicGroup(OldTable<GraphicGroup>),
Table(serde_json::Value),
}
Ok(match GraphicFormat::deserialize(deserializer)? {
GraphicFormat::OldGraphicGroup(old) => {
let mut graphic_table = Table::new();
for (graphic, source_node_id) in old.elements {
graphic_table.push(TableRow {
element: graphic,
transform: old.transform,
alpha_blending: old.alpha_blending,
source_node_id,
});
}
graphic_table
}
GraphicFormat::OlderTableOldGraphicGroup(old) => old
.element
.into_iter()
.flat_map(|element| {
element.elements.into_iter().map(move |(graphic, source_node_id)| TableRow {
element: graphic,
transform: element.transform,
alpha_blending: element.alpha_blending,
source_node_id,
})
})
.collect(),
GraphicFormat::OldTableOldGraphicGroup(old) => old
.element
.into_iter()
.flat_map(|element| {
element.elements.into_iter().map(move |(graphic, source_node_id)| TableRow {
element: graphic,
transform: element.transform,
alpha_blending: element.alpha_blending,
source_node_id,
})
})
.collect(),
GraphicFormat::OldTableGraphicGroup(old) => old
.element
.into_iter()
.flat_map(|element| {
element.elements.into_iter().map(move |(graphic, source_node_id)| TableRow {
element: graphic,
transform: Default::default(),
alpha_blending: Default::default(),
source_node_id,
})
})
.collect(),
GraphicFormat::Table(value) => {
// Try to deserialize as either table format
if let Ok(old_table) = serde_json::from_value::<Table<GraphicGroup>>(value.clone()) {
let mut graphic_table = Table::new();
for row in old_table.iter() {
for (graphic, source_node_id) in &row.element.elements {
graphic_table.push(TableRow {
element: graphic.clone(),
transform: *row.transform,
alpha_blending: *row.alpha_blending,
source_node_id: *source_node_id,
});
}
}
graphic_table
} else if let Ok(new_table) = serde_json::from_value::<Table<Graphic>>(value) {
new_table
} else {
return Err(serde::de::Error::custom("Failed to deserialize Table<Graphic>"));
}
}
})
}

View File

@@ -1,256 +0,0 @@
use crate::{Node, WasmNotSend};
use dyn_any::DynFuture;
use std::future::Future;
use std::hash::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::sync::Arc;
use std::sync::Mutex;
/// Caches the output of a given node called with a specific input.
///
/// A cache miss occurs when the Option is None. In this case, the node evaluates the inner node and memoizes (stores) the result.
///
/// A cache hit occurs when the Option is Some and has a stored hash matching the hash of the call argument. In this case, the node returns the cached value without re-evaluating the inner node.
///
/// Currently, only one input-output pair is cached. Subsequent calls with different inputs will overwrite the previous cache.
#[derive(Default)]
pub struct MemoNode<T, CachedNode> {
cache: Arc<Mutex<Option<(u64, T)>>>,
node: CachedNode,
}
impl<'i, I: Hash + 'i, T: 'i + Clone + WasmNotSend, CachedNode: 'i> Node<'i, I> for MemoNode<T, CachedNode>
where
CachedNode: for<'any_input> Node<'any_input, I>,
for<'a> <CachedNode as Node<'a, I>>::Output: Future<Output = T> + WasmNotSend,
{
// TODO: This should return a reference to the cached cached_value
// but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty accurate xD
type Output = DynFuture<'i, T>;
fn eval(&'i self, input: I) -> Self::Output {
let mut hasher = DefaultHasher::new();
input.hash(&mut hasher);
let hash = hasher.finish();
if let Some(data) = self.cache.lock().as_ref().unwrap().as_ref().and_then(|data| (data.0 == hash).then_some(data.1.clone())) {
Box::pin(async move { data })
} else {
let fut = self.node.eval(input);
let cache = self.cache.clone();
Box::pin(async move {
let value = fut.await;
*cache.lock().unwrap() = Some((hash, value.clone()));
value
})
}
}
fn reset(&self) {
self.cache.lock().unwrap().take();
}
}
impl<T, CachedNode> MemoNode<T, CachedNode> {
pub fn new(node: CachedNode) -> MemoNode<T, CachedNode> {
MemoNode { cache: Default::default(), node }
}
}
#[allow(clippy::module_inception)]
pub mod memo {
pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::MemoNode");
}
/// Caches the output of a given Node and acts as a proxy.
/// In contrast to the regular `MemoNode`, this variant ignores all input.
/// This node might result in the document not updating properly. Use with caution!
#[derive(Default)]
pub struct ImpureMemoNode<I, T, CachedNode> {
cache: Arc<Mutex<Option<T>>>,
node: CachedNode,
_phantom: std::marker::PhantomData<I>,
}
impl<'i, I: 'i, T: 'i + Clone + WasmNotSend, CachedNode: 'i> Node<'i, I> for ImpureMemoNode<I, T, CachedNode>
where
CachedNode: for<'any_input> Node<'any_input, I>,
for<'a> <CachedNode as Node<'a, I>>::Output: Future<Output = T> + WasmNotSend,
{
// TODO: This should return a reference to the cached cached_value but that requires a lot of lifetime magic
// TODO: (This was suggested by copilot but is pretty accurate xD)
type Output = DynFuture<'i, T>;
fn eval(&'i self, input: I) -> Self::Output {
if let Some(cached_value) = self.cache.lock().as_ref().unwrap().deref() {
let data = cached_value.clone();
Box::pin(async move { data })
} else {
let fut = self.node.eval(input);
let cache = self.cache.clone();
Box::pin(async move {
let value = fut.await;
*cache.lock().unwrap() = Some(value.clone());
value
})
}
}
fn reset(&self) {
self.cache.lock().unwrap().take();
}
}
impl<T, I, CachedNode> ImpureMemoNode<I, T, CachedNode> {
pub fn new(node: CachedNode) -> ImpureMemoNode<I, T, CachedNode> {
ImpureMemoNode {
cache: Default::default(),
node,
_phantom: std::marker::PhantomData,
}
}
}
pub mod impure_memo {
pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode");
}
/// Stores both what a node was called with and what it returned.
#[derive(Clone, Debug)]
pub struct IORecord<I, O> {
pub input: I,
pub output: O,
}
/// Caches the output of the last graph evaluation for introspection
#[derive(Default)]
pub struct MonitorNode<I, T, N> {
#[allow(clippy::type_complexity)]
io: Arc<Mutex<Option<Arc<IORecord<I, T>>>>>,
node: N,
}
impl<'i, T, I, N> Node<'i, I> for MonitorNode<I, T, N>
where
I: Clone + 'static + Send + Sync,
T: Clone + 'static + Send + Sync,
for<'a> N: Node<'a, I, Output: Future<Output = T> + WasmNotSend> + 'i,
{
type Output = DynFuture<'i, T>;
fn eval(&'i self, input: I) -> Self::Output {
let io = self.io.clone();
let output_fut = self.node.eval(input.clone());
Box::pin(async move {
let output = output_fut.await;
*io.lock().unwrap() = Some(Arc::new(IORecord { input, output: output.clone() }));
output
})
}
fn serialize(&self) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
let io = self.io.lock().unwrap();
(io).as_ref().map(|output| output.clone() as Arc<dyn std::any::Any + Send + Sync>)
}
}
impl<I, T, N> MonitorNode<I, T, N> {
pub fn new(node: N) -> MonitorNode<I, T, N> {
MonitorNode { io: Arc::new(Mutex::new(None)), node }
}
}
pub mod monitor {
pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::MonitorNode");
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct MemoHash<T: Hash> {
hash: u64,
value: Arc<T>,
}
impl<'de, T: serde::Deserialize<'de> + Hash> serde::Deserialize<'de> for MemoHash<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
T::deserialize(deserializer).map(|value| Self::new(value))
}
}
impl<T: Hash + serde::Serialize> serde::Serialize for MemoHash<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.value.serialize(serializer)
}
}
impl<T: Hash> MemoHash<T> {
pub fn new(value: T) -> Self {
let hash = Self::calc_hash(&value);
Self { hash, value: value.into() }
}
pub fn new_with_hash(value: T, hash: u64) -> Self {
Self { hash, value: value.into() }
}
fn calc_hash(data: &T) -> u64 {
let mut hasher = DefaultHasher::new();
data.hash(&mut hasher);
hasher.finish()
}
pub fn inner_mut(&mut self) -> MemoHashGuard<'_, T> {
MemoHashGuard { inner: self }
}
pub fn into_inner(self) -> Arc<T> {
self.value
}
pub fn hash_code(&self) -> u64 {
self.hash
}
}
impl<T: Hash> From<T> for MemoHash<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: Hash> Hash for MemoHash<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.hash.hash(state)
}
}
impl<T: Hash> Deref for MemoHash<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
pub struct MemoHashGuard<'a, T: Hash> {
inner: &'a mut MemoHash<T>,
}
impl<T: Hash> Drop for MemoHashGuard<'_, T> {
fn drop(&mut self) {
let hash = MemoHash::<T>::calc_hash(&self.inner.value);
self.inner.hash = hash;
}
}
impl<T: Hash> Deref for MemoHashGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner.value
}
}
impl<T: Hash + Clone> std::ops::DerefMut for MemoHashGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
Arc::make_mut(&mut self.inner.value)
}
}

View File

@@ -1,67 +0,0 @@
use crate::gradient::GradientStops;
use crate::raster_types::{CPU, GPU, Raster};
use crate::table::Table;
use crate::vector::Vector;
use crate::{Artboard, Color, Graphic};
pub trait RenderComplexity {
fn render_complexity(&self) -> usize {
0
}
}
impl<T: RenderComplexity> RenderComplexity for Table<T> {
fn render_complexity(&self) -> usize {
self.iter().map(|row| row.element.render_complexity()).fold(0, usize::saturating_add)
}
}
impl RenderComplexity for Artboard {
fn render_complexity(&self) -> usize {
self.content.render_complexity()
}
}
impl RenderComplexity for Graphic {
fn render_complexity(&self) -> usize {
match self {
Self::Graphic(table) => table.render_complexity(),
Self::Vector(table) => table.render_complexity(),
Self::RasterCPU(table) => table.render_complexity(),
Self::RasterGPU(table) => table.render_complexity(),
Self::Color(table) => table.render_complexity(),
Self::Gradient(table) => table.render_complexity(),
}
}
}
impl RenderComplexity for Vector {
fn render_complexity(&self) -> usize {
self.segment_domain.ids().len()
}
}
impl RenderComplexity for Raster<CPU> {
fn render_complexity(&self) -> usize {
(self.width * self.height / 500) as usize
}
}
impl RenderComplexity for Raster<GPU> {
fn render_complexity(&self) -> usize {
// GPU textures currently can't have a thumbnail
usize::MAX
}
}
impl RenderComplexity for Color {
fn render_complexity(&self) -> usize {
1
}
}
impl RenderComplexity for GradientStops {
fn render_complexity(&self) -> usize {
1
}
}

View File

@@ -5,22 +5,24 @@ edition = "2024"
license = "MIT OR Apache-2.0"
[features]
default = ["dealloc_nodes"]
dealloc_nodes = ["graphene-core/dealloc_nodes"]
default = ["dealloc_nodes", "wgpu"]
dealloc_nodes = ["core-types/dealloc_nodes"]
wgpu = ["wgpu-executor"]
tokio = ["dep:tokio"]
wayland = []
loading = ["serde_json"]
[dependencies]
# Local dependencies
dyn-any = { workspace = true }
core-types = { workspace = true }
path-bool-nodes = { workspace = true }
brush-nodes = { workspace = true }
graphene-core = { workspace = true }
graphene-path-bool = { workspace = true }
graphene-brush = { workspace = true }
graphene-application-io = { workspace = true }
graphene-svg-renderer = { workspace = true }
graphene-raster-nodes = { workspace = true }
rendering = { workspace = true }
raster-nodes = { workspace = true }
graphic-types = { workspace = true }
text-nodes = { workspace = true }
# Workspace dependencies
log = { workspace = true }

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()

View File

@@ -9,12 +9,10 @@ license = "MIT OR Apache-2.0"
[features]
default = ["wgpu"]
wgpu = ["wgpu-executor", "gpu", "graphene-std/wgpu"]
wayland = ["graphene-std/wayland"]
gpu = ["interpreted-executor/gpu", "graphene-std/gpu", "wgpu-executor"]
[dependencies]
# Local dependencies
graphene-core = { workspace = true }
graphene-std = { workspace = true }
interpreted-executor = { workspace = true }
graph-craft = { workspace = true, features = ["loading"] }

View File

@@ -6,8 +6,8 @@ use graph_craft::graphene_compiler::{Compiler, Executor};
use graph_craft::proto::ProtoNetwork;
use graph_craft::util::load_network;
use graph_craft::wasm_application_io::EditorPreferences;
use graphene_core::text::FontCache;
use graphene_std::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
use graphene_std::text_nodes::FontCache;
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::DynamicExecutor;
use interpreted_executor::util::wrap_network_in_scope;
@@ -168,8 +168,7 @@ fn fix_nodes(network: &mut NodeNetwork) {
// https://github.com/GraphiteEditor/Graphite/blob/d68f91ccca69e90e6d2df78d544d36cd1aaf348e/editor/src/messages/portfolio/portfolio_message_handler.rs#L535
// Since the CLI doesn't have the document node definitions, a less robust method of just patching the inputs is used.
DocumentNodeImplementation::ProtoNode(proto_node_identifier)
if (proto_node_identifier.name.starts_with("graphene_core::ConstructLayerNode") || proto_node_identifier.name.starts_with("graphene_core::AddArtboardNode"))
&& node.inputs.len() < 3 =>
if (proto_node_identifier.name.starts_with("core_types::ConstructLayerNode") || proto_node_identifier.name.starts_with("core_types::AddArtboardNode")) && node.inputs.len() < 3 =>
{
node.inputs.push(NodeInput::Reflection(DocumentNodeMetadata::DocumentNodePath));
}

View File

@@ -1,2 +0,0 @@
#![no_std]
pub use graphene_raster_nodes::*;

View File

@@ -1,26 +0,0 @@
pub mod any;
pub mod render_node;
pub mod text;
#[cfg(feature = "wasm")]
pub mod wasm_application_io;
pub use graphene_application_io as application_io;
pub use graphene_brush as brush;
pub use graphene_core::vector;
pub use graphene_core::*;
pub use graphene_math_nodes as math_nodes;
pub use graphene_path_bool as path_bool;
pub use graphene_raster_nodes as raster_nodes;
/// stop gap solutions until all paths have been replaced with their absolute ones
pub mod renderer {
pub use graphene_core::math::quad::Quad;
pub use graphene_core::math::rect::Rect;
pub use graphene_svg_renderer::*;
}
pub mod raster {
pub use graphene_core::raster::*;
pub use graphene_raster_nodes::adjustments::*;
pub use graphene_raster_nodes::*;
}

View File

@@ -12,9 +12,10 @@ gpu = ["graphene-std/gpu", "graphene-std/wgpu"]
# Local dependencies
graphene-std = { workspace = true }
graph-craft = { workspace = true }
wgpu-executor = { workspace = true }
graphene-core = { workspace = true }
graphene-path-bool = { workspace = true }
wgpu-executor = { workspace = true }
core-types = { workspace = true }
path-bool-nodes = { workspace = true }
dyn-any = { workspace = true }
# Workspace dependencies

View File

@@ -4,8 +4,9 @@ pub mod util;
#[cfg(test)]
mod tests {
use core_types::*;
use futures::executor::block_on;
use graphene_core::*;
use graphene_core::ops::identity;
#[test]
fn double_number() {
@@ -21,7 +22,7 @@ mod tests {
DocumentNode {
inputs: vec![],
call_argument: concrete!(u32),
implementation: DocumentNodeImplementation::ProtoNode(ops::identity::IDENTIFIER),
implementation: DocumentNodeImplementation::ProtoNode(identity::IDENTIFIER),
..Default::default()
},
),
@@ -30,7 +31,7 @@ mod tests {
NodeId(1),
DocumentNode {
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(0), 0)],
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("graphene_core::ops::AddNode")),
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::ops::AddNode")),
..Default::default()
},
),

View File

@@ -3,15 +3,7 @@ use glam::{DAffine2, DVec2, IVec2};
use graph_craft::document::DocumentNode;
use graph_craft::document::value::RenderOutput;
use graph_craft::proto::{NodeConstructor, TypeErasedBox};
use graphene_core::raster::color::Color;
use graphene_core::raster::*;
#[cfg(feature = "gpu")]
use graphene_core::raster_types::GPU;
use graphene_core::raster_types::{CPU, Raster};
use graphene_core::{Artboard, concrete};
use graphene_core::{Cow, ProtoNodeIdentifier};
use graphene_core::{NodeIO, NodeIOTypes};
use graphene_core::{fn_type_fut, future};
use graphene_std::Artboard;
use graphene_std::Context;
use graphene_std::Graphic;
#[cfg(feature = "gpu")]
@@ -20,7 +12,13 @@ use graphene_std::any::DynAnyNode;
use graphene_std::application_io::{ImageTexture, SurfaceFrame};
use graphene_std::brush::brush_cache::BrushCache;
use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::concrete;
use graphene_std::gradient::GradientStops;
#[cfg(feature = "gpu")]
use graphene_std::raster::GPU;
use graphene_std::raster::color::Color;
use graphene_std::raster::*;
use graphene_std::raster::{CPU, Raster};
use graphene_std::render_node::RenderIntermediate;
use graphene_std::table::Table;
use graphene_std::transform::Footprint;
@@ -29,6 +27,9 @@ use graphene_std::vector::Vector;
use graphene_std::wasm_application_io::WasmEditorApi;
#[cfg(feature = "gpu")]
use graphene_std::wasm_application_io::WasmSurfaceHandle;
use graphene_std::{Cow, ProtoNodeIdentifier};
use graphene_std::{NodeIO, NodeIOTypes};
use graphene_std::{fn_type_fut, future};
use node_registry_macros::{async_node, convert_node, into_node};
use once_cell::sync::Lazy;
use std::collections::HashMap;
@@ -49,10 +50,10 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
into_node!(from: Table<Raster<CPU>>, to: Table<Raster<CPU>>),
#[cfg(feature = "gpu")]
into_node!(from: Table<Raster<GPU>>, to: Table<Raster<GPU>>),
into_node!(from: Table<Vector>, to: Table<Graphic>),
into_node!(from: Table<Raster<CPU>>, to: Table<Graphic>),
convert_node!(from: Table<Vector>, to: Table<Graphic>),
convert_node!(from: Table<Raster<CPU>>, to: Table<Graphic>),
#[cfg(feature = "gpu")]
into_node!(from: Table<Raster<GPU>>, to: Table<Graphic>),
convert_node!(from: Table<Raster<GPU>>, to: Table<Graphic>),
// into_node!(from: Table<Raster<CPU>>, to: Table<Raster<SRGBA8>>),
#[cfg(feature = "gpu")]
into_node!(from: &WasmEditorApi, to: &WgpuExecutor),
@@ -93,17 +94,17 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => BlendMode]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_path_bool::BooleanOperation]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Fill]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeCap]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeJoin]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::PaintOrder]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeAlign]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Stroke]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Gradient]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::path_bool::BooleanOperation]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::Fill]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeCap]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeJoin]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::PaintOrder]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeAlign]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::Stroke]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::Gradient]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => GradientStops]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<graphene_core::uuid::NodeId>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Box<graphene_core::vector::VectorModification>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<graphene_std::uuid::NodeId>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Box<graphene_std::vector::VectorModification>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Option<f64>]),
@@ -112,18 +113,18 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => [f64; 4]]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<NodeId>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Graphic]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::text::Font]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text::Font]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Vec<BrushStroke>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => BrushCache]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => DocumentNode]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::curve::Curve]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::transform::Footprint]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::blending::BlendMode]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::Footprint]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::blending::BlendMode]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::LuminanceCalculation]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::extract_xy::XY]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::extract_xy::XY]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RedGreenBlue]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RedGreenBlueAlpha]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::animation::RealTimeMode]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::animation::RealTimeMode]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::NoiseType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::FractalType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::CellularDistanceFunction]),
@@ -131,15 +132,15 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::DomainWarpType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RelativeAbsolute]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::SelectiveColorChoice]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::GridType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::ArcType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::MergeByDistanceAlgorithm]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::PointSpacingType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::FillType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::style::GradientType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::transform::ReferencePoint]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::CentroidType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_core::text::TextAlign]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::GridType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::ArcType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::MergeByDistanceAlgorithm]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::FillType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text::TextAlign]),
// Context nullification
#[cfg(feature = "gpu")]
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => &WasmEditorApi, Context => graphene_std::ContextFeatures]),
@@ -193,23 +194,23 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Graphic]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => glam::f32::Vec2]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => glam::f32::Affine2]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Stroke]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Gradient]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::text::Font]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::Stroke]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::Gradient]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::text::Font]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Vec<BrushStroke>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => BrushCache]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => DocumentNode]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::ContextFeatures]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::curve::Curve]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::transform::Footprint]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Box<graphene_core::vector::VectorModification>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::Fill]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::blending::BlendMode]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::transform::Footprint]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Box<graphene_std::vector::VectorModification>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::Fill]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::blending::BlendMode]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::LuminanceCalculation]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::extract_xy::XY]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::extract_xy::XY]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::RedGreenBlue]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::RedGreenBlueAlpha]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::animation::RealTimeMode]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::animation::RealTimeMode]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::NoiseType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::FractalType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::CellularDistanceFunction]),
@@ -217,35 +218,21 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::DomainWarpType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::RelativeAbsolute]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::SelectiveColorChoice]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::GridType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::ArcType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::MergeByDistanceAlgorithm]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::PointSpacingType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeCap]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeJoin]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::StrokeAlign]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::PaintOrder]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::FillType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::style::GradientType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::transform::ReferencePoint]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::vector::misc::CentroidType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_path_bool::BooleanOperation]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_core::text::TextAlign]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::GridType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::ArcType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::MergeByDistanceAlgorithm]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeCap]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeJoin]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeAlign]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::PaintOrder]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::FillType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => path_bool_nodes::BooleanOperation]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::text::TextAlign]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderIntermediate]),
// =================
// IMPURE MEMO NODES
// =================
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Artboard>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Graphic>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Vector>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<CPU>>]),
#[cfg(feature = "gpu")]
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<Color>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Table<GradientStops>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => WgpuSurface]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => Option<WgpuSurface>]),
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => ImageTexture]),
// =======================
// CREATE GPU SURFACE NODE
// =======================
@@ -295,7 +282,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
let mut map: HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
for (id, entry) in graphene_core::registry::NODE_REGISTRY.lock().unwrap().iter() {
for (id, entry) in graphene_std::registry::NODE_REGISTRY.lock().unwrap().iter() {
for (constructor, types) in entry.iter() {
map.entry(id.clone()).or_default().insert(types.clone(), *constructor);
}
@@ -362,7 +349,7 @@ mod node_registry_macros {
ProtoNodeIdentifier::new(concat!["graphene_core::ops::IntoNode<", stringify!($to), ">"]),
|mut args| {
Box::pin(async move {
let node = graphene_core::ops::IntoNode::new(
let node = graphene_std::ops::IntoNode::new(
graphene_std::any::downcast_node::<Context, $from>(args.pop().unwrap()),
graphene_std::any::FutureWrapperNode::new(graphene_std::value::ClonedNode::new(std::marker::PhantomData::<$to>)),
);
@@ -371,7 +358,7 @@ mod node_registry_macros {
})
},
{
let node = graphene_core::ops::IntoNode::new(
let node = graphene_std::ops::IntoNode::new(
graphene_std::any::PanicNode::<Context, core::pin::Pin<Box<dyn core::future::Future<Output = $from> + Send>>>::new(),
graphene_std::any::FutureWrapperNode::new(graphene_std::value::ClonedNode::new(std::marker::PhantomData::<$to>)),
);
@@ -412,7 +399,7 @@ mod node_registry_macros {
|mut args| {
Box::pin(async move {
let mut args = args.drain(..);
let node = graphene_core::ops::ConvertNode::new(
let node = graphene_std::ops::ConvertNode::new(
graphene_std::any::downcast_node::<Context, $from>(args.next().expect("Convert node did not get first argument")),
graphene_std::any::downcast_node::<Context, $convert>(args.next().expect("Convert node did not get converter argument")),
graphene_std::any::FutureWrapperNode::new(graphene_std::value::ClonedNode::new(std::marker::PhantomData::<$to>))
@@ -422,7 +409,8 @@ mod node_registry_macros {
})
},
{
let node = graphene_core::ops::ConvertNode::new(
let node = graphene_std::ops::ConvertNode::new(
graphene_std::any::PanicNode::<Context, core::pin::Pin<Box<dyn core::future::Future<Output = $from> + Send>>>::new(),
graphene_std::any::PanicNode::<Context, core::pin::Pin<Box<dyn core::future::Future<Output = $convert> + Send>>>::new(),
graphene_std::any::FutureWrapperNode::new(graphene_std::value::ClonedNode::new(std::marker::PhantomData::<$to>))

View File

@@ -43,7 +43,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
},
DocumentNode {
call_argument: concrete!(Context),
inputs: vec![NodeInput::import(graphene_core::Type::Fn(Box::new(concrete!(Context)), Box::new(generic!(T))), 0)],
inputs: vec![NodeInput::import(core_types::Type::Fn(Box::new(concrete!(Context)), Box::new(generic!(T))), 0)],
implementation: DocumentNodeImplementation::ProtoNode(graphene_std::render_node::render_intermediate::IDENTIFIER),
context_features: graphene_std::ContextDependencies {
extract: ContextFeatures::VARARGS,

View File

@@ -13,7 +13,9 @@ wgpu = ["dep:wgpu"]
[dependencies]
# Local dependencies
dyn-any = { workspace = true }
graphene-core = { workspace = true }
core-types = { workspace = true }
vector-types = { workspace = true }
text-nodes = { workspace = true }
# Workspace dependencies
glam = { workspace = true }

View File

@@ -1,8 +1,6 @@
use core_types::transform::Footprint;
use dyn_any::{DynAny, StaticType, StaticTypeSized};
use glam::{DAffine2, UVec2};
use graphene_core::text::FontCache;
use graphene_core::transform::Footprint;
use graphene_core::vector::style::RenderMode;
use std::fmt::Debug;
use std::future::Future;
use std::hash::{Hash, Hasher};
@@ -10,6 +8,8 @@ use std::pin::Pin;
use std::ptr::addr_of;
use std::sync::Arc;
use std::time::Duration;
use text_nodes::FontCache;
use vector_types::vector::style::RenderMode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct SurfaceId(pub u64);

View File

@@ -1,8 +1,8 @@
[package]
name = "graphene-core"
name = "core-types"
version = "0.1.0"
edition = "2024"
description = "API definitions for Graphene"
description = "Core types and traits for Graphene node system"
authors = ["Graphite Authors <contact@graphite.rs>"]
license = "MIT OR Apache-2.0"
@@ -10,12 +10,11 @@ license = "MIT OR Apache-2.0"
default = ["serde"]
nightly = []
type_id_logging = []
wgpu = ["dep:wgpu"]
dealloc_nodes = []
[dependencies]
# Local dependencies
graphene-core-shaders = { workspace = true, features = ["std"] }
no-std-types = { workspace = true, features = ["std"] }
# Workspace dependencies
bitflags = { workspace = true }
@@ -43,7 +42,6 @@ polycool = { workspace = true }
# Optional workspace dependencies
serde = { workspace = true, optional = true }
wgpu = { workspace = true, optional = true }
[dev-dependencies]
# Workspace dependencies

View File

@@ -1,4 +1,4 @@
use crate::{Color, gradient::GradientStops};
use crate::Color;
use glam::{DAffine2, DVec2};
#[derive(Clone, Copy, Default, Debug, PartialEq)]
@@ -33,8 +33,3 @@ impl BoundingBox for Color {
RenderBoundingBox::Infinite
}
}
impl BoundingBox for GradientStops {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> RenderBoundingBox {
RenderBoundingBox::Infinite
}
}

View File

@@ -1,4 +1,4 @@
use crate::raster::Color;
use crate::Color;
// RENDERING
pub const LAYER_OUTLINE_STROKE_COLOR: Color = Color::BLACK;

View File

@@ -1,5 +1,5 @@
use crate::transform::Footprint;
pub use graphene_core_shaders::context::{ArcCtx, Ctx};
pub use no_std_types::context::{ArcCtx, Ctx};
use std::any::Any;
use std::borrow::Borrow;
use std::hash::{Hash, Hasher};

View File

@@ -1,51 +1,34 @@
#[macro_use]
extern crate log;
pub mod animation;
pub mod artboard;
pub mod blending_nodes;
pub mod bounds;
pub mod consts;
pub mod context;
pub mod context_modification;
pub mod debug;
pub mod extract_xy;
pub mod generic;
pub mod gradient;
pub mod graphic;
pub mod logic;
pub mod math;
pub mod memo;
pub mod misc;
pub mod ops;
pub mod raster;
pub mod raster_types;
pub mod registry;
pub mod render_complexity;
pub mod subpath;
pub mod table;
pub mod text;
pub mod transform;
pub mod transform_nodes;
pub mod uuid;
pub mod value;
pub mod vector;
pub use crate as graphene_core;
pub use artboard::Artboard;
pub use crate as core_types;
pub use blending::*;
pub use color::Color;
pub use context::*;
pub use ctor;
pub use dyn_any::{StaticTypeSized, WasmNotSend, WasmNotSync};
pub use graphene_core_shaders::AsU32;
pub use graphene_core_shaders::blending;
pub use graphene_core_shaders::choice_type;
pub use graphene_core_shaders::color;
pub use graphene_core_shaders::shaders;
pub use graphic::Graphic;
pub use memo::MemoHash;
pub use no_std_types::AsU32;
pub use no_std_types::blending;
pub use no_std_types::choice_type;
pub use no_std_types::color;
pub use no_std_types::shaders;
pub use num_traits;
pub use specta;
use std::any::TypeId;
use std::future::Future;
use std::pin::Pin;

View File

@@ -1,5 +1,4 @@
pub mod bbox;
pub mod math_ext;
pub mod polynomial;
pub mod quad;
pub mod rect;

View File

@@ -0,0 +1,105 @@
use std::hash::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::sync::Arc;
/// Stores both what a node was called with and what it returned.
#[derive(Clone, Debug)]
pub struct IORecord<I, O> {
pub input: I,
pub output: O,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct MemoHash<T: Hash> {
hash: u64,
value: Arc<T>,
}
impl<'de, T: serde::Deserialize<'de> + Hash> serde::Deserialize<'de> for MemoHash<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
T::deserialize(deserializer).map(|value| Self::new(value))
}
}
impl<T: Hash + serde::Serialize> serde::Serialize for MemoHash<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.value.serialize(serializer)
}
}
impl<T: Hash> MemoHash<T> {
pub fn new(value: T) -> Self {
let hash = Self::calc_hash(&value);
Self { hash, value: value.into() }
}
pub fn new_with_hash(value: T, hash: u64) -> Self {
Self { hash, value: value.into() }
}
fn calc_hash(data: &T) -> u64 {
let mut hasher = DefaultHasher::new();
data.hash(&mut hasher);
hasher.finish()
}
pub fn inner_mut(&mut self) -> MemoHashGuard<'_, T> {
MemoHashGuard { inner: self }
}
pub fn into_inner(self) -> Arc<T> {
self.value
}
pub fn hash_code(&self) -> u64 {
self.hash
}
}
impl<T: Hash> From<T> for MemoHash<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: Hash> Hash for MemoHash<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.hash.hash(state)
}
}
impl<T: Hash> Deref for MemoHash<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
pub struct MemoHashGuard<'a, T: Hash> {
inner: &'a mut MemoHash<T>,
}
impl<T: Hash> Drop for MemoHashGuard<'_, T> {
fn drop(&mut self) {
let hash = MemoHash::<T>::calc_hash(&self.inner.value);
self.inner.hash = hash;
}
}
impl<T: Hash> Deref for MemoHashGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner.value
}
}
impl<T: Hash + Clone> std::ops::DerefMut for MemoHashGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
Arc::make_mut(&mut self.inner.value)
}
}

View File

@@ -62,9 +62,9 @@ impl Clampable for DVec2 {
}
// TODO: Eventually remove this migration document upgrade code
pub fn migrate_color<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<crate::table::Table<graphene_core_shaders::color::Color>, D::Error> {
pub fn migrate_color<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<crate::table::Table<no_std_types::color::Color>, D::Error> {
use crate::table::Table;
use graphene_core_shaders::color::Color;
use no_std_types::color::Color;
use serde::Deserialize;
#[derive(serde::Serialize, serde::Deserialize)]

View File

@@ -1,16 +1,11 @@
use graphene_core_shaders::Ctx;
use crate::{ExtractFootprint, Node, transform::Footprint};
use crate::{
Node,
table::{Table, TableRow},
transform::Footprint,
};
use std::future::Future;
use std::marker::PhantomData;
// TODO: Rename to "Passthrough"
/// Passes-through the input value without changing it.
/// This is useful for rerouting wires for organization purposes.
#[node_macro::node(skip_impl)]
fn identity<'i, T: 'i + Send>(value: T) -> T {
value
}
// Type
// TODO: Document this
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
@@ -44,11 +39,6 @@ impl<'i, N: for<'a> Node<'a, I> + Clone, I: 'i> Clone for TypeNode<N, I, <N as N
}
impl<'i, N: for<'a> Node<'a, I> + Copy, I: 'i> Copy for TypeNode<N, I, <N as Node<'i, I>>::Output> {}
#[node_macro::node(skip_impl)]
fn into<'i, T: 'i + Send + Into<O>, O: 'i + Send>(_: impl Ctx, value: T, _out_ty: PhantomData<O>) -> O {
value.into()
}
/// The [`Convert`] trait allows for conversion between Rust primitive numeric types.
/// Because number casting is lossy, we cannot use the normal [`Into`] trait like we do for other types.
pub trait Convert<T, C>: Sized {
@@ -65,6 +55,27 @@ impl<T: ToString + Send> Convert<String, ()> for T {
}
}
// trait mentioning inner type in args
pub trait TableConvert<U> {
fn convert_row(self) -> U;
}
//
impl<U, T: TableConvert<U> + Send> Convert<Table<U>, ()> for Table<T> {
async fn convert(self, _: Footprint, _: ()) -> Table<U> {
let table: Table<U> = self
.into_iter()
.map(|row| TableRow {
element: row.element.convert_row(),
transform: row.transform,
alpha_blending: row.alpha_blending,
source_node_id: row.source_node_id,
})
.collect();
table
}
}
/// Implements the [`Convert`] trait for conversion between the cartesian product of Rust's primitive numeric types.
macro_rules! impl_convert {
($from:ty, $to:ty) => {
@@ -105,18 +116,3 @@ impl_convert!(i128);
impl_convert!(u128);
impl_convert!(isize);
impl_convert!(usize);
#[node_macro::node(skip_impl)]
async fn convert<'i, T: 'i + Send + Convert<O, C>, O: 'i + Send, C: 'i + Send>(ctx: impl Ctx + ExtractFootprint, value: T, converter: C, _out_ty: PhantomData<O>) -> O {
value.convert(*ctx.try_footprint().unwrap_or(&Footprint::DEFAULT), converter).await
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn identity_node() {
assert_eq!(identity(&4), &4);
}
}

View File

@@ -6,7 +6,7 @@ use std::ops::Deref;
use std::pin::Pin;
use std::sync::{LazyLock, Mutex};
pub use graphene_core_shaders::registry::types;
pub use no_std_types::registry::types;
// Translation struct between macro and definition
#[derive(Clone, Debug)]

View File

@@ -0,0 +1,21 @@
// Raster types moved to raster-types crate
use crate::Color;
use crate::table::Table;
pub trait RenderComplexity {
fn render_complexity(&self) -> usize {
0
}
}
impl<T: RenderComplexity> RenderComplexity for Table<T> {
fn render_complexity(&self) -> usize {
self.iter().map(|row| row.element.render_complexity()).fold(0, usize::saturating_add)
}
}
impl RenderComplexity for Color {
fn render_complexity(&self) -> usize {
1
}
}

View File

@@ -315,3 +315,10 @@ pub struct TableRowMut<'a, T> {
pub alpha_blending: &'a mut AlphaBlending,
pub source_node_id: &'a mut Option<NodeId>,
}
// Conversion from Table<Color> to Option<Color> - extracts first element
impl From<Table<crate::Color>> for Option<crate::Color> {
fn from(table: Table<crate::Color>) -> Self {
table.iter().nth(0).map(|row| row.element).copied()
}
}

View File

@@ -1,6 +1,4 @@
use crate::Artboard;
use crate::math::bbox::AxisAlignedBbox;
pub use crate::vector::ReferencePoint;
use core::f64;
use glam::{DAffine2, DMat2, DVec2, UVec2};
@@ -54,16 +52,6 @@ impl<T: Transform> Transform for &T {
}
}
// Implementations for Artboard
impl Transform for Artboard {
fn transform(&self) -> DAffine2 {
DAffine2::from_translation(self.location.as_dvec2())
}
fn local_pivot(&self, pivot: DVec2) -> DVec2 {
self.location.as_dvec2() + self.dimensions.as_dvec2() * pivot
}
}
// Implementations for DAffine2
impl Transform for DAffine2 {
fn transform(&self) -> DAffine2 {

View File

@@ -173,22 +173,22 @@ fn migrate_type_descriptor_names<'de, D: serde::Deserializer<'de>>(deserializer:
let name = String::deserialize(deserializer)?;
let name = match name.as_str() {
"f32" => "f64".to_string(),
"graphene_core::transform::Footprint" => "std::option::Option<std::sync::Arc<graphene_core::context::OwnedContextImpl>>".to_string(),
"graphene_core::graphic_element::GraphicGroup" => "graphene_core::table::Table<graphene_core::graphic::Graphic>".to_string(),
"graphene_core::raster::image::ImageFrame<Color>"
| "graphene_core::raster::image::ImageFrame<graphene_core::raster::color::Color>"
| "graphene_core::instances::Instances<graphene_core::raster::image::ImageFrame<Color>>"
| "graphene_core::instances::Instances<graphene_core::raster::image::ImageFrame<graphene_core::raster::color::Color>>"
| "graphene_core::instances::Instances<graphene_core::raster::image::Image<graphene_core::raster::color::Color>>" => {
"graphene_core::table::Table<graphene_core::raster::image::Image<graphene_core::raster::color::Color>>".to_string()
"grahpene_core::transform::Footprint" => "std::option::Option<std::sync::Arc<grahpene_core::context::OwnedContextImpl>>".to_string(),
"grahpene_core::graphic_element::GraphicGroup" => "grahpene_core::table::Table<grahpene_core::graphic_types::Graphic>".to_string(),
"grahpene_core::raster::image::ImageFrame<Color>"
| "grahpene_core::raster::image::ImageFrame<grahpene_core::raster::color::Color>"
| "grahpene_core::instances::Instances<grahpene_core::raster::image::ImageFrame<Color>>"
| "grahpene_core::instances::Instances<grahpene_core::raster::image::ImageFrame<grahpene_core::raster::color::Color>>"
| "grahpene_core::instances::Instances<grahpene_core::raster::image::Image<grahpene_core::raster::color::Color>>" => {
"grahpene_core::table::Table<grahpene_core::raster::image::Image<grahpene_core::raster::color::Color>>".to_string()
}
"graphene_core::vector::vector_data::VectorData"
| "graphene_core::instances::Instances<graphene_core::vector::vector_data::VectorData>"
| "graphene_core::table::Table<graphene_core::vector::vector_data::VectorData>"
| "graphene_core::table::Table<graphene_core::vector::vector_data::Vector>" => "graphene_core::table::Table<graphene_core::vector::vector_types::Vector>".to_string(),
"graphene_core::instances::Instances<graphene_core::graphic_element::Artboard>" => "graphene_core::table::Table<graphene_core::artboard::Artboard>".to_string(),
"graphene_core::vector::vector_data::modification::VectorModification" => "graphene_core::vector::vector_modification::VectorModification".to_string(),
"graphene_core::table::Table<graphene_core::graphic_element::Graphic>" => "graphene_core::table::Table<graphene_core::graphic::Graphic>".to_string(),
"grahpene_core::vector::vector_data::VectorData"
| "grahpene_core::instances::Instances<grahpene_core::vector::vector_data::VectorData>"
| "grahpene_core::table::Table<grahpene_core::vector::vector_data::VectorData>"
| "grahpene_core::table::Table<grahpene_core::vector::vector_data::Vector>" => "grahpene_core::table::Table<grahpene_core::vector::vector_types::Vector>".to_string(),
"grahpene_core::instances::Instances<grahpene_core::graphic_element::Artboard>" => "grahpene_core::table::Table<grahpene_core::artboard::Artboard>".to_string(),
"grahpene_core::vector::vector_data::modification::VectorModification" => "grahpene_core::vector::vector_modification::VectorModification".to_string(),
"grahpene_core::table::Table<grahpene_core::graphic_element::Graphic>" => "grahpene_core::table::Table<grahpene_core::graphic_types::Graphic>".to_string(),
_ => name,
};

View File

@@ -0,0 +1,26 @@
[package]
name = "graphic-types"
version = "0.1.0"
edition = "2024"
description = "Graphic types for Graphene - combines vector types with core infrastructure"
authors = ["Graphite Authors <contact@graphite.rs>"]
license = "MIT OR Apache-2.0"
[features]
default = ["serde"]
[dependencies]
# Local dependencies
core-types = { workspace = true }
raster-types = { workspace = true, features = ["wgpu"] }
vector-types = { workspace = true }
node-macro = { workspace = true }
# Workspace dependencies
dyn-any = { workspace = true }
glam = { workspace = true }
specta = { workspace = true }
serde_json = { workspace = true }
# Optional workspace dependencies
serde = { workspace = true, optional = true }

View File

@@ -1,13 +1,12 @@
use crate::blending::AlphaBlending;
use crate::bounds::{BoundingBox, RenderBoundingBox};
use crate::gradient::GradientStops;
use crate::math::quad::Quad;
use crate::raster_types::{CPU, GPU, Raster};
use crate::table::{Table, TableRow};
use crate::transform::TransformMut;
use crate::uuid::NodeId;
use crate::vector::Vector;
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, Graphic, OwnedContextImpl};
use crate::graphic::Graphic;
use core_types::Color;
use core_types::blending::AlphaBlending;
use core_types::bounds::{BoundingBox, RenderBoundingBox};
use core_types::math::quad::Quad;
use core_types::render_complexity::RenderComplexity;
use core_types::table::{Table, TableRow};
use core_types::transform::Transform;
use core_types::uuid::NodeId;
use dyn_any::DynAny;
use glam::{DAffine2, DVec2, IVec2};
use std::hash::Hash;
@@ -57,6 +56,22 @@ impl BoundingBox for Artboard {
}
}
impl RenderComplexity for Artboard {
fn render_complexity(&self) -> usize {
self.content.render_complexity()
}
}
// Implementations for Artboard
impl Transform for Artboard {
fn transform(&self) -> DAffine2 {
DAffine2::from_translation(self.location.as_dvec2())
}
fn local_pivot(&self, pivot: DVec2) -> DVec2 {
self.location.as_dvec2() + self.dimensions.as_dvec2() * pivot
}
}
// TODO: Eventually remove this migration document upgrade code
pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Artboard>, D::Error> {
use serde::Deserialize;
@@ -110,57 +125,4 @@ pub fn migrate_artboard<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Re
})
}
/// Constructs a new single artboard table with the chosen properties.
#[node_macro::node(category(""))]
async fn create_artboard<T: Into<Table<Graphic>> + 'n>(
ctx: impl ExtractAll + CloneVarArgs + Ctx,
/// Graphics to include within the artboard.
#[implementations(
Context -> Table<Graphic>,
Context -> Table<Vector>,
Context -> Table<Raster<CPU>>,
Context -> Table<Raster<GPU>>,
Context -> Table<Color>,
Context -> Table<GradientStops>,
Context -> DAffine2,
)]
content: impl Node<Context<'static>, Output = T>,
/// Name of the artboard, shown in parts of the editor.
label: String,
/// Coordinate of the top-left corner of the artboard within the document.
location: DVec2,
/// Width and height of the artboard within the document. Only integers are valid.
dimensions: DVec2,
/// Color of the artboard background. Only positive integers are valid.
background: Table<Color>,
/// Whether to cut off the contained content that extends outside the artboard, or keep it visible.
clip: bool,
) -> Table<Artboard> {
let location = location.as_ivec2();
let footprint = ctx.try_footprint().copied();
let mut new_ctx = OwnedContextImpl::from(ctx);
if let Some(mut footprint) = footprint {
footprint.translate(location.as_dvec2());
new_ctx = new_ctx.with_footprint(footprint);
}
let content = content.eval(new_ctx.into_context()).await.into();
let dimensions = dimensions.as_ivec2().max(IVec2::ONE);
let location = location.min(location + dimensions);
let dimensions = dimensions.abs();
let background: Option<Color> = background.into();
let background = background.unwrap_or(Color::WHITE);
Table::new_from_element(Artboard {
content,
label,
location,
dimensions,
background,
clip,
})
}
// Node definitions moved to graphic-nodes crate

View File

@@ -0,0 +1,421 @@
use core_types::Color;
use core_types::blending::AlphaBlending;
use core_types::bounds::{BoundingBox, RenderBoundingBox};
use core_types::ops::TableConvert;
use core_types::render_complexity::RenderComplexity;
use core_types::table::{Table, TableRow};
use core_types::uuid::NodeId;
use dyn_any::DynAny;
use glam::DAffine2;
use raster_types::{CPU, GPU, Raster};
use std::hash::Hash;
use vector_types::GradientStops;
// use vector_types::Vector;
pub type Vector = vector_types::Vector<Option<Table<Graphic>>>;
/// The possible forms of graphical content that can be rendered by the Render node into either an image or SVG syntax.
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
pub enum Graphic {
Graphic(Table<Graphic>),
Vector(Table<Vector>),
RasterCPU(Table<Raster<CPU>>),
RasterGPU(Table<Raster<GPU>>),
Color(Table<Color>),
Gradient(Table<GradientStops>),
}
impl Default for Graphic {
fn default() -> Self {
Self::Graphic(Table::new())
}
}
// Graphic
impl From<Table<Graphic>> for Graphic {
fn from(graphic: Table<Graphic>) -> Self {
Graphic::Graphic(graphic)
}
}
// Vector
impl From<Vector> for Graphic {
fn from(vector: Vector) -> Self {
Graphic::Vector(Table::new_from_element(vector))
}
}
impl From<Table<Vector>> for Graphic {
fn from(vector: Table<Vector>) -> Self {
Graphic::Vector(vector)
}
}
// Note: Table<Vector> -> Table<Graphic> conversion handled by blanket impl in gcore
// Raster<CPU>
impl From<Raster<CPU>> for Graphic {
fn from(raster: Raster<CPU>) -> Self {
Graphic::RasterCPU(Table::new_from_element(raster))
}
}
impl From<Table<Raster<CPU>>> for Graphic {
fn from(raster: Table<Raster<CPU>>) -> Self {
Graphic::RasterCPU(raster)
}
}
// Note: Table conversions handled by blanket impl in gcore
// Raster<GPU>
impl From<Raster<GPU>> for Graphic {
fn from(raster: Raster<GPU>) -> Self {
Graphic::RasterGPU(Table::new_from_element(raster))
}
}
impl From<Table<Raster<GPU>>> for Graphic {
fn from(raster: Table<Raster<GPU>>) -> Self {
Graphic::RasterGPU(raster)
}
}
// Note: Table conversions handled by blanket impl in gcore
// Color
impl From<Color> for Graphic {
fn from(color: Color) -> Self {
Graphic::Color(Table::new_from_element(color))
}
}
impl From<Table<Color>> for Graphic {
fn from(color: Table<Color>) -> Self {
Graphic::Color(color)
}
}
// Note: Table conversions handled by blanket impl in gcore
// Option<Color>
impl From<Option<Color>> for Graphic {
fn from(color: Option<Color>) -> Self {
if let Some(color) = color {
Graphic::Color(Table::new_from_element(color))
} else {
Graphic::default()
}
}
}
// Note: Table conversions handled by blanket impl in gcore
// Note: Table<Color> -> Option<Color> is in gcore (Color is defined there)
// GradientStops
impl From<GradientStops> for Graphic {
fn from(gradient: GradientStops) -> Self {
Graphic::Gradient(Table::new_from_element(gradient))
}
}
impl From<Table<GradientStops>> for Graphic {
fn from(gradient: Table<GradientStops>) -> Self {
Graphic::Gradient(gradient)
}
}
// Local trait to convert types to Table<Graphic> (avoids orphan rule issues)
pub trait IntoGraphicTable {
fn into_graphic_table(self) -> Table<Graphic>;
}
impl IntoGraphicTable for Table<Graphic> {
fn into_graphic_table(self) -> Table<Graphic> {
self
}
}
impl IntoGraphicTable for Table<Vector> {
fn into_graphic_table(self) -> Table<Graphic> {
Table::new_from_element(Graphic::Vector(self))
}
}
impl IntoGraphicTable for Table<Raster<CPU>> {
fn into_graphic_table(self) -> Table<Graphic> {
Table::new_from_element(Graphic::RasterCPU(self))
}
}
impl IntoGraphicTable for Table<Raster<GPU>> {
fn into_graphic_table(self) -> Table<Graphic> {
Table::new_from_element(Graphic::RasterGPU(self))
}
}
impl IntoGraphicTable for Table<Color> {
fn into_graphic_table(self) -> Table<Graphic> {
Table::new_from_element(Graphic::Color(self))
}
}
impl IntoGraphicTable for Table<GradientStops> {
fn into_graphic_table(self) -> Table<Graphic> {
Table::new_from_element(Graphic::Gradient(self))
}
}
impl IntoGraphicTable for DAffine2 {
fn into_graphic_table(self) -> Table<Graphic> {
Table::new_from_element(Graphic::default())
}
}
// DAffine2
impl From<DAffine2> for Graphic {
fn from(_: DAffine2) -> Self {
Graphic::default()
}
}
// Note: Table conversions handled by blanket impl in gcore
impl Graphic {
pub fn as_graphic(&self) -> Option<&Table<Graphic>> {
match self {
Graphic::Graphic(graphic) => Some(graphic),
_ => None,
}
}
pub fn as_graphic_mut(&mut self) -> Option<&mut Table<Graphic>> {
match self {
Graphic::Graphic(graphic) => Some(graphic),
_ => None,
}
}
pub fn as_vector(&self) -> Option<&Table<Vector>> {
match self {
Graphic::Vector(vector) => Some(vector),
_ => None,
}
}
pub fn as_vector_mut(&mut self) -> Option<&mut Table<Vector>> {
match self {
Graphic::Vector(vector) => Some(vector),
_ => None,
}
}
pub fn as_raster(&self) -> Option<&Table<Raster<CPU>>> {
match self {
Graphic::RasterCPU(raster) => Some(raster),
_ => None,
}
}
pub fn as_raster_mut(&mut self) -> Option<&mut Table<Raster<CPU>>> {
match self {
Graphic::RasterCPU(raster) => Some(raster),
_ => None,
}
}
pub fn had_clip_enabled(&self) -> bool {
match self {
Graphic::Vector(vector) => vector.iter().all(|row| row.alpha_blending.clip),
Graphic::Graphic(graphic) => graphic.iter().all(|row| row.alpha_blending.clip),
Graphic::RasterCPU(raster) => raster.iter().all(|row| row.alpha_blending.clip),
Graphic::RasterGPU(raster) => raster.iter().all(|row| row.alpha_blending.clip),
Graphic::Color(color) => color.iter().all(|row| row.alpha_blending.clip),
Graphic::Gradient(gradient) => gradient.iter().all(|row| row.alpha_blending.clip),
}
}
pub fn can_reduce_to_clip_path(&self) -> bool {
match self {
Graphic::Vector(vector) => vector.iter().all(|row| {
let style = &row.element.style;
let alpha_blending = &row.alpha_blending;
(alpha_blending.opacity > 1. - f32::EPSILON) && style.fill().is_opaque() && style.stroke().is_none_or(|stroke| !stroke.has_renderable_stroke())
}),
_ => false,
}
}
}
impl BoundingBox for Graphic {
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> RenderBoundingBox {
match self {
Graphic::Vector(vector) => vector.bounding_box(transform, include_stroke),
Graphic::RasterCPU(raster) => raster.bounding_box(transform, include_stroke),
Graphic::RasterGPU(raster) => raster.bounding_box(transform, include_stroke),
Graphic::Graphic(graphic) => graphic.bounding_box(transform, include_stroke),
Graphic::Color(color) => color.bounding_box(transform, include_stroke),
Graphic::Gradient(gradient) => gradient.bounding_box(transform, include_stroke),
}
}
}
impl TableConvert<Graphic> for Vector {
fn convert_row(self) -> Graphic {
Graphic::Vector(Table::new_from_element(self))
}
}
impl TableConvert<Graphic> for Raster<CPU> {
fn convert_row(self) -> Graphic {
Graphic::RasterCPU(Table::new_from_element(self))
}
}
impl TableConvert<Graphic> for Raster<GPU> {
fn convert_row(self) -> Graphic {
Graphic::RasterGPU(Table::new_from_element(self))
}
}
impl RenderComplexity for Graphic {
fn render_complexity(&self) -> usize {
match self {
Self::Graphic(table) => table.render_complexity(),
Self::Vector(table) => table.render_complexity(),
Self::RasterCPU(table) => table.render_complexity(),
Self::RasterGPU(table) => table.render_complexity(),
Self::Color(table) => table.render_complexity(),
Self::Gradient(table) => table.render_complexity(),
}
}
}
// Node definitions moved to graphic-nodes crate
pub trait AtIndex {
type Output;
fn at_index(&self, index: usize) -> Option<Self::Output>;
}
impl<T: Clone> AtIndex for Vec<T> {
type Output = T;
fn at_index(&self, index: usize) -> Option<Self::Output> {
self.get(index).cloned()
}
}
impl<T: Clone> AtIndex for Table<T> {
type Output = Table<T>;
fn at_index(&self, index: usize) -> Option<Self::Output> {
let mut result_table = Self::default();
if let Some(row) = self.iter().nth(index) {
result_table.push(row.into_cloned());
Some(result_table)
} else {
None
}
}
}
// TODO: Eventually remove this migration document upgrade code
pub fn migrate_graphic<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Graphic>, D::Error> {
use serde::Deserialize;
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
pub struct OldGraphicGroup {
elements: Vec<(Graphic, Option<NodeId>)>,
transform: DAffine2,
alpha_blending: AlphaBlending,
}
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
pub struct GraphicGroup {
elements: Vec<(Graphic, Option<NodeId>)>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct OlderTable<T> {
id: Vec<u64>,
#[serde(alias = "instances", alias = "instance")]
element: Vec<T>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct OldTable<T> {
id: Vec<u64>,
#[serde(alias = "instances", alias = "instance")]
element: Vec<T>,
transform: Vec<DAffine2>,
alpha_blending: Vec<AlphaBlending>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
enum GraphicFormat {
OldGraphicGroup(OldGraphicGroup),
OlderTableOldGraphicGroup(OlderTable<OldGraphicGroup>),
OldTableOldGraphicGroup(OldTable<OldGraphicGroup>),
OldTableGraphicGroup(OldTable<GraphicGroup>),
Table(serde_json::Value),
}
Ok(match GraphicFormat::deserialize(deserializer)? {
GraphicFormat::OldGraphicGroup(old) => {
let mut graphic_table = Table::new();
for (graphic, source_node_id) in old.elements {
graphic_table.push(TableRow {
element: graphic,
transform: old.transform,
alpha_blending: old.alpha_blending,
source_node_id,
});
}
graphic_table
}
GraphicFormat::OlderTableOldGraphicGroup(old) => old
.element
.into_iter()
.flat_map(|element| {
element.elements.into_iter().map(move |(graphic, source_node_id)| TableRow {
element: graphic,
transform: element.transform,
alpha_blending: element.alpha_blending,
source_node_id,
})
})
.collect(),
GraphicFormat::OldTableOldGraphicGroup(old) => old
.element
.into_iter()
.flat_map(|element| {
element.elements.into_iter().map(move |(graphic, source_node_id)| TableRow {
element: graphic,
transform: element.transform,
alpha_blending: element.alpha_blending,
source_node_id,
})
})
.collect(),
GraphicFormat::OldTableGraphicGroup(old) => old
.element
.into_iter()
.flat_map(|element| {
element.elements.into_iter().map(move |(graphic, source_node_id)| TableRow {
element: graphic,
transform: Default::default(),
alpha_blending: Default::default(),
source_node_id,
})
})
.collect(),
GraphicFormat::Table(value) => {
// Try to deserialize as either table format
if let Ok(old_table) = serde_json::from_value::<Table<GraphicGroup>>(value.clone()) {
let mut graphic_table = Table::new();
for row in old_table.iter() {
for (graphic, source_node_id) in &row.element.elements {
graphic_table.push(TableRow {
element: graphic.clone(),
transform: *row.transform,
alpha_blending: *row.alpha_blending,
source_node_id: *source_node_id,
});
}
}
graphic_table
} else if let Ok(new_table) = serde_json::from_value::<Table<Graphic>>(value) {
new_table
} else {
return Err(serde::de::Error::custom("Failed to deserialize Table<Graphic>"));
}
}
})
}

View File

@@ -0,0 +1,100 @@
pub mod artboard;
pub mod graphic;
// Re-export all transitive dependencies so downstream crates only need to depend on graphic-types
pub use core_types;
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, Vector};
pub mod migrations {
use core_types::{
AlphaBlending,
table::{Table, TableRow},
};
use dyn_any::DynAny;
use glam::DAffine2;
use vector_types::vector::{PathStyle, PointDomain, RegionDomain, SegmentDomain, misc::HandleId};
use crate::{Graphic, Vector};
// TODO: Eventually remove this migration document upgrade code
pub fn migrate_vector<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Table<Vector>, D::Error> {
use serde::Deserialize;
#[derive(Clone, Debug, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
pub struct OldVectorData {
pub transform: DAffine2,
pub alpha_blending: AlphaBlending,
pub style: PathStyle,
pub colinear_manipulators: Vec<[HandleId; 2]>,
pub point_domain: PointDomain,
pub segment_domain: SegmentDomain,
pub region_domain: RegionDomain,
pub upstream_graphic_group: Option<Table<Graphic>>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct OldTable<T> {
#[serde(alias = "instances", alias = "instance")]
element: Vec<T>,
transform: Vec<DAffine2>,
alpha_blending: Vec<AlphaBlending>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct OlderTable<T> {
id: Vec<u64>,
#[serde(alias = "instances", alias = "instance")]
element: Vec<T>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
enum VectorFormat {
Vector(Vector),
OldVectorData(OldVectorData),
OldVectorTable(OldTable<Vector>),
OlderVectorTable(OlderTable<Vector>),
VectorTable(Table<Vector>),
}
Ok(match VectorFormat::deserialize(deserializer)? {
VectorFormat::Vector(vector) => Table::new_from_element(vector),
VectorFormat::OldVectorData(old) => {
let mut vector_table = Table::new_from_element(Vector {
style: old.style,
colinear_manipulators: old.colinear_manipulators,
point_domain: old.point_domain,
segment_domain: old.segment_domain,
region_domain: old.region_domain,
upstream_data: old.upstream_graphic_group,
});
*vector_table.iter_mut().next().unwrap().transform = old.transform;
*vector_table.iter_mut().next().unwrap().alpha_blending = old.alpha_blending;
vector_table
}
VectorFormat::OlderVectorTable(older_table) => older_table.element.into_iter().map(|element| TableRow { element, ..Default::default() }).collect(),
VectorFormat::OldVectorTable(old_table) => old_table
.element
.into_iter()
.zip(old_table.transform.into_iter().zip(old_table.alpha_blending))
.map(|(element, (transform, alpha_blending))| TableRow {
element,
transform,
alpha_blending,
source_node_id: None,
})
.collect(),
VectorFormat::VectorTable(vector_table) => vector_table,
})
}
}

View File

@@ -1,8 +1,8 @@
[package]
name = "graphene-core-shaders"
name = "no-std-types"
version = "0.1.0"
edition = "2024"
description = "no_std API definitions for Graphene"
description = "no_std types for Graphene (shader-compatible)"
authors = ["Graphite Authors <contact@graphite.rs>"]
license = "MIT OR Apache-2.0"
@@ -48,7 +48,7 @@ specta = { workspace = true, optional = true }
log = { workspace = true, optional = true }
[dev-dependencies]
graphene-core = { workspace = true }
core-types = { workspace = true }
[lints.rust]
# the spirv target is not in the list of common cfgs so must be added manually
@@ -57,4 +57,4 @@ unexpected_cfgs = { level = "warn", check-cfg = [
] }
[package.metadata.cargo-shear]
ignored = ["graphene-core"]
ignored = ["core-types"]

View File

@@ -360,7 +360,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgbaf32(0.3, 0.14, 0.15, 0.92).unwrap();
/// assert!(color.components() == (0.3, 0.14, 0.15, 0.92));
///
@@ -398,7 +398,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgb8_srgb(0x72, 0x67, 0x62);
/// let color2 = Color::from_rgba8_srgb(0x72, 0x67, 0x62, 0xFF);
/// assert_eq!(color, color2)
@@ -413,7 +413,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgba8_srgb(0x72, 0x67, 0x62, 0x61);
/// ```
#[inline(always)]
@@ -431,7 +431,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_hsla(0.5, 0.2, 0.3, 1.);
/// ```
pub fn from_hsla(hue: f32, saturation: f32, lightness: f32, alpha: f32) -> Color {
@@ -473,7 +473,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgbaf32(0.114, 0.103, 0.98, 0.97).unwrap();
/// assert!(color.r() == 0.114);
/// ```
@@ -486,7 +486,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgbaf32(0.114, 0.103, 0.98, 0.97).unwrap();
/// assert!(color.g() == 0.103);
/// ```
@@ -499,7 +499,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgbaf32(0.114, 0.103, 0.98, 0.97).unwrap();
/// assert!(color.b() == 0.98);
/// ```
@@ -512,7 +512,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgbaf32(0.114, 0.103, 0.98, 0.97).unwrap();
/// assert!(color.a() == 0.97);
/// ```
@@ -788,7 +788,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgbaf32(0.114, 0.103, 0.98, 0.97).unwrap();
/// assert_eq!(color.components(), (0.114, 0.103, 0.98, 0.97));
/// ```
@@ -801,7 +801,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgba8_srgb(0x52, 0x67, 0xFA, 0x61); // Premultiplied alpha
/// assert_eq!("3240a261", color.to_rgba_hex_srgb()); // Equivalent hex incorporating premultiplied alpha
/// ```
@@ -819,7 +819,7 @@ impl Color {
/// Return a 6-character RGB hex string (without a # prefix). Use this if the [`Color`] is in linear space.
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgba8_srgb(0x52, 0x67, 0xFA, 0x61); // Premultiplied alpha
/// assert_eq!("3240a2", color.to_rgb_hex_srgb()); // Equivalent hex incorporating premultiplied alpha
/// ```
@@ -830,7 +830,7 @@ impl Color {
/// Return a 6-character RGB hex string (without a # prefix). Use this if the [`Color`] is in gamma space.
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgba8_srgb(0x52, 0x67, 0xFA, 0x61); // Premultiplied alpha
/// assert_eq!("3240a2", color.to_rgb_hex_srgb()); // Equivalent hex incorporating premultiplied alpha
/// ```
@@ -843,7 +843,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgbaf32(0.114, 0.103, 0.98, 0.97).unwrap();
/// // TODO: Add test
/// ```
@@ -857,7 +857,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgbaf32(0.114, 0.103, 0.98, 0.97).unwrap();
/// // TODO: Add test
/// ```
@@ -872,7 +872,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_hsla(0.5, 0.2, 0.3, 1.).to_hsla();
/// ```
pub fn to_hsla(&self) -> [f32; 4] {
@@ -908,7 +908,7 @@ impl Color {
///
/// # Examples
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgba_str("7C67FA61").unwrap();
/// ```
pub fn from_rgba_str(color_str: &str) -> Option<Color> {
@@ -926,7 +926,7 @@ impl Color {
/// Creates a color from a 6-character RGB hex string (without a # prefix).
///
/// ```
/// use graphene_core::color::Color;
/// use core_types::color::Color;
/// let color = Color::from_rgb_str("7C67FA").unwrap();
/// ```
pub fn from_rgb_str(color_str: &str) -> Option<Color> {

View File

@@ -0,0 +1,29 @@
[package]
name = "raster-types"
version = "0.1.0"
edition = "2024"
description = "Raster data types for Graphene node system"
authors = ["Graphite Authors <contact@graphite.rs>"]
license = "MIT OR Apache-2.0"
[features]
default = ["serde"]
wgpu = ["dep:wgpu"]
[dependencies]
# Local dependencies
core-types = { workspace = true }
node-macro = { workspace = true }
# Workspace dependencies
dyn-any = { workspace = true }
glam = { workspace = true }
base64 = { workspace = true }
bytemuck = { workspace = true }
specta = { workspace = true }
image = { workspace = true }
serde_json = { workspace = true }
# Optional workspace dependencies
serde = { workspace = true, optional = true }
wgpu = { workspace = true, optional = true }

View File

@@ -1,10 +1,12 @@
use super::Color;
use crate::AlphaBlending;
use crate::color::float_to_srgb_u8;
use crate::raster_types::Raster;
use crate::table::{Table, TableRow};
use crate::vector::Vector;
use crate::raster_types::{CPU, Raster};
use crate::{Bitmap, BitmapMut};
use core_types::AlphaBlending;
use core_types::Color;
use core_types::color::float_to_srgb_u8;
use core_types::table::{Table, TableRow};
// use crate::vector::Vector; // TODO: Check if Vector is actually used, if so handle differently
use core::hash::{Hash, Hasher};
use core_types::color::*;
use dyn_any::{DynAny, StaticType};
use glam::{DAffine2, DVec2};
use std::vec::Vec;
@@ -12,8 +14,8 @@ use std::vec::Vec;
mod base64_serde {
//! Basic wrapper for [`serde`] to perform [`base64`] encoding
use super::super::Pixel;
use base64::Engine;
use core_types::color::*;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn as_base64<S: Serializer, P: Pixel>(key: &[P], serializer: S) -> Result<S::Ok, S::Error> {
@@ -58,7 +60,7 @@ impl Hash for TransformImage {
fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
}
impl<P: Pixel + Debug> Debug for Image<P> {
impl<P: Pixel + std::fmt::Debug> std::fmt::Debug for Image<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let length = self.data.len();
f.debug_struct("Image")
@@ -236,7 +238,6 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
pub enum GraphicElement {
GraphicGroup(Table<GraphicElement>),
VectorData(Table<Vector>),
RasterFrame(RasterFrame),
}
@@ -391,8 +392,6 @@ pub fn migrate_image_frame_row<'de, D: serde::Deserializer<'de>>(deserializer: D
pub enum GraphicElement {
/// Equivalent to the SVG <g> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/g
GraphicGroup(Table<GraphicElement>),
/// A vector shape, equivalent to the SVG <path> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
VectorData(Table<Vector>),
RasterFrame(RasterFrame),
}
@@ -461,7 +460,7 @@ pub fn migrate_image_frame_row<'de, D: serde::Deserializer<'de>>(deserializer: D
})
}
impl<P: Debug + Copy + Pixel> Sample for Image<P> {
impl<P: std::fmt::Debug + Copy + Pixel> Sample for Image<P> {
type Pixel = P;
// TODO: Improve sampling logic

View File

@@ -1,13 +1,18 @@
pub mod image;
pub mod raster_types;
// Re-exports for convenience
pub use image::Image;
pub use raster_types::*;
// Re-export color types from no-std-types
pub use core_types::color::*;
/// as to not yet rename all references
pub mod color {
pub use super::*;
}
pub mod image;
pub use self::image::Image;
pub use crate::color::*;
use crate::raster_types::CPU;
use std::fmt::Debug;
pub trait Bitmap {

View File

@@ -1,8 +1,8 @@
use crate::Color;
use crate::bounds::{BoundingBox, RenderBoundingBox};
use crate::math::quad::Quad;
use crate::raster::Image;
use crate::image::Image;
use core::ops::Deref;
use core_types::Color;
use core_types::bounds::{BoundingBox, RenderBoundingBox};
use core_types::math::quad::Quad;
use dyn_any::DynAny;
use glam::{DAffine2, DVec2};
use std::fmt::Debug;
@@ -211,3 +211,17 @@ where
RenderBoundingBox::Rectangle((transform * unit_rectangle).bounding_box())
}
}
// RenderComplexity trait implementations
impl core_types::render_complexity::RenderComplexity for Raster<CPU> {
fn render_complexity(&self) -> usize {
(self.width * self.height / 500) as usize
}
}
impl core_types::render_complexity::RenderComplexity for Raster<GPU> {
fn render_complexity(&self) -> usize {
// GPU textures currently can't have a thumbnail
usize::MAX
}
}

View File

@@ -1,15 +1,15 @@
[package]
name = "graphene-svg-renderer"
name = "rendering"
version = "0.1.0"
edition = "2024"
description = "graphene svg renderer"
description = "SVG rendering for Graphene"
authors = ["Graphite Authors <contact@graphite.rs>"]
license = "MIT OR Apache-2.0"
[dependencies]
# Local dependencies
dyn-any = { workspace = true }
graphene-core = { workspace = true }
core-types = { workspace = true }
# Workspace dependencies
glam = { workspace = true }
@@ -19,6 +19,9 @@ log = { workspace = true }
num-traits = { workspace = true }
usvg = { workspace = true }
kurbo = { workspace = true }
vector-types = { workspace = true }
graphic-types = { workspace = true }
# Optional workspace dependencies
vello = { workspace = true, optional = true }
# Workspace dependencies
vello = { workspace = true }

View File

@@ -1,6 +1,6 @@
use glam::DVec2;
use graphene_core::subpath::{ManipulatorGroup, Subpath};
use graphene_core::vector::PointId;
use vector_types::subpath::{ManipulatorGroup, Subpath};
use vector_types::vector::PointId;
pub fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<PointId>> {
let mut subpaths = Vec::new();

View File

@@ -1,9 +1,9 @@
use crate::renderer::{RenderParams, format_transform_matrix};
use core_types::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
use core_types::uuid::generate_uuid;
use glam::DAffine2;
use graphene_core::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
use graphene_core::gradient::{Gradient, GradientType};
use graphene_core::uuid::generate_uuid;
use graphene_core::vector::style::{Fill, PaintOrder, PathStyle, RenderMode, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
use graphic_types::vector_types::gradient::{Gradient, GradientType};
use graphic_types::vector_types::vector::style::{Fill, PaintOrder, PathStyle, RenderMode, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
use std::fmt::Write;
pub trait RenderExt {

View File

@@ -1,26 +1,26 @@
use crate::render_ext::RenderExt;
use crate::to_peniko::BlendModeExt;
use core_types::blending::BlendMode;
use core_types::bounds::BoundingBox;
use core_types::bounds::RenderBoundingBox;
use core_types::color::Color;
use core_types::math::quad::Quad;
use core_types::render_complexity::RenderComplexity;
use core_types::table::{Table, TableRow};
use core_types::transform::{Footprint, Transform};
use core_types::uuid::{NodeId, generate_uuid};
use dyn_any::DynAny;
use glam::{DAffine2, DVec2};
use graphene_core::blending::BlendMode;
use graphene_core::bounds::BoundingBox;
use graphene_core::bounds::RenderBoundingBox;
use graphene_core::color::Color;
use graphene_core::gradient::GradientStops;
use graphene_core::gradient::GradientType;
use graphene_core::math::quad::Quad;
use graphene_core::raster::BitmapMut;
use graphene_core::raster::Image;
use graphene_core::raster_types::{CPU, GPU, Raster};
use graphene_core::render_complexity::RenderComplexity;
use graphene_core::subpath::Subpath;
use graphene_core::table::{Table, TableRow};
use graphene_core::transform::{Footprint, Transform};
use graphene_core::uuid::{NodeId, generate_uuid};
use graphene_core::vector::Vector;
use graphene_core::vector::click_target::{ClickTarget, FreePoint};
use graphene_core::vector::style::{Fill, PaintOrder, RenderMode, Stroke, StrokeAlign};
use graphene_core::{Artboard, Graphic};
use graphic_types::Vector;
use graphic_types::raster_types::BitmapMut;
use graphic_types::raster_types::Image;
use graphic_types::raster_types::{CPU, GPU, Raster};
use graphic_types::vector_types::gradient::GradientStops;
use graphic_types::vector_types::gradient::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};
use kurbo::Affine;
use num_traits::Zero;
use std::collections::{HashMap, HashSet};
@@ -28,7 +28,6 @@ use std::fmt::Write;
use std::hash::{DefaultHasher, Hash, Hasher};
use std::ops::Deref;
use std::sync::{Arc, LazyLock};
#[cfg(feature = "vello")]
use vello::*;
#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
@@ -152,7 +151,6 @@ impl Default for SvgRender {
#[derive(Clone, Debug, Default)]
pub struct RenderContext {
#[cfg(feature = "vello")]
pub resource_overrides: Vec<(peniko::ImageBrush, wgpu::Texture)>,
}
@@ -264,7 +262,6 @@ impl RenderMetadata {
pub trait Render: BoundingBox + RenderComplexity {
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams);
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, _render_params: &RenderParams);
/// The upstream click targets for each layer are collected during the render so that they do not have to be calculated for each click detection.
@@ -295,7 +292,6 @@ impl Render for Graphic {
}
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
match self {
Graphic::Graphic(table) => table.render_to_vello(scene, transform, context, render_params),
@@ -448,7 +444,6 @@ impl Render for Artboard {
);
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
use vello::peniko;
@@ -503,7 +498,6 @@ impl Render for Table<Artboard> {
}
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
for row in self.iter() {
row.element.render_to_vello(scene, transform, context, render_params);
@@ -579,7 +573,6 @@ impl Render for Table<Graphic> {
}
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, render_params: &RenderParams) {
let mut iter = self.iter().peekable();
let mut mask_element_and_transform = None;
@@ -860,14 +853,15 @@ impl Render for Table<Vector> {
}
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
use graphene_core::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
use graphene_core::vector::style::{GradientType, StrokeCap, StrokeJoin};
use core_types::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
use graphic_types::vector_types::vector::style::{GradientType, StrokeCap, StrokeJoin};
use vello::kurbo::{Cap, Join};
use vello::peniko;
for row in self.iter() {
use graphic_types::vector_types::vector;
let multiplied_transform = parent_transform * *row.transform;
let has_real_stroke = row.element.style.stroke().filter(|stroke| stroke.weight() > 0.);
let set_stroke_transform = has_real_stroke.map(|stroke| stroke.transform).filter(|transform| transform.matrix2.determinant() != 0.);
@@ -919,7 +913,7 @@ impl Render for Table<Vector> {
row.element.style.stroke().is_some_and(|stroke| stroke.has_renderable_stroke() && stroke.align.is_not_centered()) && row.element.stroke_bezier_paths().all(|path| path.closed());
let use_layer = can_draw_aligned_stroke;
let wants_stroke_below = row.element.style.stroke().is_some_and(|s| s.paint_order == graphene_core::vector::style::PaintOrder::StrokeBelow);
let wants_stroke_below = row.element.style.stroke().is_some_and(|s| s.paint_order == vector::style::PaintOrder::StrokeBelow);
// Closures to avoid duplicated fill/stroke drawing logic
let do_fill = |scene: &mut Scene| match row.element.style.fill() {
@@ -1150,7 +1144,7 @@ impl Render for Table<Vector> {
metadata.click_targets.entry(element_id).or_insert(click_targets);
}
if let Some(upstream_nested_layers) = &vector.upstream_nested_layers {
if let Some(upstream_nested_layers) = &vector.upstream_data {
footprint.transform *= transform;
upstream_nested_layers.collect_metadata(metadata, footprint, None);
}
@@ -1286,7 +1280,6 @@ impl Render for Table<Raster<CPU>> {
}
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, _: &mut RenderContext, render_params: &RenderParams) {
use vello::peniko;
@@ -1302,13 +1295,13 @@ impl Render for Table<Raster<CPU>> {
let opacity = alpha_blending.opacity(render_params.for_mask);
let mut layer = false;
if opacity < 1. || alpha_blending.blend_mode != BlendMode::default() {
if let RenderBoundingBox::Rectangle(bounds) = self.bounding_box(transform, false) {
let blending = peniko::BlendMode::new(blend_mode, peniko::Compose::SrcOver);
let rect = kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y);
scene.push_layer(blending, opacity, kurbo::Affine::IDENTITY, &rect);
layer = true;
}
if (opacity < 1. || alpha_blending.blend_mode != BlendMode::default())
&& let RenderBoundingBox::Rectangle(bounds) = self.bounding_box(transform, false)
{
let blending = peniko::BlendMode::new(blend_mode, peniko::Compose::SrcOver);
let rect = kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y);
scene.push_layer(blending, opacity, kurbo::Affine::IDENTITY, &rect);
layer = true;
}
let image_brush = peniko::ImageBrush::new(peniko::ImageData {
@@ -1354,20 +1347,19 @@ impl Render for Table<Raster<GPU>> {
log::warn!("tried to render texture as an svg");
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, _render_params: &RenderParams) {
use vello::peniko;
for row in self.iter() {
let blend_mode = *row.alpha_blending;
let mut layer = false;
if blend_mode != Default::default() {
if let RenderBoundingBox::Rectangle(bounds) = self.bounding_box(transform, true) {
let blending = peniko::BlendMode::new(blend_mode.blend_mode.to_peniko(), peniko::Compose::SrcOver);
let rect = kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y);
scene.push_layer(blending, blend_mode.opacity, kurbo::Affine::IDENTITY, &rect);
layer = true;
}
if blend_mode != Default::default()
&& let RenderBoundingBox::Rectangle(bounds) = self.bounding_box(transform, true)
{
let blending = peniko::BlendMode::new(blend_mode.blend_mode.to_peniko(), peniko::Compose::SrcOver);
let rect = kurbo::Rect::new(bounds[0].x, bounds[0].y, bounds[1].x, bounds[1].y);
scene.push_layer(blending, blend_mode.opacity, kurbo::Affine::IDENTITY, &rect);
layer = true;
}
let width = row.element.data().width();
@@ -1440,7 +1432,6 @@ impl Render for Table<Color> {
}
}
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, _parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
use vello::peniko;
@@ -1534,7 +1525,6 @@ impl Render for Table<GradientStops> {
}
// TODO: Fix infinite gradient rendering
#[cfg(feature = "vello")]
fn render_to_vello(&self, scene: &mut Scene, _parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
use vello::peniko;

View File

@@ -1,12 +1,10 @@
use graphene_core::BlendMode;
use core_types::BlendMode;
use vello::peniko;
#[cfg(feature = "vello")]
pub trait BlendModeExt {
fn to_peniko(&self) -> peniko::Mix;
}
#[cfg(feature = "vello")]
impl BlendModeExt for BlendMode {
fn to_peniko(&self) -> peniko::Mix {
match self {

View File

@@ -0,0 +1,33 @@
[package]
name = "vector-types"
version = "0.1.0"
edition = "2024"
description = "Vector graphics types and algorithms for Graphene"
authors = ["Graphite Authors <contact@graphite.rs>"]
license = "MIT OR Apache-2.0"
[features]
default = ["serde"]
[dependencies]
# Local dependencies
core-types = { workspace = true }
node-macro = { workspace = true }
# Workspace dependencies
bitflags = { workspace = true }
bytemuck = { workspace = true }
num-traits = { workspace = true }
glam = { workspace = true }
kurbo = { workspace = true }
lyon_geom = { workspace = true }
dyn-any = { workspace = true }
specta = { workspace = true }
log = { workspace = true }
petgraph = { workspace = true }
rustc-hash = { workspace = true }
polycool = { workspace = true }
tinyvec = { workspace = true }
# Optional workspace dependencies
serde = { workspace = true, optional = true }

View File

@@ -1,4 +1,4 @@
use crate::Color;
use core_types::{Color, render_complexity::RenderComplexity};
use dyn_any::DynAny;
use glam::{DAffine2, DVec2};
@@ -32,6 +32,12 @@ impl Default for GradientStops {
}
}
impl RenderComplexity for GradientStops {
fn render_complexity(&self) -> usize {
1
}
}
impl IntoIterator for GradientStops {
type Item = (f64, Color);
type IntoIter = std::vec::IntoIter<(f64, Color)>;
@@ -232,3 +238,9 @@ impl Gradient {
Some(index)
}
}
impl core_types::bounds::BoundingBox for GradientStops {
fn bounding_box(&self, _transform: DAffine2, _include_stroke: bool) -> core_types::bounds::RenderBoundingBox {
core_types::bounds::RenderBoundingBox::Infinite
}
}

View File

@@ -0,0 +1,20 @@
#[macro_use]
extern crate log;
pub mod gradient;
pub mod math;
pub mod subpath;
pub mod vector;
// Re-export commonly used types at the crate root
pub use core_types as gcore;
pub use gradient::{GradientStops, GradientType};
pub use math::{QuadExt, RectExt};
pub use subpath::Subpath;
pub use vector::Vector;
pub use vector::reference_point::ReferencePoint;
// Re-export dependencies that users of this crate will need
pub use dyn_any;
pub use glam;
pub use kurbo;

View File

@@ -1,7 +1,7 @@
use crate::math::quad::Quad;
use crate::math::rect::Rect;
use crate::subpath::Bezier;
use crate::vector::misc::dvec2_to_point;
use core_types::math::quad::Quad;
use core_types::math::rect::Rect;
use kurbo::{Line, PathSeg};
pub trait QuadExt {

View File

@@ -239,7 +239,7 @@ impl<PointId: Identifier> Subpath<PointId> {
let mut start_angle = 0.;
let mut sweep_angle = sweep_angle.abs();
if (sweep_angle / std::f64::consts::TAU).floor() as u32 % 2 == 0 {
if ((sweep_angle / std::f64::consts::TAU).floor() as u32).is_multiple_of(2) {
sweep_angle %= std::f64::consts::TAU;
} else {
start_angle = sweep_angle % std::f64::consts::TAU;

View File

@@ -1,8 +1,8 @@
use super::consts::MAX_ABSOLUTE_DIFFERENCE;
use super::*;
use crate::math::polynomial::pathseg_to_parametric_polynomial;
use crate::vector::algorithms::bezpath_algorithms::pathseg_length_centroid_and_length;
use crate::vector::algorithms::intersection::{filtered_all_segment_intersections, pathseg_self_intersections};
use core_types::math::polynomial::pathseg_to_parametric_polynomial;
use glam::DVec2;
impl<PointId: Identifier> Subpath<PointId> {

View File

@@ -1,9 +1,9 @@
use super::intersection::bezpath_intersections;
use super::poisson_disk::poisson_disk_sample;
use super::util::pathseg_tangent;
use crate::math::polynomial::pathseg_to_parametric_polynomial;
use crate::vector::algorithms::offset_subpath::MAX_ABSOLUTE_DIFFERENCE;
use crate::vector::misc::{PointSpacingType, dvec2_to_point, point_to_dvec2};
use core_types::math::polynomial::pathseg_to_parametric_polynomial;
use glam::{DMat2, DVec2};
use kurbo::common::{solve_cubic, solve_quadratic};
use kurbo::{BezPath, CubicBez, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveDeriv, PathEl, PathSeg, Point, QuadBez, Rect, Shape, Vec2};

Some files were not shown because too many files have changed in this diff Show More