mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-08 00:05:00 +00:00
WIP
This commit is contained in:
parent
9f9a50e79a
commit
79c47637d2
85 changed files with 1148 additions and 844 deletions
121
Cargo.lock
generated
121
Cargo.lock
generated
|
@ -2133,10 +2133,16 @@ dependencies = [
|
||||||
"graphene-application-io",
|
"graphene-application-io",
|
||||||
"graphene-brush",
|
"graphene-brush",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"graphene-element",
|
||||||
"graphene-element-nodes",
|
"graphene-element-nodes",
|
||||||
|
"graphene-math-nodes",
|
||||||
"graphene-path-bool",
|
"graphene-path-bool",
|
||||||
|
"graphene-raster",
|
||||||
"graphene-raster-nodes",
|
"graphene-raster-nodes",
|
||||||
"graphene-svg-renderer",
|
"graphene-svg-renderer",
|
||||||
|
"graphene-text",
|
||||||
|
"graphene-vector",
|
||||||
|
"graphene-vector-nodes",
|
||||||
"iai-callgrind",
|
"iai-callgrind",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
|
@ -2161,6 +2167,7 @@ dependencies = [
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
"glam",
|
"glam",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"graphene-text",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
|
@ -2174,9 +2181,15 @@ dependencies = [
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
"glam",
|
"glam",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"graphene-element",
|
||||||
|
"graphene-raster",
|
||||||
"graphene-raster-nodes",
|
"graphene-raster-nodes",
|
||||||
|
"graphene-svg-renderer",
|
||||||
|
"graphene-vector",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"specta",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2189,7 +2202,6 @@ dependencies = [
|
||||||
"fern",
|
"fern",
|
||||||
"futures",
|
"futures",
|
||||||
"graph-craft",
|
"graph-craft",
|
||||||
"graphene-core",
|
|
||||||
"graphene-std",
|
"graphene-std",
|
||||||
"interpreted-executor",
|
"interpreted-executor",
|
||||||
"log",
|
"log",
|
||||||
|
@ -2204,14 +2216,12 @@ name = "graphene-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bezier-rs",
|
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"ctor",
|
"ctor",
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
"glam",
|
"glam",
|
||||||
"half",
|
"half",
|
||||||
"image",
|
"image",
|
||||||
"kurbo",
|
|
||||||
"log",
|
"log",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
|
@ -2229,6 +2239,22 @@ dependencies = [
|
||||||
"wgpu",
|
"wgpu",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "graphene-element"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bezier-rs",
|
||||||
|
"dyn-any",
|
||||||
|
"glam",
|
||||||
|
"graphene-core",
|
||||||
|
"graphene-raster",
|
||||||
|
"graphene-vector",
|
||||||
|
"node-macro",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"specta",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphene-element-nodes"
|
name = "graphene-element-nodes"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -2237,7 +2263,10 @@ dependencies = [
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
"glam",
|
"glam",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"graphene-element",
|
||||||
"graphene-math-nodes",
|
"graphene-math-nodes",
|
||||||
|
"graphene-raster",
|
||||||
|
"graphene-vector",
|
||||||
"log",
|
"log",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
|
@ -2266,6 +2295,8 @@ dependencies = [
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
"glam",
|
"glam",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"graphene-element",
|
||||||
|
"graphene-vector",
|
||||||
"log",
|
"log",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
"path-bool",
|
"path-bool",
|
||||||
|
@ -2273,6 +2304,23 @@ dependencies = [
|
||||||
"specta",
|
"specta",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "graphene-raster"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bytemuck",
|
||||||
|
"dyn-any",
|
||||||
|
"glam",
|
||||||
|
"graphene-core",
|
||||||
|
"image",
|
||||||
|
"node-macro",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"specta",
|
||||||
|
"wgpu",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphene-raster-nodes"
|
name = "graphene-raster-nodes"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -2284,6 +2332,7 @@ dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"glam",
|
"glam",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"graphene-raster",
|
||||||
"image",
|
"image",
|
||||||
"ndarray",
|
"ndarray",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
|
@ -2308,14 +2357,18 @@ dependencies = [
|
||||||
"graphene-application-io",
|
"graphene-application-io",
|
||||||
"graphene-brush",
|
"graphene-brush",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"graphene-element",
|
||||||
"graphene-element-nodes",
|
"graphene-element-nodes",
|
||||||
"graphene-math-nodes",
|
"graphene-math-nodes",
|
||||||
"graphene-path-bool",
|
"graphene-path-bool",
|
||||||
|
"graphene-raster",
|
||||||
"graphene-raster-nodes",
|
"graphene-raster-nodes",
|
||||||
"graphene-svg-renderer",
|
"graphene-svg-renderer",
|
||||||
|
"graphene-text",
|
||||||
|
"graphene-vector",
|
||||||
|
"graphene-vector-nodes",
|
||||||
"image",
|
"image",
|
||||||
"log",
|
"log",
|
||||||
"ndarray",
|
|
||||||
"node-macro",
|
"node-macro",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
|
@ -2337,13 +2390,73 @@ dependencies = [
|
||||||
"dyn-any",
|
"dyn-any",
|
||||||
"glam",
|
"glam",
|
||||||
"graphene-core",
|
"graphene-core",
|
||||||
|
"graphene-element",
|
||||||
|
"graphene-raster",
|
||||||
|
"graphene-vector",
|
||||||
"log",
|
"log",
|
||||||
|
"node-macro",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"specta",
|
||||||
"usvg",
|
"usvg",
|
||||||
"vello",
|
"vello",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "graphene-text"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bezier-rs",
|
||||||
|
"dyn-any",
|
||||||
|
"glam",
|
||||||
|
"graphene-core",
|
||||||
|
"graphene-vector",
|
||||||
|
"kurbo",
|
||||||
|
"node-macro",
|
||||||
|
"rustybuzz 0.20.1",
|
||||||
|
"serde",
|
||||||
|
"specta",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "graphene-vector"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bezier-rs",
|
||||||
|
"dyn-any",
|
||||||
|
"glam",
|
||||||
|
"graphene-core",
|
||||||
|
"kurbo",
|
||||||
|
"log",
|
||||||
|
"node-macro",
|
||||||
|
"petgraph 0.7.1",
|
||||||
|
"rustc-hash 2.1.1",
|
||||||
|
"serde",
|
||||||
|
"specta",
|
||||||
|
"tinyvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "graphene-vector-nodes"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bezier-rs",
|
||||||
|
"dyn-any",
|
||||||
|
"glam",
|
||||||
|
"graphene-core",
|
||||||
|
"graphene-vector",
|
||||||
|
"kurbo",
|
||||||
|
"log",
|
||||||
|
"node-macro",
|
||||||
|
"petgraph 0.7.1",
|
||||||
|
"rand 0.9.0",
|
||||||
|
"rustc-hash 2.1.1",
|
||||||
|
"serde",
|
||||||
|
"specta",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "graphite-desktop"
|
name = "graphite-desktop"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
25
Cargo.toml
25
Cargo.toml
|
@ -7,14 +7,22 @@ members = [
|
||||||
"node-graph/gapplication-io",
|
"node-graph/gapplication-io",
|
||||||
"node-graph/gbrush",
|
"node-graph/gbrush",
|
||||||
"node-graph/gcore",
|
"node-graph/gcore",
|
||||||
"node-graph/gstd",
|
"node-graph/gelement",
|
||||||
|
"node-graph/gelement-nodes",
|
||||||
|
"node-graph/gmath-nodes",
|
||||||
|
"node-graph/gpath-bool",
|
||||||
"node-graph/gelement-nodes",
|
"node-graph/gelement-nodes",
|
||||||
"node-graph/gmath-nodes",
|
"node-graph/gmath-nodes",
|
||||||
"node-graph/gpath-bool",
|
"node-graph/gpath-bool",
|
||||||
"node-graph/graph-craft",
|
"node-graph/graph-craft",
|
||||||
"node-graph/graphene-cli",
|
"node-graph/graphene-cli",
|
||||||
|
"node-graph/graster",
|
||||||
"node-graph/graster-nodes",
|
"node-graph/graster-nodes",
|
||||||
|
"node-graph/gstd",
|
||||||
"node-graph/gsvg-renderer",
|
"node-graph/gsvg-renderer",
|
||||||
|
"node-graph/gtext",
|
||||||
|
"node-graph/gvector",
|
||||||
|
"node-graph/gvector-nodes",
|
||||||
"node-graph/interpreted-executor",
|
"node-graph/interpreted-executor",
|
||||||
"node-graph/node-macro",
|
"node-graph/node-macro",
|
||||||
"node-graph/preprocessor",
|
"node-graph/preprocessor",
|
||||||
|
@ -29,14 +37,22 @@ default-members = [
|
||||||
"frontend/wasm",
|
"frontend/wasm",
|
||||||
"node-graph/gbrush",
|
"node-graph/gbrush",
|
||||||
"node-graph/gcore",
|
"node-graph/gcore",
|
||||||
"node-graph/gstd",
|
"node-graph/gelement",
|
||||||
|
"node-graph/gelement-nodes",
|
||||||
|
"node-graph/gmath-nodes",
|
||||||
|
"node-graph/gpath-bool",
|
||||||
"node-graph/gelement-nodes",
|
"node-graph/gelement-nodes",
|
||||||
"node-graph/gmath-nodes",
|
"node-graph/gmath-nodes",
|
||||||
"node-graph/gpath-bool",
|
"node-graph/gpath-bool",
|
||||||
"node-graph/graph-craft",
|
"node-graph/graph-craft",
|
||||||
"node-graph/graphene-cli",
|
"node-graph/graphene-cli",
|
||||||
|
"node-graph/graster",
|
||||||
"node-graph/graster-nodes",
|
"node-graph/graster-nodes",
|
||||||
|
"node-graph/gstd",
|
||||||
"node-graph/gsvg-renderer",
|
"node-graph/gsvg-renderer",
|
||||||
|
"node-graph/gtext",
|
||||||
|
"node-graph/gvector",
|
||||||
|
"node-graph/gvector-nodes",
|
||||||
"node-graph/interpreted-executor",
|
"node-graph/interpreted-executor",
|
||||||
"node-graph/node-macro",
|
"node-graph/node-macro",
|
||||||
]
|
]
|
||||||
|
@ -52,13 +68,18 @@ path-bool = { path = "libraries/path-bool" }
|
||||||
graphene-application-io = { path = "node-graph/gapplication-io" }
|
graphene-application-io = { path = "node-graph/gapplication-io" }
|
||||||
graphene-brush = { path = "node-graph/gbrush" }
|
graphene-brush = { path = "node-graph/gbrush" }
|
||||||
graphene-core = { path = "node-graph/gcore" }
|
graphene-core = { path = "node-graph/gcore" }
|
||||||
|
graphene-element = { path = "node-graph/gelement" }
|
||||||
graphene-element-nodes = { path = "node-graph/gelement-nodes" }
|
graphene-element-nodes = { path = "node-graph/gelement-nodes" }
|
||||||
graphene-math-nodes = { path = "node-graph/gmath-nodes" }
|
graphene-math-nodes = { path = "node-graph/gmath-nodes" }
|
||||||
graphene-path-bool = { path = "node-graph/gpath-bool" }
|
graphene-path-bool = { path = "node-graph/gpath-bool" }
|
||||||
graph-craft = { path = "node-graph/graph-craft" }
|
graph-craft = { path = "node-graph/graph-craft" }
|
||||||
|
graphene-raster = { path = "node-graph/graster" }
|
||||||
graphene-raster-nodes = { path = "node-graph/graster-nodes" }
|
graphene-raster-nodes = { path = "node-graph/graster-nodes" }
|
||||||
graphene-std = { path = "node-graph/gstd" }
|
graphene-std = { path = "node-graph/gstd" }
|
||||||
graphene-svg-renderer = { path = "node-graph/gsvg-renderer" }
|
graphene-svg-renderer = { path = "node-graph/gsvg-renderer" }
|
||||||
|
graphene-text = { path = "node-graph/gtext" }
|
||||||
|
graphene-vector = { path = "node-graph/gvector" }
|
||||||
|
graphene-vector-nodes = { path = "node-graph/gvector-nodes" }
|
||||||
interpreted-executor = { path = "node-graph/interpreted-executor" }
|
interpreted-executor = { path = "node-graph/interpreted-executor" }
|
||||||
node-macro = { path = "node-graph/node-macro" }
|
node-macro = { path = "node-graph/node-macro" }
|
||||||
wgpu-executor = { path = "node-graph/wgpu-executor" }
|
wgpu-executor = { path = "node-graph/wgpu-executor" }
|
||||||
|
|
|
@ -14,6 +14,7 @@ wgpu = ["dep:wgpu"]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
dyn-any = { workspace = true }
|
dyn-any = { workspace = true }
|
||||||
graphene-core = { workspace = true }
|
graphene-core = { workspace = true }
|
||||||
|
graphene-text = { workspace = true }
|
||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
glam = { workspace = true }
|
glam = { workspace = true }
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
||||||
use glam::{DAffine2, UVec2};
|
use glam::{DAffine2, UVec2};
|
||||||
use graphene_core::text::FontCache;
|
|
||||||
use graphene_core::transform::Footprint;
|
use graphene_core::transform::Footprint;
|
||||||
use graphene_core::vector::style::ViewMode;
|
use graphene_core::view_mode::ViewMode;
|
||||||
|
use graphene_text::FontCache;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
|
@ -14,11 +14,16 @@ serde = ["dep:serde"]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
dyn-any = { workspace = true }
|
dyn-any = { workspace = true }
|
||||||
graphene-core = { workspace = true }
|
graphene-core = { workspace = true }
|
||||||
|
graphene-vector = { workspace = true }
|
||||||
|
graphene-raster = { workspace = true }
|
||||||
|
graphene-element = { workspace = true }
|
||||||
graphene-raster-nodes = { workspace = true }
|
graphene-raster-nodes = { workspace = true }
|
||||||
|
graphene-svg-renderer = { workspace = true }
|
||||||
node-macro = { workspace = true }
|
node-macro = { workspace = true }
|
||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
glam = { workspace = true }
|
glam = { workspace = true }
|
||||||
|
specta = { workspace = true }
|
||||||
|
|
||||||
# Optional workspace dependencies
|
# Optional workspace dependencies
|
||||||
serde = { workspace = true, optional = true, features = ["derive"] }
|
serde = { workspace = true, optional = true, features = ["derive"] }
|
||||||
|
@ -26,3 +31,4 @@ serde = { workspace = true, optional = true, features = ["derive"] }
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
use crate::brush_cache::BrushCache;
|
use crate::brush_cache::BrushCache;
|
||||||
use crate::brush_stroke::{BrushStroke, BrushStyle};
|
use crate::brush_stroke::{BrushStroke, BrushStyle};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphene_core::Node;
|
||||||
use graphene_core::blending::BlendMode;
|
use graphene_core::blending::BlendMode;
|
||||||
use graphene_core::bounds::BoundingBox;
|
use graphene_core::bounds::BoundingBox;
|
||||||
use graphene_core::color::{Alpha, Color, Pixel, Sample};
|
use graphene_core::color::{Alpha, Color, Pixel, Sample};
|
||||||
use graphene_core::generic::FnNode;
|
use graphene_core::generic::FnNode;
|
||||||
use graphene_core::instances::Instance;
|
use graphene_core::instances::Instance;
|
||||||
use graphene_core::math::bbox::{AxisAlignedBbox, Bbox};
|
use graphene_core::math::bbox::{AxisAlignedBbox, Bbox};
|
||||||
use graphene_core::raster::BitmapMut;
|
|
||||||
use graphene_core::raster::image::Image;
|
|
||||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
|
||||||
use graphene_core::registry::FutureWrapperNode;
|
use graphene_core::registry::FutureWrapperNode;
|
||||||
use graphene_core::transform::Transform;
|
use graphene_core::transform::Transform;
|
||||||
use graphene_core::value::ClonedNode;
|
use graphene_core::value::ClonedNode;
|
||||||
use graphene_core::{Ctx, Node};
|
use graphene_raster::bitmap::BitmapMut;
|
||||||
|
use graphene_raster::image::Image;
|
||||||
|
use graphene_raster::{CPU, Raster, RasterDataTable};
|
||||||
use graphene_raster_nodes::adjustments::blend_colors;
|
use graphene_raster_nodes::adjustments::blend_colors;
|
||||||
use graphene_raster_nodes::std_nodes::{empty_image, extend_image_to_bounds};
|
use graphene_raster_nodes::std_nodes::{empty_image, extend_image_to_bounds};
|
||||||
|
use graphene_svg_renderer::renderer::GraphicElementRendered;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct BrushStampGenerator<P: Pixel + Alpha> {
|
pub struct BrushStampGenerator<P: Pixel + Alpha> {
|
||||||
|
|
|
@ -27,13 +27,11 @@ rustc-hash = { workspace = true }
|
||||||
dyn-any = { workspace = true }
|
dyn-any = { workspace = true }
|
||||||
ctor = { workspace = true }
|
ctor = { workspace = true }
|
||||||
rand_chacha = { workspace = true }
|
rand_chacha = { workspace = true }
|
||||||
bezier-rs = { workspace = true }
|
|
||||||
specta = { workspace = true }
|
specta = { workspace = true }
|
||||||
rustybuzz = { workspace = true }
|
rustybuzz = { workspace = true }
|
||||||
image = { workspace = true }
|
image = { workspace = true }
|
||||||
half = { workspace = true }
|
half = { workspace = true }
|
||||||
tinyvec = { workspace = true }
|
tinyvec = { workspace = true }
|
||||||
kurbo = { workspace = true }
|
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
|
use log::warn;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, DynAny, specta::Type, serde::Serialize, serde::Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, DynAny, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::Color;
|
use crate::color::Color;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
|
||||||
pub trait BoundingBox {
|
pub trait BoundingBox {
|
||||||
|
|
|
@ -5,8 +5,6 @@ use std::fmt::Debug;
|
||||||
#[cfg(target_arch = "spirv")]
|
#[cfg(target_arch = "spirv")]
|
||||||
use spirv_std::num_traits::float::Float;
|
use spirv_std::num_traits::float::Float;
|
||||||
|
|
||||||
pub use crate::blending::*;
|
|
||||||
|
|
||||||
pub trait Linear {
|
pub trait Linear {
|
||||||
fn from_f32(x: f32) -> Self;
|
fn from_f32(x: f32) -> Self;
|
||||||
fn to_f32(self) -> f32;
|
fn to_f32(self) -> f32;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::raster::Color;
|
use crate::color::Color;
|
||||||
|
|
||||||
// RENDERING
|
// RENDERING
|
||||||
pub const LAYER_OUTLINE_STROKE_COLOR: Color = Color::BLACK;
|
pub const LAYER_OUTLINE_STROKE_COLOR: Color = Color::BLACK;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::raster_types::{CPU, RasterDataTable};
|
use crate::color::Color;
|
||||||
use crate::{Color, Ctx};
|
use crate::context::Ctx;
|
||||||
|
|
||||||
/// Meant for debugging purposes, not general use. Returns the size of the input type in bytes.
|
/// Meant for debugging purposes, not general use. Returns the size of the input type in bytes.
|
||||||
#[node_macro::node(category("Debug"))]
|
#[node_macro::node(category("Debug"))]
|
||||||
|
@ -19,8 +19,4 @@ fn unwrap<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<f32>, O
|
||||||
input.unwrap_or_default()
|
input.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Meant for debugging purposes, not general use. Clones the input value.
|
// FIXME am I allowed to just remove clone?
|
||||||
#[node_macro::node(category("Debug"))]
|
|
||||||
fn clone<'i, T: Clone + 'i>(_: impl Ctx, #[implementations(&RasterDataTable<CPU>)] value: &'i T) -> T {
|
|
||||||
value.clone()
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::Ctx;
|
use crate::context::Ctx;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DVec2, IVec2, UVec2};
|
use glam::{DVec2, IVec2, UVec2};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::Color;
|
use crate::color::Color;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::AlphaBlending;
|
use crate::blending::AlphaBlending;
|
||||||
use crate::uuid::NodeId;
|
use crate::uuid::NodeId;
|
||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
pub mod blending;
|
pub mod blending;
|
||||||
pub mod bounds;
|
pub mod bounds;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
|
@ -10,35 +7,26 @@ pub mod debug;
|
||||||
pub mod extract_xy;
|
pub mod extract_xy;
|
||||||
pub mod generic;
|
pub mod generic;
|
||||||
pub mod gradient;
|
pub mod gradient;
|
||||||
mod graphic_element;
|
|
||||||
pub mod instances;
|
pub mod instances;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod memo;
|
pub mod memo;
|
||||||
pub mod misc;
|
pub mod misc;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod raster;
|
|
||||||
pub mod raster_types;
|
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
pub mod structural;
|
pub mod structural;
|
||||||
pub mod text;
|
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
pub mod uuid;
|
pub mod uuid;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
pub mod vector;
|
|
||||||
|
|
||||||
pub use crate as graphene_core;
|
pub use crate as graphene_core;
|
||||||
pub use blending::*;
|
|
||||||
pub use context::*;
|
|
||||||
pub use ctor;
|
pub use ctor;
|
||||||
pub use dyn_any::{StaticTypeSized, WasmNotSend, WasmNotSync};
|
pub use dyn_any::{StaticTypeSized, WasmNotSend, WasmNotSync};
|
||||||
pub use graphic_element::*;
|
|
||||||
pub use memo::MemoHash;
|
pub use memo::MemoHash;
|
||||||
pub use num_traits;
|
pub use num_traits;
|
||||||
pub use raster::Color;
|
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
pub use std::borrow::Cow;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
pub use types::Cow;
|
|
||||||
|
|
||||||
// pub trait Node: for<'n> NodeIO<'n> {
|
// pub trait Node: for<'n> NodeIO<'n> {
|
||||||
/// The node trait allows for defining any node. Nodes can only take one call argument input, however they can store references to other nodes inside the struct.
|
/// The node trait allows for defining any node. Nodes can only take one call argument input, however they can store references to other nodes inside the struct.
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
pub mod bbox;
|
pub mod bbox;
|
||||||
pub mod math_ext;
|
|
||||||
pub mod quad;
|
pub mod quad;
|
||||||
pub mod rect;
|
pub mod rect;
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
use crate::Artboard;
|
|
||||||
use crate::math::bbox::AxisAlignedBbox;
|
|
||||||
pub use crate::vector::ReferencePoint;
|
|
||||||
use core::f64;
|
|
||||||
use glam::{DAffine2, DMat2, DVec2};
|
use glam::{DAffine2, DMat2, DVec2};
|
||||||
|
|
||||||
pub trait Transform {
|
pub trait Transform {
|
||||||
|
@ -31,16 +27,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
|
// Implementations for DAffine2
|
||||||
impl Transform for DAffine2 {
|
impl Transform for DAffine2 {
|
||||||
fn transform(&self) -> DAffine2 {
|
fn transform(&self) -> DAffine2 {
|
||||||
|
@ -110,13 +96,6 @@ impl Footprint {
|
||||||
quality: RenderQuality::Full,
|
quality: RenderQuality::Full,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn viewport_bounds_in_local_space(&self) -> AxisAlignedBbox {
|
|
||||||
let inverse = self.transform.inverse();
|
|
||||||
let start = inverse.transform_point2((0., 0.).into());
|
|
||||||
let end = inverse.transform_point2(self.resolution.as_dvec2());
|
|
||||||
AxisAlignedBbox { start, end }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scale(&self) -> DVec2 {
|
pub fn scale(&self) -> DVec2 {
|
||||||
self.transform.decompose_scale()
|
self.transform.decompose_scale()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
pub mod algorithms;
|
|
||||||
pub mod click_target;
|
|
||||||
pub mod generator_nodes;
|
|
||||||
pub mod misc;
|
|
||||||
mod reference_point;
|
|
||||||
pub mod style;
|
|
||||||
mod vector_data;
|
|
||||||
mod vector_nodes;
|
|
||||||
|
|
||||||
pub use bezier_rs;
|
|
||||||
pub use reference_point::*;
|
|
||||||
pub use style::PathStyle;
|
|
||||||
pub use vector_data::*;
|
|
||||||
pub use vector_nodes::*;
|
|
|
@ -14,6 +14,9 @@ serde = ["dep:serde"]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
dyn-any = { workspace = true }
|
dyn-any = { workspace = true }
|
||||||
graphene-core = { workspace = true }
|
graphene-core = { workspace = true }
|
||||||
|
graphene-raster = { workspace = true }
|
||||||
|
graphene-vector = { workspace = true }
|
||||||
|
graphene-element = { workspace = true }
|
||||||
node-macro = { workspace = true }
|
node-macro = { workspace = true }
|
||||||
bezier-rs = { workspace = true }
|
bezier-rs = { workspace = true }
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use graphene_core::GraphicGroupTable;
|
|
||||||
use graphene_core::blending::BlendMode;
|
use graphene_core::blending::BlendMode;
|
||||||
use graphene_core::color::Color;
|
use graphene_core::color::Color;
|
||||||
use graphene_core::context::Ctx;
|
use graphene_core::context::Ctx;
|
||||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
|
||||||
use graphene_core::registry::types::Percentage;
|
use graphene_core::registry::types::Percentage;
|
||||||
use graphene_core::vector::VectorDataTable;
|
use graphene_element::GraphicGroupTable;
|
||||||
|
use graphene_raster::{CPU, RasterDataTable};
|
||||||
|
use graphene_vector::VectorDataTable;
|
||||||
|
|
||||||
pub(super) trait MultiplyAlpha {
|
pub(super) trait MultiplyAlpha {
|
||||||
fn multiply_alpha(&mut self, factor: f64);
|
fn multiply_alpha(&mut self, factor: f64);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graphene_core::GraphicElement;
|
use graphene_core::color::Color;
|
||||||
use graphene_core::GraphicGroupTable;
|
|
||||||
use graphene_core::context::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, OwnedContextImpl};
|
use graphene_core::context::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, OwnedContextImpl};
|
||||||
use graphene_core::instances::{InstanceRef, Instances};
|
use graphene_core::instances::{InstanceRef, Instances};
|
||||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
use graphene_element::GraphicElement;
|
||||||
use graphene_core::vector::VectorDataTable;
|
use graphene_element::GraphicGroupTable;
|
||||||
|
use graphene_raster::{CPU, GPU, RasterDataTable};
|
||||||
|
use graphene_vector::VectorDataTable;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
|
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
|
||||||
|
@ -105,7 +106,7 @@ mod test {
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graphene_core::Node;
|
use graphene_core::Node;
|
||||||
use graphene_core::extract_xy::{ExtractXyNode, XY};
|
use graphene_core::extract_xy::{ExtractXyNode, XY};
|
||||||
use graphene_core::vector::VectorData;
|
use graphene_vector::VectorData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -4,3 +4,4 @@ pub mod conversion;
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub mod logic;
|
pub mod logic;
|
||||||
pub mod transform_nodes;
|
pub mod transform_nodes;
|
||||||
|
pub mod vector_element_nodes;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene_core::color::Color;
|
use graphene_core::color::Color;
|
||||||
use graphene_core::context::{Context, Ctx};
|
use graphene_core::context::{Context, Ctx};
|
||||||
use graphene_core::vector::VectorDataTable;
|
use graphene_vector::VectorDataTable;
|
||||||
|
|
||||||
#[node_macro::node(category("Debug"), name("Log to Console"))]
|
#[node_macro::node(category("Debug"), name("Log to Console"))]
|
||||||
fn log_to_console<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, VectorDataTable, DAffine2, Color, Option<Color>)] value: T) -> T {
|
fn log_to_console<T: std::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, VectorDataTable, DAffine2, Color, Option<Color>)] value: T) -> T {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use core::f64;
|
use core::f64;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene_core::GraphicGroupTable;
|
|
||||||
use graphene_core::context::{CloneVarArgs, Context, Ctx, ExtractAll, OwnedContextImpl};
|
use graphene_core::context::{CloneVarArgs, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||||
use graphene_core::instances::Instances;
|
use graphene_core::instances::Instances;
|
||||||
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
|
||||||
use graphene_core::transform::{ApplyTransform, Footprint, Transform};
|
use graphene_core::transform::{ApplyTransform, Footprint, Transform};
|
||||||
use graphene_core::vector::VectorDataTable;
|
use graphene_element::GraphicGroupTable;
|
||||||
|
use graphene_raster::{CPU, GPU, RasterDataTable};
|
||||||
|
use graphene_vector::VectorDataTable;
|
||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(category(""))]
|
||||||
async fn transform<T: 'n + 'static>(
|
async fn transform<T: 'n + 'static>(
|
||||||
|
|
538
node-graph/gelement-nodes/src/vector_element_nodes.rs
Normal file
538
node-graph/gelement-nodes/src/vector_element_nodes.rs
Normal file
|
@ -0,0 +1,538 @@
|
||||||
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphene_core::color::Color;
|
||||||
|
use graphene_core::context::Ctx;
|
||||||
|
use graphene_core::gradient::{Gradient, GradientStops};
|
||||||
|
use graphene_core::instances::{InstanceMut, Instances};
|
||||||
|
use graphene_core::registry::types::{Angle, IntegerCount, Multiplier, PixelSize, SeedValue};
|
||||||
|
use graphene_element::{GraphicElement, GraphicGroupTable};
|
||||||
|
use graphene_raster::{CPU, GPU, RasterDataTable};
|
||||||
|
use graphene_vector::reference_point::ReferencePoint;
|
||||||
|
use graphene_vector::style::{Fill, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||||
|
use graphene_vector::{VectorData, VectorDataTable};
|
||||||
|
use rand::{Rng, SeedableRng};
|
||||||
|
use std::f64::consts::TAU;
|
||||||
|
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||||
|
|
||||||
|
/// Implemented for types that can be converted to an iterator of vector data.
|
||||||
|
/// Used for the fill and stroke node so they can be used on VectorData or GraphicGroup
|
||||||
|
trait VectorDataTableIterMut {
|
||||||
|
fn vector_iter_mut(&mut self) -> impl Iterator<Item = InstanceMut<'_, VectorData>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VectorDataTableIterMut for GraphicGroupTable {
|
||||||
|
fn vector_iter_mut(&mut self) -> impl Iterator<Item = InstanceMut<'_, VectorData>> {
|
||||||
|
// Grab only the direct children
|
||||||
|
self.instance_mut_iter()
|
||||||
|
.filter_map(|element| element.instance.as_vector_data_mut())
|
||||||
|
.flat_map(move |vector_data| vector_data.instance_mut_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VectorDataTableIterMut for VectorDataTable {
|
||||||
|
fn vector_iter_mut(&mut self) -> impl Iterator<Item = InstanceMut<'_, VectorData>> {
|
||||||
|
self.instance_mut_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector))]
|
||||||
|
async fn assign_colors<T>(
|
||||||
|
_: impl Ctx,
|
||||||
|
#[implementations(GraphicGroupTable, VectorDataTable)]
|
||||||
|
#[widget(ParsedWidgetOverride::Hidden)]
|
||||||
|
/// The vector elements, or group of vector elements, to apply the fill and/or stroke style to.
|
||||||
|
mut vector_group: T,
|
||||||
|
#[default(true)]
|
||||||
|
/// Whether to style the fill.
|
||||||
|
fill: bool,
|
||||||
|
/// Whether to style the stroke.
|
||||||
|
stroke: bool,
|
||||||
|
#[widget(ParsedWidgetOverride::Custom = "assign_colors_gradient")]
|
||||||
|
/// The range of colors to select from.
|
||||||
|
gradient: GradientStops,
|
||||||
|
/// Whether to reverse the gradient.
|
||||||
|
reverse: bool,
|
||||||
|
/// Whether to randomize the color selection for each element from throughout the gradient.
|
||||||
|
randomize: bool,
|
||||||
|
#[widget(ParsedWidgetOverride::Custom = "assign_colors_seed")]
|
||||||
|
/// The seed used for randomization.
|
||||||
|
seed: SeedValue,
|
||||||
|
#[widget(ParsedWidgetOverride::Custom = "assign_colors_repeat_every")]
|
||||||
|
/// The number of elements to span across the gradient before repeating. A 0 value will span the entire gradient once.
|
||||||
|
repeat_every: u32,
|
||||||
|
) -> T
|
||||||
|
where
|
||||||
|
T: VectorDataTableIterMut + 'n + Send,
|
||||||
|
{
|
||||||
|
let length = vector_group.vector_iter_mut().count();
|
||||||
|
let gradient = if reverse { gradient.reversed() } else { gradient };
|
||||||
|
|
||||||
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
||||||
|
|
||||||
|
for (i, vector_data) in vector_group.vector_iter_mut().enumerate() {
|
||||||
|
let factor = match randomize {
|
||||||
|
true => rng.random::<f64>(),
|
||||||
|
false => match repeat_every {
|
||||||
|
0 => i as f64 / (length - 1).max(1) as f64,
|
||||||
|
1 => 0.,
|
||||||
|
_ => i as f64 % repeat_every as f64 / (repeat_every - 1) as f64,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let color = gradient.evaluate(factor);
|
||||||
|
|
||||||
|
if fill {
|
||||||
|
vector_data.instance.style.set_fill(Fill::Solid(color));
|
||||||
|
}
|
||||||
|
if stroke {
|
||||||
|
if let Some(stroke) = vector_data.instance.style.stroke().and_then(|stroke| stroke.with_color(&Some(color))) {
|
||||||
|
vector_data.instance.style.set_stroke(stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_group
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))]
|
||||||
|
async fn fill<F: Into<Fill> + 'n + Send, V>(
|
||||||
|
_: impl Ctx,
|
||||||
|
#[implementations(
|
||||||
|
VectorDataTable,
|
||||||
|
VectorDataTable,
|
||||||
|
VectorDataTable,
|
||||||
|
VectorDataTable,
|
||||||
|
GraphicGroupTable,
|
||||||
|
GraphicGroupTable,
|
||||||
|
GraphicGroupTable,
|
||||||
|
GraphicGroupTable
|
||||||
|
)]
|
||||||
|
/// The vector elements, or group of vector elements, to apply the fill to.
|
||||||
|
mut vector_data: V,
|
||||||
|
#[implementations(
|
||||||
|
Fill,
|
||||||
|
Option<Color>,
|
||||||
|
Color,
|
||||||
|
Gradient,
|
||||||
|
Fill,
|
||||||
|
Option<Color>,
|
||||||
|
Color,
|
||||||
|
Gradient,
|
||||||
|
)]
|
||||||
|
#[default(Color::BLACK)]
|
||||||
|
/// The fill to paint the path with.
|
||||||
|
fill: F,
|
||||||
|
_backup_color: Option<Color>,
|
||||||
|
_backup_gradient: Gradient,
|
||||||
|
) -> V
|
||||||
|
where
|
||||||
|
V: VectorDataTableIterMut + 'n + Send,
|
||||||
|
{
|
||||||
|
let fill: Fill = fill.into();
|
||||||
|
for vector in vector_data.vector_iter_mut() {
|
||||||
|
let mut fill = fill.clone();
|
||||||
|
if let Fill::Gradient(gradient) = &mut fill {
|
||||||
|
gradient.transform *= *vector.transform;
|
||||||
|
}
|
||||||
|
vector.instance.style.set_fill(fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_data
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies a stroke style to the vector data contained in the input.
|
||||||
|
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("stroke_properties"))]
|
||||||
|
async fn stroke<C: Into<Option<Color>> + 'n + Send, V>(
|
||||||
|
_: impl Ctx,
|
||||||
|
#[implementations(VectorDataTable, VectorDataTable, GraphicGroupTable, GraphicGroupTable)]
|
||||||
|
/// The vector elements, or group of vector elements, to apply the stroke to.
|
||||||
|
mut vector_data: Instances<V>,
|
||||||
|
#[implementations(
|
||||||
|
Option<Color>,
|
||||||
|
Color,
|
||||||
|
Option<Color>,
|
||||||
|
Color,
|
||||||
|
)]
|
||||||
|
#[default(Color::BLACK)]
|
||||||
|
/// The stroke color.
|
||||||
|
color: C,
|
||||||
|
#[default(2.)]
|
||||||
|
/// The stroke weight.
|
||||||
|
weight: f64,
|
||||||
|
/// The alignment of stroke to the path's centerline or (for closed shapes) the inside or outside of the shape.
|
||||||
|
align: StrokeAlign,
|
||||||
|
/// The shape of the stroke at open endpoints.
|
||||||
|
cap: StrokeCap,
|
||||||
|
/// The curvature of the bent stroke at sharp corners.
|
||||||
|
join: StrokeJoin,
|
||||||
|
#[default(4.)]
|
||||||
|
/// The threshold for when a miter-joined stroke is converted to a bevel-joined stroke when a sharp angle becomes pointier than this ratio.
|
||||||
|
miter_limit: f64,
|
||||||
|
/// The order to paint the stroke on top of the fill, or the fill on top of the stroke.
|
||||||
|
/// <https://svgwg.org/svg2-draft/painting.html#PaintOrderProperty>
|
||||||
|
paint_order: PaintOrder,
|
||||||
|
/// The stroke dash lengths. Each length forms a distance in a pattern where the first length is a dash, the second is a gap, and so on. If the list is an odd length, the pattern repeats with solid-gap roles reversed.
|
||||||
|
dash_lengths: Vec<f64>,
|
||||||
|
/// The phase offset distance from the starting point of the dash pattern.
|
||||||
|
dash_offset: f64,
|
||||||
|
) -> Instances<V>
|
||||||
|
where
|
||||||
|
Instances<V>: VectorDataTableIterMut + 'n + Send,
|
||||||
|
{
|
||||||
|
let stroke = Stroke {
|
||||||
|
color: color.into(),
|
||||||
|
weight,
|
||||||
|
dash_lengths,
|
||||||
|
dash_offset,
|
||||||
|
cap,
|
||||||
|
join,
|
||||||
|
join_miter_limit: miter_limit,
|
||||||
|
align,
|
||||||
|
transform: DAffine2::IDENTITY,
|
||||||
|
non_scaling: false,
|
||||||
|
paint_order,
|
||||||
|
};
|
||||||
|
|
||||||
|
for vector in vector_data.vector_iter_mut() {
|
||||||
|
let mut stroke = stroke.clone();
|
||||||
|
stroke.transform *= *vector.transform;
|
||||||
|
vector.instance.style.set_stroke(stroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_data
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||||
|
async fn repeat<I: 'n + Send + Clone>(
|
||||||
|
_: impl Ctx,
|
||||||
|
// TODO: Implement other GraphicElementRendered types.
|
||||||
|
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)] instance: Instances<I>,
|
||||||
|
#[default(100., 100.)]
|
||||||
|
// TODO: When using a custom Properties panel layout in document_node_definitions.rs and this default is set, the widget weirdly doesn't show up in the Properties panel. Investigation is needed.
|
||||||
|
direction: PixelSize,
|
||||||
|
angle: Angle,
|
||||||
|
#[default(4)] instances: IntegerCount,
|
||||||
|
) -> Instances<I> {
|
||||||
|
let angle = angle.to_radians();
|
||||||
|
let count = instances.max(1);
|
||||||
|
let total = (count - 1) as f64;
|
||||||
|
|
||||||
|
let mut result_table = Instances::<I>::default();
|
||||||
|
|
||||||
|
for index in 0..count {
|
||||||
|
let angle = index as f64 * angle / total;
|
||||||
|
let translation = index as f64 * direction / total;
|
||||||
|
let transform = DAffine2::from_angle(angle) * DAffine2::from_translation(translation);
|
||||||
|
|
||||||
|
for instance in instance.instance_ref_iter() {
|
||||||
|
let mut instance = instance.to_instance_cloned();
|
||||||
|
|
||||||
|
let local_translation = DAffine2::from_translation(instance.transform.translation);
|
||||||
|
let local_matrix = DAffine2::from_mat2(instance.transform.matrix2);
|
||||||
|
instance.transform = local_translation * transform * local_matrix;
|
||||||
|
|
||||||
|
result_table.push(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result_table
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||||
|
async fn circular_repeat<I: 'n + Send + Clone>(
|
||||||
|
_: impl Ctx,
|
||||||
|
// TODO: Implement other GraphicElementRendered types.
|
||||||
|
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)] instance: Instances<I>,
|
||||||
|
angle_offset: Angle,
|
||||||
|
#[default(5)] radius: f64,
|
||||||
|
#[default(5)] instances: IntegerCount,
|
||||||
|
) -> Instances<I> {
|
||||||
|
let count = instances.max(1);
|
||||||
|
|
||||||
|
let mut result_table = Instances::<I>::default();
|
||||||
|
|
||||||
|
for index in 0..count {
|
||||||
|
let angle = DAffine2::from_angle((TAU / count as f64) * index as f64 + angle_offset.to_radians());
|
||||||
|
let translation = DAffine2::from_translation(radius * DVec2::Y);
|
||||||
|
let transform = angle * translation;
|
||||||
|
|
||||||
|
for instance in instance.instance_ref_iter() {
|
||||||
|
let mut instance = instance.to_instance_cloned();
|
||||||
|
|
||||||
|
let local_translation = DAffine2::from_translation(instance.transform.translation);
|
||||||
|
let local_matrix = DAffine2::from_mat2(instance.transform.matrix2);
|
||||||
|
instance.transform = local_translation * transform * local_matrix;
|
||||||
|
|
||||||
|
result_table.push(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result_table
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(name("Copy to Points"), category("Instancing"), path(graphene_core::vector))]
|
||||||
|
async fn copy_to_points<I: 'n + Send + Clone>(
|
||||||
|
_: impl Ctx,
|
||||||
|
points: VectorDataTable,
|
||||||
|
#[expose]
|
||||||
|
/// Artwork to be copied and placed at each point.
|
||||||
|
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)]
|
||||||
|
instance: Instances<I>,
|
||||||
|
/// Minimum range of randomized sizes given to each instance.
|
||||||
|
#[default(1)]
|
||||||
|
#[range((0., 2.))]
|
||||||
|
#[unit("x")]
|
||||||
|
random_scale_min: Multiplier,
|
||||||
|
/// Maximum range of randomized sizes given to each instance.
|
||||||
|
#[default(1)]
|
||||||
|
#[range((0., 2.))]
|
||||||
|
#[unit("x")]
|
||||||
|
random_scale_max: Multiplier,
|
||||||
|
/// Bias for the probability distribution of randomized sizes (0 is uniform, negatives favor more of small sizes, positives favor more of large sizes).
|
||||||
|
#[range((-50., 50.))]
|
||||||
|
random_scale_bias: f64,
|
||||||
|
/// Seed to determine unique variations on all the randomized instance sizes.
|
||||||
|
random_scale_seed: SeedValue,
|
||||||
|
/// Range of randomized angles given to each instance, in degrees ranging from furthest clockwise to counterclockwise.
|
||||||
|
#[range((0., 360.))]
|
||||||
|
random_rotation: Angle,
|
||||||
|
/// Seed to determine unique variations on all the randomized instance angles.
|
||||||
|
random_rotation_seed: SeedValue,
|
||||||
|
) -> Instances<I> {
|
||||||
|
let mut result_table = Instances::<I>::default();
|
||||||
|
|
||||||
|
let random_scale_difference = random_scale_max - random_scale_min;
|
||||||
|
|
||||||
|
for point_instance in points.instance_iter() {
|
||||||
|
let mut scale_rng = rand::rngs::StdRng::seed_from_u64(random_scale_seed.into());
|
||||||
|
let mut rotation_rng = rand::rngs::StdRng::seed_from_u64(random_rotation_seed.into());
|
||||||
|
|
||||||
|
let do_scale = random_scale_difference.abs() > 1e-6;
|
||||||
|
let do_rotation = random_rotation.abs() > 1e-6;
|
||||||
|
|
||||||
|
let points_transform = point_instance.transform;
|
||||||
|
for &point in point_instance.instance.point_domain.positions() {
|
||||||
|
let translation = points_transform.transform_point2(point);
|
||||||
|
|
||||||
|
let rotation = if do_rotation {
|
||||||
|
let degrees = (rotation_rng.random::<f64>() - 0.5) * random_rotation;
|
||||||
|
degrees / 360. * TAU
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
};
|
||||||
|
|
||||||
|
let scale = if do_scale {
|
||||||
|
if random_scale_bias.abs() < 1e-6 {
|
||||||
|
// Linear
|
||||||
|
random_scale_min + scale_rng.random::<f64>() * random_scale_difference
|
||||||
|
} else {
|
||||||
|
// Weighted (see <https://www.desmos.com/calculator/gmavd3m9bd>)
|
||||||
|
let horizontal_scale_factor = 1. - 2_f64.powf(random_scale_bias);
|
||||||
|
let scale_factor = (1. - scale_rng.random::<f64>() * horizontal_scale_factor).log2() / random_scale_bias;
|
||||||
|
random_scale_min + scale_factor * random_scale_difference
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
random_scale_min
|
||||||
|
};
|
||||||
|
|
||||||
|
let transform = DAffine2::from_scale_angle_translation(DVec2::splat(scale), rotation, translation);
|
||||||
|
|
||||||
|
for mut instance in instance.instance_ref_iter().map(|instance| instance.to_instance_cloned()) {
|
||||||
|
let local_matrix = DAffine2::from_mat2(instance.transform.matrix2);
|
||||||
|
instance.transform = transform * local_matrix;
|
||||||
|
|
||||||
|
result_table.push(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result_table
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
||||||
|
async fn mirror<I: 'n + Send + Clone>(
|
||||||
|
_: impl Ctx,
|
||||||
|
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)] instance: Instances<I>,
|
||||||
|
#[default(ReferencePoint::Center)] relative_to_bounds: ReferencePoint,
|
||||||
|
offset: f64,
|
||||||
|
#[range((-90., 90.))] angle: Angle,
|
||||||
|
#[default(true)] keep_original: bool,
|
||||||
|
) -> Instances<I> {
|
||||||
|
let mut result_table = Instances::default();
|
||||||
|
|
||||||
|
// Normalize the direction vector
|
||||||
|
let normal = DVec2::from_angle(angle.to_radians());
|
||||||
|
|
||||||
|
// The mirror reference is based on the bounding box (at least for now, until we have proper local layer origins)
|
||||||
|
let Some(bounding_box) = instance.bounding_box(DAffine2::IDENTITY, false) else {
|
||||||
|
return result_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
let reference_point_location = relative_to_bounds.point_in_bounding_box((bounding_box[0], bounding_box[1]).into());
|
||||||
|
let mirror_reference_point = reference_point_location.map(|point| point + normal * offset);
|
||||||
|
|
||||||
|
// Create the reflection matrix
|
||||||
|
let reflection = DAffine2::from_mat2_translation(
|
||||||
|
glam::DMat2::from_cols(
|
||||||
|
DVec2::new(1. - 2. * normal.x * normal.x, -2. * normal.y * normal.x),
|
||||||
|
DVec2::new(-2. * normal.x * normal.y, 1. - 2. * normal.y * normal.y),
|
||||||
|
),
|
||||||
|
DVec2::ZERO,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Apply reflection around the reference point
|
||||||
|
let reflected_transform = if let Some(mirror_reference_point) = mirror_reference_point {
|
||||||
|
DAffine2::from_translation(mirror_reference_point) * reflection * DAffine2::from_translation(-mirror_reference_point)
|
||||||
|
} else {
|
||||||
|
reflection * DAffine2::from_translation(DVec2::from_angle(angle.to_radians()) * DVec2::splat(-offset))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add original instance depending on the keep_original flag
|
||||||
|
if keep_original {
|
||||||
|
for instance in instance.clone().instance_iter() {
|
||||||
|
result_table.push(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and add mirrored instance
|
||||||
|
for mut instance in instance.instance_iter() {
|
||||||
|
instance.transform = reflected_transform * instance.transform;
|
||||||
|
instance.source_node_id = None;
|
||||||
|
result_table.push(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
result_table
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||||
|
async fn flatten_path<I: 'n + Send>(_: impl Ctx, #[implementations(GraphicGroupTable, VectorDataTable)] graphic_group_input: Instances<I>) -> VectorDataTable {
|
||||||
|
// A node based solution to support passing through vector data could be a network node with a cache node connected to
|
||||||
|
// a Flatten Path connected to an if else node, another connection from the cache directly
|
||||||
|
// To the if else node, and another connection from the cache to a matches type node connected to the if else node.
|
||||||
|
fn flatten_group(graphic_group_table: &GraphicGroupTable, output: &mut InstanceMut<VectorData>) {
|
||||||
|
for (group_index, current_element) in graphic_group_table.instance_ref_iter().enumerate() {
|
||||||
|
match current_element.instance {
|
||||||
|
GraphicElement::VectorData(vector_data_table) => {
|
||||||
|
// Loop through every row of the VectorDataTable and concatenate each instance's subpath into the output VectorData instance.
|
||||||
|
for (vector_index, vector_data_instance) in vector_data_table.instance_ref_iter().enumerate() {
|
||||||
|
let other = vector_data_instance.instance;
|
||||||
|
let transform = *current_element.transform * *vector_data_instance.transform;
|
||||||
|
let node_id = current_element.source_node_id.map(|node_id| node_id.0).unwrap_or_default();
|
||||||
|
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
(group_index, vector_index, node_id).hash(&mut hasher);
|
||||||
|
let collision_hash_seed = hasher.finish();
|
||||||
|
|
||||||
|
output.instance.concat(other, transform, collision_hash_seed);
|
||||||
|
|
||||||
|
// Use the last encountered style as the output style
|
||||||
|
output.instance.style = vector_data_instance.instance.style.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GraphicElement::GraphicGroup(graphic_group) => {
|
||||||
|
let mut graphic_group = graphic_group.clone();
|
||||||
|
for instance in graphic_group.instance_mut_iter() {
|
||||||
|
*instance.transform = *current_element.transform * *instance.transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
flatten_group(&graphic_group, output);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a table with one instance of an empty VectorData, then get a mutable reference to it which we append flattened subpaths to
|
||||||
|
let mut output_table = VectorDataTable::new(VectorData::default());
|
||||||
|
let Some(mut output) = output_table.instance_mut_iter().next() else {
|
||||||
|
return output_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Flatten the graphic group input into the output VectorData instance
|
||||||
|
let base_graphic_group = GraphicGroupTable::new(graphic_group_input.to_graphic_element());
|
||||||
|
flatten_group(&base_graphic_group, &mut output);
|
||||||
|
|
||||||
|
// Return the single-row VectorDataTable containing the flattened VectorData subpaths
|
||||||
|
output_table
|
||||||
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("General"), path(graphene_core::vector))]
|
||||||
|
async fn count_elements<I>(_: impl Ctx, #[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>, RasterDataTable<GPU>)] source: Instances<I>) -> u64 {
|
||||||
|
source.instance_iter().count() as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use bezier_rs::Subpath;
|
||||||
|
use graphene_core::transform::Footprint;
|
||||||
|
use graphene_vector::PointId;
|
||||||
|
|
||||||
|
fn vector_node(data: Subpath<PointId>) -> VectorDataTable {
|
||||||
|
VectorDataTable::new(VectorData::from_subpath(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn repeat() {
|
||||||
|
let direction = DVec2::X * 1.5;
|
||||||
|
let instances = 3;
|
||||||
|
let repeated = super::repeat(Footprint::default(), vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE)), direction, 0., instances).await;
|
||||||
|
let vector_data = super::flatten_path(Footprint::default(), repeated).await;
|
||||||
|
let vector_data = vector_data.instance_ref_iter().next().unwrap().instance;
|
||||||
|
assert_eq!(vector_data.region_bezier_paths().count(), 3);
|
||||||
|
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
||||||
|
assert!((subpath.manipulator_groups()[0].anchor - direction * index as f64 / (instances - 1) as f64).length() < 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn repeat_transform_position() {
|
||||||
|
let direction = DVec2::new(12., 10.);
|
||||||
|
let instances = 8;
|
||||||
|
let repeated = super::repeat(Footprint::default(), vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE)), direction, 0., instances).await;
|
||||||
|
let vector_data = super::flatten_path(Footprint::default(), repeated).await;
|
||||||
|
let vector_data = vector_data.instance_ref_iter().next().unwrap().instance;
|
||||||
|
assert_eq!(vector_data.region_bezier_paths().count(), 8);
|
||||||
|
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
||||||
|
assert!((subpath.manipulator_groups()[0].anchor - direction * index as f64 / (instances - 1) as f64).length() < 1e-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn circular_repeat() {
|
||||||
|
let repeated = super::circular_repeat(Footprint::default(), vector_node(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE)), 45., 4., 8).await;
|
||||||
|
let vector_data = super::flatten_path(Footprint::default(), repeated).await;
|
||||||
|
let vector_data = vector_data.instance_ref_iter().next().unwrap().instance;
|
||||||
|
assert_eq!(vector_data.region_bezier_paths().count(), 8);
|
||||||
|
|
||||||
|
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
||||||
|
let expected_angle = (index as f64 + 1.) * 45.;
|
||||||
|
|
||||||
|
let center = (subpath.manipulator_groups()[0].anchor + subpath.manipulator_groups()[2].anchor) / 2.;
|
||||||
|
let actual_angle = DVec2::Y.angle_to(center).to_degrees();
|
||||||
|
|
||||||
|
assert!((actual_angle - expected_angle).abs() % 360. < 1e-5, "Expected {expected_angle} found {actual_angle}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn copy_to_points() {
|
||||||
|
let points = Subpath::new_rect(DVec2::NEG_ONE * 10., DVec2::ONE * 10.);
|
||||||
|
let instance = Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE);
|
||||||
|
|
||||||
|
let expected_points = VectorData::from_subpath(points.clone()).point_domain.positions().to_vec();
|
||||||
|
|
||||||
|
let copy_to_points = super::copy_to_points(Footprint::default(), vector_node(points), vector_node(instance), 1., 1., 0., 0, 0., 0).await;
|
||||||
|
let flatten_path = super::flatten_path(Footprint::default(), copy_to_points).await;
|
||||||
|
let flattened_copy_to_points = flatten_path.instance_ref_iter().next().unwrap().instance;
|
||||||
|
|
||||||
|
assert_eq!(flattened_copy_to_points.region_bezier_paths().count(), expected_points.len());
|
||||||
|
|
||||||
|
for (index, (_, subpath)) in flattened_copy_to_points.region_bezier_paths().enumerate() {
|
||||||
|
let offset = expected_points[index];
|
||||||
|
assert_eq!(
|
||||||
|
&subpath.anchors(),
|
||||||
|
&[offset + DVec2::NEG_ONE, offset + DVec2::new(1., -1.), offset + DVec2::ONE, offset + DVec2::new(-1., 1.),]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
node-graph/gelement/Cargo.toml
Normal file
22
node-graph/gelement/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
[package]
|
||||||
|
name = "graphene-element"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
description = "graphene element data format"
|
||||||
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Local dependencies
|
||||||
|
dyn-any = { workspace = true }
|
||||||
|
graphene-core = { workspace = true }
|
||||||
|
graphene-raster = { workspace = true }
|
||||||
|
graphene-vector = { workspace = true }
|
||||||
|
node-macro = { workspace = true }
|
||||||
|
bezier-rs = { workspace = true }
|
||||||
|
|
||||||
|
# Workspace dependencies
|
||||||
|
glam = { workspace = true }
|
||||||
|
specta = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
|
@ -1,14 +1,13 @@
|
||||||
use crate::blending::AlphaBlending;
|
|
||||||
use crate::bounds::BoundingBox;
|
|
||||||
use crate::color::Color;
|
|
||||||
use crate::instances::{Instance, Instances};
|
|
||||||
use crate::math::quad::Quad;
|
|
||||||
use crate::raster::image::Image;
|
|
||||||
use crate::raster_types::{CPU, GPU, Raster, RasterDataTable};
|
|
||||||
use crate::uuid::NodeId;
|
|
||||||
use crate::vector::{VectorData, VectorDataTable};
|
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
|
use graphene_core::blending::AlphaBlending;
|
||||||
|
use graphene_core::color::Color;
|
||||||
|
use graphene_core::instances::{Instance, Instances};
|
||||||
|
use graphene_core::transform::Transform;
|
||||||
|
use graphene_core::uuid::NodeId;
|
||||||
|
use graphene_raster::image::Image;
|
||||||
|
use graphene_raster::{CPU, GPU, Raster, RasterDataTable};
|
||||||
|
use graphene_vector::{VectorData, VectorDataTable};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
// TODO: Eventually remove this migration document upgrade code
|
// TODO: Eventually remove this migration document upgrade code
|
||||||
|
@ -246,6 +245,16 @@ pub struct Artboard {
|
||||||
pub clip: bool,
|
pub clip: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Artboard {
|
impl Default for Artboard {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(IVec2::ZERO, IVec2::new(1920, 1080))
|
Self::new(IVec2::ZERO, IVec2::new(1920, 1080))
|
2
node-graph/gelement/src/lib.rs
Normal file
2
node-graph/gelement/src/lib.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
mod graphic_element;
|
||||||
|
pub use graphic_element::*;
|
|
@ -1,7 +1,9 @@
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
|
use graphene_core::color::Color;
|
||||||
|
use graphene_core::context::Ctx;
|
||||||
use graphene_core::gradient::GradientStops;
|
use graphene_core::gradient::GradientStops;
|
||||||
|
use graphene_core::num_traits;
|
||||||
use graphene_core::registry::types::{Fraction, Percentage};
|
use graphene_core::registry::types::{Fraction, Percentage};
|
||||||
use graphene_core::{Color, Ctx, num_traits};
|
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use math_parser::ast;
|
use math_parser::ast;
|
||||||
use math_parser::context::{EvalContext, NothingMap, ValueProvider};
|
use math_parser::context::{EvalContext, NothingMap, ValueProvider};
|
||||||
|
|
|
@ -11,6 +11,8 @@ license = "MIT OR Apache-2.0"
|
||||||
dyn-any = { workspace = true }
|
dyn-any = { workspace = true }
|
||||||
bezier-rs = { workspace = true }
|
bezier-rs = { workspace = true }
|
||||||
graphene-core = { workspace = true }
|
graphene-core = { workspace = true }
|
||||||
|
graphene-vector = { workspace = true }
|
||||||
|
graphene-element = { workspace = true }
|
||||||
node-macro = { workspace = true }
|
node-macro = { workspace = true }
|
||||||
glam = { workspace = true }
|
glam = { workspace = true }
|
||||||
specta = { workspace = true }
|
specta = { workspace = true }
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphene_core::context::{CloneVarArgs, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||||
use graphene_core::instances::{Instance, InstanceRef};
|
use graphene_core::instances::{Instance, InstanceRef};
|
||||||
use graphene_core::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
use graphene_element::{GraphicElement, GraphicGroupTable};
|
||||||
use graphene_core::vector::style::Fill;
|
use graphene_vector::style::Fill;
|
||||||
use graphene_core::vector::{PointId, VectorData, VectorDataTable};
|
use graphene_vector::{PointId, VectorData, VectorDataTable};
|
||||||
use graphene_core::{Color, Ctx, GraphicElement, GraphicGroupTable};
|
|
||||||
pub use path_bool as path_bool_lib;
|
|
||||||
use path_bool::{FillRule, PathBooleanOperation};
|
use path_bool::{FillRule, PathBooleanOperation};
|
||||||
use std::ops::Mul;
|
use std::ops::Mul;
|
||||||
|
|
||||||
// TODO: Fix boolean ops to work by removing .transform() and .one_instnace_*() calls,
|
// TODO: Fix boolean ops to work by removing .transform() and .one_instnace_*() calls,
|
||||||
// TODO: since before we used a Vec of single-row tables and now we use a single table
|
// TODO: since before we used a Vec of single-row tables and now we use a single table
|
||||||
// TODO: with multiple rows while still assuming a single row for the boolean operations.
|
// TODO: with multiple rows while still assuming a single row for the boolean operations.
|
||||||
|
|
|
@ -20,8 +20,14 @@ graphene-element-nodes = { workspace = true }
|
||||||
graphene-path-bool = { workspace = true }
|
graphene-path-bool = { workspace = true }
|
||||||
graphene-brush = { workspace = true }
|
graphene-brush = { workspace = true }
|
||||||
graphene-application-io = { workspace = true }
|
graphene-application-io = { workspace = true }
|
||||||
|
graphene-element = { workspace = true }
|
||||||
|
graphene-math-nodes = { workspace = true }
|
||||||
|
graphene-raster = { workspace = true }
|
||||||
graphene-svg-renderer = { workspace = true }
|
graphene-svg-renderer = { workspace = true }
|
||||||
graphene-raster-nodes = { workspace = true }
|
graphene-raster-nodes = { workspace = true }
|
||||||
|
graphene-text = { workspace = true }
|
||||||
|
graphene-vector = { workspace = true }
|
||||||
|
graphene-vector-nodes = { workspace = true }
|
||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
|
|
|
@ -7,12 +7,13 @@ pub use glam::{DAffine2, DVec2, IVec2, UVec2};
|
||||||
use graphene_application_io::SurfaceFrame;
|
use graphene_application_io::SurfaceFrame;
|
||||||
use graphene_brush::brush_cache::BrushCache;
|
use graphene_brush::brush_cache::BrushCache;
|
||||||
use graphene_brush::brush_stroke::BrushStroke;
|
use graphene_brush::brush_stroke::BrushStroke;
|
||||||
use graphene_core::raster_types::CPU;
|
use graphene_core::color::Color;
|
||||||
use graphene_core::transform::ReferencePoint;
|
|
||||||
use graphene_core::uuid::NodeId;
|
use graphene_core::uuid::NodeId;
|
||||||
use graphene_core::vector::style::Fill;
|
use graphene_core::{MemoHash, Node, Type};
|
||||||
use graphene_core::{Color, MemoHash, Node, Type};
|
use graphene_raster::CPU;
|
||||||
use graphene_svg_renderer::RenderMetadata;
|
use graphene_svg_renderer::RenderMetadata;
|
||||||
|
use graphene_vector::reference_point::ReferencePoint;
|
||||||
|
use graphene_vector::style::Fill;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
@ -181,41 +182,41 @@ tagged_value! {
|
||||||
F64Array4([f64; 4]),
|
F64Array4([f64; 4]),
|
||||||
NodePath(Vec<NodeId>),
|
NodePath(Vec<NodeId>),
|
||||||
#[serde(alias = "ManipulatorGroupIds")] // TODO: Eventually remove this alias document upgrade code
|
#[serde(alias = "ManipulatorGroupIds")] // TODO: Eventually remove this alias document upgrade code
|
||||||
PointIds(Vec<graphene_core::vector::PointId>),
|
PointIds(Vec<graphene_vector::PointId>),
|
||||||
// ====================
|
// ====================
|
||||||
// GRAPHICAL DATA TYPES
|
// GRAPHICAL DATA TYPES
|
||||||
// ====================
|
// ====================
|
||||||
GraphicElement(graphene_core::GraphicElement),
|
GraphicElement(graphene_element::GraphicElement),
|
||||||
#[cfg_attr(target_arch = "wasm32", serde(deserialize_with = "graphene_core::vector::migrate_vector_data"))] // TODO: Eventually remove this migration document upgrade code
|
#[cfg_attr(target_arch = "wasm32", serde(deserialize_with = "graphene_core::vector::migrate_vector_data"))] // TODO: Eventually remove this migration document upgrade code
|
||||||
VectorData(graphene_core::vector::VectorDataTable),
|
VectorData(graphene_vector::VectorDataTable),
|
||||||
#[cfg_attr(target_arch = "wasm32", serde(alias = "ImageFrame", deserialize_with = "graphene_core::raster::image::migrate_image_frame"))] // TODO: Eventually remove this migration document upgrade code
|
#[cfg_attr(target_arch = "wasm32", serde(alias = "ImageFrame", deserialize_with = "graphene_core::raster::image::migrate_image_frame"))] // TODO: Eventually remove this migration document upgrade code
|
||||||
RasterData(graphene_core::raster_types::RasterDataTable<CPU>),
|
RasterData(graphene_raster::RasterDataTable<CPU>),
|
||||||
#[cfg_attr(target_arch = "wasm32", serde(deserialize_with = "graphene_core::migrate_graphic_group"))] // TODO: Eventually remove this migration document upgrade code
|
#[cfg_attr(target_arch = "wasm32", serde(deserialize_with = "graphene_core::migrate_graphic_group"))] // TODO: Eventually remove this migration document upgrade code
|
||||||
GraphicGroup(graphene_core::GraphicGroupTable),
|
GraphicGroup(graphene_element::GraphicGroupTable),
|
||||||
#[cfg_attr(target_arch = "wasm32", serde(deserialize_with = "graphene_core::migrate_artboard_group"))] // TODO: Eventually remove this migration document upgrade code
|
#[cfg_attr(target_arch = "wasm32", serde(deserialize_with = "graphene_core::migrate_artboard_group"))] // TODO: Eventually remove this migration document upgrade code
|
||||||
ArtboardGroup(graphene_core::ArtboardGroupTable),
|
ArtboardGroup(graphene_element::ArtboardGroupTable),
|
||||||
// ============
|
// ============
|
||||||
// STRUCT TYPES
|
// STRUCT TYPES
|
||||||
// ============
|
// ============
|
||||||
Artboard(graphene_core::Artboard),
|
Artboard(graphene_element::Artboard),
|
||||||
Image(graphene_core::raster::Image<Color>),
|
Image(graphene_raster::image::Image<Color>),
|
||||||
Color(graphene_core::raster::color::Color),
|
Color(Color),
|
||||||
OptionalColor(Option<graphene_core::raster::color::Color>),
|
OptionalColor(Option<Color>),
|
||||||
Palette(Vec<Color>),
|
Palette(Vec<Color>),
|
||||||
Subpaths(Vec<bezier_rs::Subpath<graphene_core::vector::PointId>>),
|
Subpaths(Vec<bezier_rs::Subpath<graphene_vector::PointId>>),
|
||||||
Fill(graphene_core::vector::style::Fill),
|
Fill(graphene_vector::style::Fill),
|
||||||
Stroke(graphene_core::vector::style::Stroke),
|
Stroke(graphene_vector::style::Stroke),
|
||||||
Gradient(graphene_core::vector::style::Gradient),
|
Gradient(graphene_core::gradient::Gradient),
|
||||||
#[serde(alias = "GradientPositions")] // TODO: Eventually remove this alias document upgrade code
|
#[serde(alias = "GradientPositions")] // TODO: Eventually remove this alias document upgrade code
|
||||||
GradientStops(graphene_core::vector::style::GradientStops),
|
GradientStops(graphene_core::gradient::GradientStops),
|
||||||
Font(graphene_core::text::Font),
|
Font(graphene_text::Font),
|
||||||
BrushStrokes(Vec<BrushStroke>),
|
BrushStrokes(Vec<BrushStroke>),
|
||||||
BrushCache(BrushCache),
|
BrushCache(BrushCache),
|
||||||
DocumentNode(DocumentNode),
|
DocumentNode(DocumentNode),
|
||||||
Curve(graphene_raster_nodes::curve::Curve),
|
Curve(graphene_raster_nodes::curve::Curve),
|
||||||
Footprint(graphene_core::transform::Footprint),
|
Footprint(graphene_core::transform::Footprint),
|
||||||
VectorModification(Box<graphene_core::vector::VectorModification>),
|
VectorModification(Box<graphene_vector_nodes::modification::VectorModification>),
|
||||||
FontCache(Arc<graphene_core::text::FontCache>),
|
FontCache(Arc<graphene_text::FontCache>),
|
||||||
// ==========
|
// ==========
|
||||||
// ENUM TYPES
|
// ENUM TYPES
|
||||||
// ==========
|
// ==========
|
||||||
|
@ -232,21 +233,21 @@ tagged_value! {
|
||||||
DomainWarpType(graphene_raster_nodes::adjustments::DomainWarpType),
|
DomainWarpType(graphene_raster_nodes::adjustments::DomainWarpType),
|
||||||
RelativeAbsolute(graphene_raster_nodes::adjustments::RelativeAbsolute),
|
RelativeAbsolute(graphene_raster_nodes::adjustments::RelativeAbsolute),
|
||||||
SelectiveColorChoice(graphene_raster_nodes::adjustments::SelectiveColorChoice),
|
SelectiveColorChoice(graphene_raster_nodes::adjustments::SelectiveColorChoice),
|
||||||
GridType(graphene_core::vector::misc::GridType),
|
GridType(graphene_vector_nodes::misc::GridType),
|
||||||
ArcType(graphene_core::vector::misc::ArcType),
|
ArcType(graphene_vector_nodes::misc::ArcType),
|
||||||
MergeByDistanceAlgorithm(graphene_core::vector::misc::MergeByDistanceAlgorithm),
|
MergeByDistanceAlgorithm(graphene_vector_nodes::misc::MergeByDistanceAlgorithm),
|
||||||
PointSpacingType(graphene_core::vector::misc::PointSpacingType),
|
PointSpacingType(graphene_vector_nodes::misc::PointSpacingType),
|
||||||
#[serde(alias = "LineCap")]
|
#[serde(alias = "LineCap")]
|
||||||
StrokeCap(graphene_core::vector::style::StrokeCap),
|
StrokeCap(graphene_vector::style::StrokeCap),
|
||||||
#[serde(alias = "LineJoin")]
|
#[serde(alias = "LineJoin")]
|
||||||
StrokeJoin(graphene_core::vector::style::StrokeJoin),
|
StrokeJoin(graphene_vector::style::StrokeJoin),
|
||||||
StrokeAlign(graphene_core::vector::style::StrokeAlign),
|
StrokeAlign(graphene_vector::style::StrokeAlign),
|
||||||
PaintOrder(graphene_core::vector::style::PaintOrder),
|
PaintOrder(graphene_vector::style::PaintOrder),
|
||||||
FillType(graphene_core::vector::style::FillType),
|
FillType(graphene_vector::style::FillType),
|
||||||
FillChoice(graphene_core::vector::style::FillChoice),
|
FillChoice(graphene_vector::style::FillChoice),
|
||||||
GradientType(graphene_core::vector::style::GradientType),
|
GradientType(graphene_core::gradient::GradientType),
|
||||||
ReferencePoint(graphene_core::transform::ReferencePoint),
|
ReferencePoint(graphene_vector::reference_point::ReferencePoint),
|
||||||
CentroidType(graphene_core::vector::misc::CentroidType),
|
CentroidType(graphene_vector_nodes::misc::CentroidType),
|
||||||
BooleanOperation(graphene_path_bool::BooleanOperation),
|
BooleanOperation(graphene_path_bool::BooleanOperation),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ gpu = ["interpreted-executor/gpu", "graphene-std/gpu", "wgpu-executor"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
graphene-core = { workspace = true }
|
|
||||||
graphene-std = { workspace = true }
|
graphene-std = { workspace = true }
|
||||||
interpreted-executor = { workspace = true }
|
interpreted-executor = { workspace = true }
|
||||||
graph-craft = { workspace = true, features = ["loading"] }
|
graph-craft = { workspace = true, features = ["loading"] }
|
||||||
|
|
|
@ -14,6 +14,7 @@ serde = ["dep:serde"]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
dyn-any = { workspace = true }
|
dyn-any = { workspace = true }
|
||||||
graphene-core = { workspace = true }
|
graphene-core = { workspace = true }
|
||||||
|
graphene-raster = { workspace = true }
|
||||||
node-macro = { workspace = true }
|
node-macro = { workspace = true }
|
||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
|
|
||||||
use crate::curve::CubicSplines;
|
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use graphene_core::Node;
|
use graphene_core::Node;
|
||||||
use graphene_core::blending::BlendMode;
|
use graphene_core::blending::BlendMode;
|
||||||
|
@ -8,12 +7,12 @@ use graphene_core::color::Color;
|
||||||
use graphene_core::color::Pixel;
|
use graphene_core::color::Pixel;
|
||||||
use graphene_core::context::Ctx;
|
use graphene_core::context::Ctx;
|
||||||
use graphene_core::gradient::GradientStops;
|
use graphene_core::gradient::GradientStops;
|
||||||
use graphene_core::raster::image::Image;
|
|
||||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
|
||||||
use graphene_core::registry::types::{Angle, Percentage, SignedPercentage};
|
use graphene_core::registry::types::{Angle, Percentage, SignedPercentage};
|
||||||
|
use graphene_raster::curve::CubicSplines;
|
||||||
|
use graphene_raster::image::Image;
|
||||||
|
use graphene_raster::{CPU, Raster, RasterDataTable};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
// TODO: Implement the following:
|
// TODO: Implement the following:
|
||||||
// Color Balance
|
// Color Balance
|
||||||
// Aims for interoperable compatibility with:
|
// Aims for interoperable compatibility with:
|
||||||
|
@ -1181,8 +1180,8 @@ fn color_overlay<T: Adjust<Color>>(
|
||||||
mod test {
|
mod test {
|
||||||
use graphene_core::blending::BlendMode;
|
use graphene_core::blending::BlendMode;
|
||||||
use graphene_core::color::Color;
|
use graphene_core::color::Color;
|
||||||
use graphene_core::raster::image::Image;
|
use graphene_raster::image::Image;
|
||||||
use graphene_core::raster_types::{Raster, RasterDataTable};
|
use graphene_raster::{Raster, RasterDataTable};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn color_overlay_multiply() {
|
async fn color_overlay_multiply() {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use graphene_core::context::Ctx;
|
use graphene_core::context::Ctx;
|
||||||
use graphene_core::raster::image::Image;
|
|
||||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
|
||||||
use graphene_core::registry::types::Percentage;
|
use graphene_core::registry::types::Percentage;
|
||||||
|
use graphene_raster::image::Image;
|
||||||
|
use graphene_raster::{CPU, Raster, RasterDataTable};
|
||||||
use image::{DynamicImage, GenericImage, GenericImageView, GrayImage, ImageBuffer, Luma, Rgba, RgbaImage};
|
use image::{DynamicImage, GenericImage, GenericImageView, GrayImage, ImageBuffer, Luma, Rgba, RgbaImage};
|
||||||
use ndarray::{Array2, ArrayBase, Dim, OwnedRepr};
|
use ndarray::{Array2, ArrayBase, Dim, OwnedRepr};
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use graphene_core::color::Color;
|
use graphene_core::color::Color;
|
||||||
use graphene_core::context::Ctx;
|
use graphene_core::context::Ctx;
|
||||||
use graphene_core::raster::image::Image;
|
|
||||||
use graphene_core::raster::{Bitmap, BitmapMut};
|
|
||||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
|
||||||
use graphene_core::registry::types::PixelLength;
|
use graphene_core::registry::types::PixelLength;
|
||||||
|
use graphene_raster::bitmap::{Bitmap, BitmapMut};
|
||||||
|
use graphene_raster::image::Image;
|
||||||
|
use graphene_raster::{CPU, Raster, RasterDataTable};
|
||||||
|
|
||||||
/// Blurs the image with a Gaussian or blur kernel filter.
|
/// Blurs the image with a Gaussian or blur kernel filter.
|
||||||
#[node_macro::node(category("Raster: Filter"))]
|
#[node_macro::node(category("Raster: Filter"))]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use graphene_core::color::Color;
|
use graphene_core::color::Color;
|
||||||
use graphene_core::context::Ctx;
|
use graphene_core::context::Ctx;
|
||||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
use graphene_raster::{CPU, RasterDataTable};
|
||||||
|
|
||||||
#[node_macro::node(category("Color"))]
|
#[node_macro::node(category("Color"))]
|
||||||
async fn image_color_palette(
|
async fn image_color_palette(
|
||||||
|
@ -65,8 +65,8 @@ async fn image_color_palette(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use graphene_core::raster::image::Image;
|
use graphene_raster::image::Image;
|
||||||
use graphene_core::raster_types::{Raster, RasterDataTable};
|
use graphene_raster::{Raster, RasterDataTable};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_image_color_palette() {
|
fn test_image_color_palette() {
|
||||||
|
|
|
@ -8,10 +8,11 @@ use graphene_core::color::{Alpha, AlphaMut, Channel, LinearChannel, Luminance, R
|
||||||
use graphene_core::context::{Ctx, ExtractFootprint};
|
use graphene_core::context::{Ctx, ExtractFootprint};
|
||||||
use graphene_core::instances::Instance;
|
use graphene_core::instances::Instance;
|
||||||
use graphene_core::math::bbox::Bbox;
|
use graphene_core::math::bbox::Bbox;
|
||||||
use graphene_core::raster::image::Image;
|
|
||||||
use graphene_core::raster::{Bitmap, BitmapMut};
|
|
||||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
|
||||||
use graphene_core::transform::Transform;
|
use graphene_core::transform::Transform;
|
||||||
|
use graphene_raster::bitmap::{Bitmap, BitmapMut};
|
||||||
|
use graphene_raster::image::Image;
|
||||||
|
use graphene_raster::transform::FootprintExt;
|
||||||
|
use graphene_raster::{CPU, Raster, RasterDataTable};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rand_chacha::ChaCha8Rng;
|
use rand_chacha::ChaCha8Rng;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
32
node-graph/graster/Cargo.toml
Normal file
32
node-graph/graster/Cargo.toml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
[package]
|
||||||
|
name = "graphene-raster"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
description = "graphene raster data format"
|
||||||
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["serde"]
|
||||||
|
serde = ["dep:serde"]
|
||||||
|
wgpu = ["dep:wgpu"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Local dependencies
|
||||||
|
dyn-any = { workspace = true }
|
||||||
|
graphene-core = { workspace = true }
|
||||||
|
node-macro = { workspace = true }
|
||||||
|
|
||||||
|
# Workspace dependencies
|
||||||
|
glam = { workspace = true }
|
||||||
|
specta = { workspace = true }
|
||||||
|
base64 = { workspace = true }
|
||||||
|
bytemuck = { workspace = true }
|
||||||
|
image = { workspace = true }
|
||||||
|
|
||||||
|
# Optional workspace dependencies
|
||||||
|
serde = { workspace = true, optional = true, features = ["derive"] }
|
||||||
|
wgpu = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
serde_json = { workspace = true }
|
|
@ -1,20 +1,4 @@
|
||||||
use crate::GraphicGroupTable;
|
use graphene_core::color::Pixel;
|
||||||
pub use crate::color::*;
|
|
||||||
use crate::raster_types::{CPU, RasterDataTable};
|
|
||||||
use crate::vector::VectorDataTable;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "spirv")]
|
|
||||||
use spirv_std::num_traits::float::Float;
|
|
||||||
|
|
||||||
/// as to not yet rename all references
|
|
||||||
pub mod color {
|
|
||||||
pub use super::*;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod image;
|
|
||||||
|
|
||||||
pub use self::image::Image;
|
|
||||||
|
|
||||||
pub trait Bitmap {
|
pub trait Bitmap {
|
||||||
type Pixel: Pixel;
|
type Pixel: Pixel;
|
|
@ -1,18 +1,19 @@
|
||||||
use super::Color;
|
use crate::bitmap::{Bitmap, BitmapMut};
|
||||||
use crate::AlphaBlending;
|
use crate::{CPU, Raster, RasterDataTable};
|
||||||
use crate::color::float_to_srgb_u8;
|
|
||||||
use crate::instances::{Instance, Instances};
|
|
||||||
use crate::raster_types::Raster;
|
|
||||||
use core::hash::{Hash, Hasher};
|
use core::hash::{Hash, Hasher};
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphene_core::blending::AlphaBlending;
|
||||||
|
use graphene_core::color::{Alpha, AssociatedAlpha, Color, Linear, Pixel, RGB, SRGBA8, Sample, float_to_srgb_u8};
|
||||||
|
use graphene_core::instances::{Instance, Instances};
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
mod base64_serde {
|
mod base64_serde {
|
||||||
//! Basic wrapper for [`serde`] to perform [`base64`] encoding
|
//! Basic wrapper for [`serde`] to perform [`base64`] encoding
|
||||||
|
|
||||||
use super::super::Pixel;
|
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
|
use graphene_core::color::Pixel;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
pub fn as_base64<S: Serializer, P: Pixel>(key: &[P], serializer: S) -> Result<S::Ok, S::Error> {
|
pub fn as_base64<S: Serializer, P: Pixel>(key: &[P], serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
@ -138,7 +139,6 @@ impl Image<Color> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use super::*;
|
|
||||||
impl<P: Alpha + RGB + AssociatedAlpha> Image<P>
|
impl<P: Alpha + RGB + AssociatedAlpha> Image<P>
|
||||||
where
|
where
|
||||||
P::ColorChannel: Linear,
|
P::ColorChannel: Linear,
|
||||||
|
@ -485,7 +485,6 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_image_serialization_roundtrip() {
|
fn test_image_serialization_roundtrip() {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Color;
|
|
||||||
let image = Image {
|
let image = Image {
|
||||||
width: 2,
|
width: 2,
|
||||||
height: 2,
|
height: 2,
|
5
node-graph/graster/src/lib.rs
Normal file
5
node-graph/graster/src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod bitmap;
|
||||||
|
pub mod image;
|
||||||
|
mod raster_types;
|
||||||
|
|
||||||
|
pub use raster_types::*;
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::Color;
|
use crate::image::Image;
|
||||||
use crate::bounds::BoundingBox;
|
|
||||||
use crate::instances::Instances;
|
|
||||||
use crate::math::quad::Quad;
|
|
||||||
use crate::raster::Image;
|
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphene_core::bounds::BoundingBox;
|
||||||
|
use graphene_core::color::Color;
|
||||||
|
use graphene_core::instances::Instances;
|
||||||
|
use graphene_core::math::quad::Quad;
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
@ -32,9 +32,14 @@ graphene-path-bool = { workspace = true }
|
||||||
graphene-math-nodes = { workspace = true }
|
graphene-math-nodes = { workspace = true }
|
||||||
graphene-svg-renderer = { workspace = true }
|
graphene-svg-renderer = { workspace = true }
|
||||||
graphene-application-io = { workspace = true }
|
graphene-application-io = { workspace = true }
|
||||||
|
graphene-element = { workspace = true }
|
||||||
graphene-element-nodes = { workspace = true }
|
graphene-element-nodes = { workspace = true }
|
||||||
|
graphene-raster = { workspace = true }
|
||||||
graphene-raster-nodes = { workspace = true }
|
graphene-raster-nodes = { workspace = true }
|
||||||
|
graphene-vector = { workspace = true }
|
||||||
|
graphene-vector-nodes = { workspace = true }
|
||||||
graphene-brush = { workspace = true }
|
graphene-brush = { workspace = true }
|
||||||
|
graphene-text = { workspace = true }
|
||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
fastnoise-lite = { workspace = true }
|
fastnoise-lite = { workspace = true }
|
||||||
|
@ -66,8 +71,5 @@ web-sys = { workspace = true, optional = true, features = [
|
||||||
"ImageBitmapRenderingContext",
|
"ImageBitmapRenderingContext",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
# Required dependencies
|
|
||||||
ndarray = "0.16.1"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
|
|
@ -6,13 +6,14 @@ pub mod wasm_application_io;
|
||||||
|
|
||||||
pub use graphene_application_io as application_io;
|
pub use graphene_application_io as application_io;
|
||||||
pub use graphene_brush as brush;
|
pub use graphene_brush as brush;
|
||||||
pub use graphene_core::vector;
|
|
||||||
pub use graphene_core::*;
|
pub use graphene_core::*;
|
||||||
|
pub use graphene_element as element;
|
||||||
pub use graphene_element_nodes as element_nodes;
|
pub use graphene_element_nodes as element_nodes;
|
||||||
pub use graphene_element_nodes::animation;
|
pub use graphene_element_nodes::animation;
|
||||||
pub use graphene_math_nodes as math_nodes;
|
pub use graphene_math_nodes as math_nodes;
|
||||||
pub use graphene_path_bool as path_bool;
|
pub use graphene_path_bool as path_bool;
|
||||||
pub use graphene_raster_nodes as raster_nodes;
|
pub use graphene_raster_nodes as raster_nodes;
|
||||||
|
pub use graphene_vector as vector;
|
||||||
|
|
||||||
/// stop gap solutions until all paths have been replaced with their absolute ones
|
/// stop gap solutions until all paths have been replaced with their absolute ones
|
||||||
pub mod renderer {
|
pub mod renderer {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::vector::{VectorData, VectorDataTable};
|
|
||||||
use graph_craft::wasm_application_io::WasmEditorApi;
|
use graph_craft::wasm_application_io::WasmEditorApi;
|
||||||
use graphene_core::Ctx;
|
use graphene_core::context::Ctx;
|
||||||
pub use graphene_core::text::*;
|
pub use graphene_text::*;
|
||||||
|
use graphene_vector::{VectorData, VectorDataTable};
|
||||||
|
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(category(""))]
|
||||||
fn text<'i: 'n>(
|
fn text<'i: 'n>(
|
||||||
|
|
|
@ -13,11 +13,17 @@ vello = ["dep:vello", "bezier-rs/kurbo"]
|
||||||
# Local dependencies
|
# Local dependencies
|
||||||
dyn-any = { workspace = true }
|
dyn-any = { workspace = true }
|
||||||
graphene-core = { workspace = true }
|
graphene-core = { workspace = true }
|
||||||
|
graphene-raster = { workspace = true }
|
||||||
|
graphene-element = { workspace = true }
|
||||||
|
graphene-vector = { workspace = true }
|
||||||
|
node-macro = { workspace = true }
|
||||||
bezier-rs = { workspace = true }
|
bezier-rs = { workspace = true }
|
||||||
|
|
||||||
# Workspace dependencies
|
# Workspace dependencies
|
||||||
glam = { workspace = true }
|
glam = { workspace = true }
|
||||||
|
specta = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
num-traits = { workspace = true }
|
num-traits = { workspace = true }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use graphene_core::vector::PointId;
|
use graphene_vector::PointId;
|
||||||
|
|
||||||
pub fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<PointId>> {
|
pub fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<PointId>> {
|
||||||
let mut subpaths = Vec::new();
|
let mut subpaths = Vec::new();
|
||||||
|
|
|
@ -3,8 +3,8 @@ use glam::{DAffine2, DVec2};
|
||||||
use graphene_core::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
|
use graphene_core::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
|
||||||
use graphene_core::gradient::{Gradient, GradientType};
|
use graphene_core::gradient::{Gradient, GradientType};
|
||||||
use graphene_core::uuid::generate_uuid;
|
use graphene_core::uuid::generate_uuid;
|
||||||
use graphene_core::vector::style::{Fill, PaintOrder, PathStyle, Stroke, StrokeAlign, StrokeCap, StrokeJoin, ViewMode};
|
use graphene_core::view_mode::ViewMode;
|
||||||
use std::fmt::Write;
|
use graphene_vector::style::{Fill, PaintOrder, PathStyle, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||||
|
|
||||||
pub trait RenderExt {
|
pub trait RenderExt {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
use crate::render_ext::RenderExt;
|
use crate::render_ext::RenderExt;
|
||||||
use crate::to_peniko::BlendModeExt;
|
use base64::Engine;
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene_core::blending::BlendMode;
|
use graphene_core::blending::BlendMode;
|
||||||
use graphene_core::bounds::BoundingBox;
|
|
||||||
use graphene_core::color::Color;
|
use graphene_core::color::Color;
|
||||||
use graphene_core::instances::Instance;
|
use graphene_core::instances::Instance;
|
||||||
use graphene_core::math::quad::Quad;
|
use graphene_core::math::quad::Quad;
|
||||||
use graphene_core::raster::Image;
|
use graphene_core::math::rect::Rect;
|
||||||
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
|
||||||
use graphene_core::transform::{Footprint, Transform};
|
use graphene_core::transform::{Footprint, Transform};
|
||||||
use graphene_core::uuid::{NodeId, generate_uuid};
|
use graphene_core::uuid::{NodeId, generate_uuid};
|
||||||
use graphene_core::vector::VectorDataTable;
|
use graphene_core::view_mode::ViewMode;
|
||||||
use graphene_core::vector::click_target::{ClickTarget, FreePoint};
|
use graphene_element::{Artboard, ArtboardGroupTable, GraphicElement, GraphicGroupTable};
|
||||||
use graphene_core::vector::style::{Fill, Stroke, StrokeAlign, ViewMode};
|
use graphene_raster::image::Image;
|
||||||
use graphene_core::{AlphaBlending, Artboard, ArtboardGroupTable, GraphicElement, GraphicGroupTable};
|
use graphene_raster::{CPU, GPU, RasterDataTable};
|
||||||
|
use graphene_vector::VectorDataTable;
|
||||||
|
use graphene_vector::click_target::{ClickTarget, FreePoint};
|
||||||
|
use graphene_vector::style::{Fill, Stroke, StrokeAlign};
|
||||||
use num_traits::Zero;
|
use num_traits::Zero;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
28
node-graph/gtext/Cargo.toml
Normal file
28
node-graph/gtext/Cargo.toml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
[package]
|
||||||
|
name = "graphene-text"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
description = "graphene text nodes"
|
||||||
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["serde"]
|
||||||
|
serde = ["dep:serde"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Local dependencies
|
||||||
|
dyn-any = { workspace = true }
|
||||||
|
bezier-rs = { workspace = true }
|
||||||
|
graphene-core = { workspace = true }
|
||||||
|
graphene-vector = { workspace = true }
|
||||||
|
node-macro = { workspace = true }
|
||||||
|
|
||||||
|
# Workspace dependencies
|
||||||
|
kurbo = { workspace = true }
|
||||||
|
glam = { workspace = true }
|
||||||
|
specta = { workspace = true }
|
||||||
|
rustybuzz = { workspace = true }
|
||||||
|
|
||||||
|
# Optional workspace dependencies
|
||||||
|
serde = { workspace = true, optional = true, features = ["derive"] }
|
|
@ -1,4 +1,5 @@
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
|
use graphene_core::consts::{DEFAULT_FONT_FAMILY, DEFAULT_FONT_STYLE};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// A font type (storing font family and font style and an optional preview URL)
|
/// A font type (storing font family and font style and an optional preview URL)
|
||||||
|
@ -16,7 +17,7 @@ impl Font {
|
||||||
}
|
}
|
||||||
impl Default for Font {
|
impl Default for Font {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(crate::consts::DEFAULT_FONT_FAMILY.into(), crate::consts::DEFAULT_FONT_STYLE.into())
|
Self::new(DEFAULT_FONT_FAMILY.into(), DEFAULT_FONT_STYLE.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// A cache of all loaded font data and preview urls along with the default font (send from `init_app` in `editor_api.rs`)
|
/// A cache of all loaded font data and preview urls along with the default font (send from `init_app` in `editor_api.rs`)
|
||||||
|
@ -33,9 +34,7 @@ impl FontCache {
|
||||||
if self.font_file_data.contains_key(font) {
|
if self.font_file_data.contains_key(font) {
|
||||||
Some(font)
|
Some(font)
|
||||||
} else {
|
} else {
|
||||||
self.font_file_data
|
self.font_file_data.keys().find(|font| font.font_family == DEFAULT_FONT_FAMILY && font.font_style == DEFAULT_FONT_STYLE)
|
||||||
.keys()
|
|
||||||
.find(|font| font.font_family == crate::consts::DEFAULT_FONT_FAMILY && font.font_style == crate::consts::DEFAULT_FONT_STYLE)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::vector::PointId;
|
|
||||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
|
use graphene_vector::PointId;
|
||||||
use rustybuzz::ttf_parser::{GlyphId, OutlineBuilder};
|
use rustybuzz::ttf_parser::{GlyphId, OutlineBuilder};
|
||||||
use rustybuzz::{GlyphBuffer, UnicodeBuffer};
|
use rustybuzz::{GlyphBuffer, UnicodeBuffer};
|
||||||
|
|
38
node-graph/gvector-nodes/Cargo.toml
Normal file
38
node-graph/gvector-nodes/Cargo.toml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
[package]
|
||||||
|
name = "graphene-vector-nodes"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
description = "graphene vector nodes"
|
||||||
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["serde"]
|
||||||
|
serde = [
|
||||||
|
"dep:serde",
|
||||||
|
"bezier-rs/serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Local dependencies
|
||||||
|
dyn-any = { workspace = true }
|
||||||
|
bezier-rs = { workspace = true }
|
||||||
|
graphene-core = { workspace = true }
|
||||||
|
graphene-vector = { workspace = true }
|
||||||
|
node-macro = { workspace = true }
|
||||||
|
|
||||||
|
# Workspace dependencies
|
||||||
|
kurbo = { workspace = true }
|
||||||
|
glam = { workspace = true }
|
||||||
|
specta = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
|
rustc-hash = { workspace = true }
|
||||||
|
petgraph = { workspace = true }
|
||||||
|
rand = { workspace = true }
|
||||||
|
|
||||||
|
# Optional workspace dependencies
|
||||||
|
serde = { workspace = true, optional = true, features = ["derive"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
# Workspace dependencies
|
||||||
|
tokio = { workspace = true }
|
|
@ -1,5 +1,5 @@
|
||||||
use super::poisson_disk::poisson_disk_sample;
|
use super::poisson_disk::poisson_disk_sample;
|
||||||
use crate::vector::misc::{PointSpacingType, dvec2_to_point};
|
use crate::misc::{PointSpacingType, dvec2_to_point};
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveDeriv, PathEl, PathSeg, Point, Rect, Shape};
|
use kurbo::{BezPath, DEFAULT_ACCURACY, Line, ParamCurve, ParamCurveDeriv, PathEl, PathSeg, Point, Rect, Shape};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::vector::{PointDomain, PointId, SegmentDomain, VectorData, VectorDataIndex};
|
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphene_vector::{PointDomain, PointId, SegmentDomain, VectorData, VectorDataIndex};
|
||||||
use petgraph::prelude::UnGraphMap;
|
use petgraph::prelude::UnGraphMap;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::vector::PointId;
|
|
||||||
use bezier_rs::{Bezier, BezierHandles, Join, Subpath, TValue};
|
use bezier_rs::{Bezier, BezierHandles, Join, Subpath, TValue};
|
||||||
|
use graphene_vector::PointId;
|
||||||
|
|
||||||
/// Value to control smoothness and mathematical accuracy to offset a cubic Bezier.
|
/// Value to control smoothness and mathematical accuracy to offset a cubic Bezier.
|
||||||
const CUBIC_REGULARIZATION_ACCURACY: f64 = 0.5;
|
const CUBIC_REGULARIZATION_ACCURACY: f64 = 0.5;
|
|
@ -141,7 +141,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn closed_spline() {
|
fn closed_spline() {
|
||||||
use crate::vector::misc::{dvec2_to_point, point_to_dvec2};
|
use graphene_vector::{dvec2_to_point, point_to_dvec2};
|
||||||
use kurbo::{BezPath, ParamCurve, ParamCurveDeriv};
|
use kurbo::{BezPath, ParamCurve, ParamCurveDeriv};
|
||||||
|
|
||||||
// These points are just chosen arbitrary
|
// These points are just chosen arbitrary
|
|
@ -1,10 +1,9 @@
|
||||||
use super::misc::{ArcType, AsU64, GridType};
|
use super::misc::{ArcType, AsU64, GridType};
|
||||||
use super::{PointId, SegmentId, StrokeId};
|
|
||||||
use crate::Ctx;
|
|
||||||
use crate::registry::types::{Angle, PixelSize};
|
|
||||||
use crate::vector::{HandleId, VectorData, VectorDataTable};
|
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
|
use graphene_core::context::Ctx;
|
||||||
|
use graphene_core::registry::types::{Angle, PixelSize};
|
||||||
|
use graphene_vector::{HandleId, PointId, SegmentId, StrokeId, VectorData, VectorDataTable};
|
||||||
|
|
||||||
trait CornerRadius {
|
trait CornerRadius {
|
||||||
fn generate(self, size: DVec2, clamped: bool) -> VectorDataTable;
|
fn generate(self, size: DVec2, clamped: bool) -> VectorDataTable;
|
5
node-graph/gvector-nodes/src/lib.rs
Normal file
5
node-graph/gvector-nodes/src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod algorithms;
|
||||||
|
pub mod generator_nodes;
|
||||||
|
pub mod misc;
|
||||||
|
pub mod modification;
|
||||||
|
pub mod vector_nodes;
|
|
@ -1,12 +1,20 @@
|
||||||
use super::*;
|
use crate::misc::point_to_dvec2;
|
||||||
use crate::Ctx;
|
|
||||||
use crate::instances::Instance;
|
|
||||||
use crate::uuid::generate_uuid;
|
|
||||||
use bezier_rs::BezierHandles;
|
use bezier_rs::BezierHandles;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
|
use glam::DVec2;
|
||||||
|
use graphene_core::context::Ctx;
|
||||||
|
use graphene_core::instances::Instance;
|
||||||
|
use graphene_core::uuid::generate_uuid;
|
||||||
|
use graphene_vector::{FillId, HandleId, HandleType, PointDomain, PointId, RegionDomain, RegionId, SegmentDomain, SegmentId, StrokeId, VectorData, VectorDataTable};
|
||||||
use kurbo::{BezPath, PathEl, Point};
|
use kurbo::{BezPath, PathEl, Point};
|
||||||
|
use log::warn;
|
||||||
|
use serde::de::{SeqAccess, Visitor};
|
||||||
|
use serde::ser::SerializeSeq;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::fmt;
|
||||||
use std::hash::BuildHasher;
|
use std::hash::BuildHasher;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
/// Represents a procedural change to the [`PointDomain`] in [`VectorData`].
|
/// Represents a procedural change to the [`PointDomain`] in [`VectorData`].
|
||||||
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
@ -434,11 +442,6 @@ async fn path_modify(_ctx: impl Ctx, mut vector_data: VectorDataTable, modificat
|
||||||
|
|
||||||
// Do we want to enforce that all serialized/deserialized hashmaps are a vec of tuples?
|
// Do we want to enforce that all serialized/deserialized hashmaps are a vec of tuples?
|
||||||
// TODO: Eventually remove this document upgrade code
|
// TODO: Eventually remove this document upgrade code
|
||||||
use serde::de::{SeqAccess, Visitor};
|
|
||||||
use serde::ser::SerializeSeq;
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
||||||
use std::fmt;
|
|
||||||
use std::hash::Hash;
|
|
||||||
pub fn serialize_hashmap<K, V, S, H>(hashmap: &HashMap<K, V, H>, serializer: S) -> Result<S::Ok, S::Error>
|
pub fn serialize_hashmap<K, V, S, H>(hashmap: &HashMap<K, V, H>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
K: Serialize + Eq + Hash,
|
K: Serialize + Eq + Hash,
|
|
@ -1,421 +1,26 @@
|
||||||
use super::algorithms::bezpath_algorithms::{self, position_on_bezpath, sample_polyline_on_bezpath, split_bezpath, tangent_on_bezpath};
|
use super::algorithms::bezpath_algorithms::{self, position_on_bezpath, sample_polyline_on_bezpath, split_bezpath, tangent_on_bezpath};
|
||||||
use super::algorithms::offset_subpath::offset_subpath;
|
use super::algorithms::offset_subpath::offset_subpath;
|
||||||
use super::algorithms::spline::{solve_spline_first_handle_closed, solve_spline_first_handle_open};
|
use super::algorithms::spline::{solve_spline_first_handle_closed, solve_spline_first_handle_open};
|
||||||
use super::misc::{CentroidType, point_to_dvec2};
|
use super::misc::{CentroidType, MergeByDistanceAlgorithm, PointSpacingType, dvec2_to_point, point_to_dvec2};
|
||||||
use super::style::{Fill, Gradient, GradientStops, Stroke};
|
use crate::modification::VectorDataExt;
|
||||||
use super::{PointId, SegmentDomain, SegmentId, StrokeId, VectorData, VectorDataExt, VectorDataTable};
|
|
||||||
use crate::bounds::BoundingBox;
|
|
||||||
use crate::instances::{Instance, InstanceMut, Instances};
|
|
||||||
use crate::raster_types::{CPU, GPU, RasterDataTable};
|
|
||||||
use crate::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, PixelSize, SeedValue};
|
|
||||||
use crate::transform::{Footprint, ReferencePoint, Transform};
|
|
||||||
use crate::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
|
||||||
use crate::vector::misc::{MergeByDistanceAlgorithm, PointSpacingType};
|
|
||||||
use crate::vector::style::{PaintOrder, StrokeAlign, StrokeCap, StrokeJoin};
|
|
||||||
use crate::vector::{FillId, PointDomain, RegionId};
|
|
||||||
use crate::{CloneVarArgs, Color, Context, Ctx, ExtractAll, GraphicElement, GraphicGroupTable, OwnedContextImpl};
|
|
||||||
use bezier_rs::{Join, ManipulatorGroup, Subpath};
|
use bezier_rs::{Join, ManipulatorGroup, Subpath};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, ParamCurve, PathEl, PathSeg, Shape};
|
use graphene_core::color::Color;
|
||||||
|
use graphene_core::context::{CloneVarArgs, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||||
|
use graphene_core::gradient::{Gradient, GradientStops};
|
||||||
|
use graphene_core::instances::{Instance, InstanceMut, Instances};
|
||||||
|
use graphene_core::registry::types::{Angle, Fraction, IntegerCount, Length, Multiplier, Percentage, PixelLength, PixelSize, SeedValue};
|
||||||
|
use graphene_core::transform::{Footprint, Transform};
|
||||||
|
use graphene_vector::style::{Fill, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||||
|
use graphene_vector::{FillId, PointDomain, PointId, RegionId, SegmentDomain, SegmentId, StrokeId, VectorData, VectorDataTable};
|
||||||
|
use kurbo::{Affine, BezPath, DEFAULT_ACCURACY, ParamCurve, PathEl, PathSeg, Point, Shape};
|
||||||
|
use log::warn;
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
use std::f64::consts::TAU;
|
use std::f64::consts::TAU;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
/// Implemented for types that can be converted to an iterator of vector data.
|
|
||||||
/// Used for the fill and stroke node so they can be used on VectorData or GraphicGroup
|
|
||||||
trait VectorDataTableIterMut {
|
|
||||||
fn vector_iter_mut(&mut self) -> impl Iterator<Item = InstanceMut<'_, VectorData>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VectorDataTableIterMut for GraphicGroupTable {
|
|
||||||
fn vector_iter_mut(&mut self) -> impl Iterator<Item = InstanceMut<'_, VectorData>> {
|
|
||||||
// Grab only the direct children
|
|
||||||
self.instance_mut_iter()
|
|
||||||
.filter_map(|element| element.instance.as_vector_data_mut())
|
|
||||||
.flat_map(move |vector_data| vector_data.instance_mut_iter())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VectorDataTableIterMut for VectorDataTable {
|
|
||||||
fn vector_iter_mut(&mut self) -> impl Iterator<Item = InstanceMut<'_, VectorData>> {
|
|
||||||
self.instance_mut_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector))]
|
|
||||||
async fn assign_colors<T>(
|
|
||||||
_: impl Ctx,
|
|
||||||
#[implementations(GraphicGroupTable, VectorDataTable)]
|
|
||||||
#[widget(ParsedWidgetOverride::Hidden)]
|
|
||||||
/// The vector elements, or group of vector elements, to apply the fill and/or stroke style to.
|
|
||||||
mut vector_group: T,
|
|
||||||
#[default(true)]
|
|
||||||
/// Whether to style the fill.
|
|
||||||
fill: bool,
|
|
||||||
/// Whether to style the stroke.
|
|
||||||
stroke: bool,
|
|
||||||
#[widget(ParsedWidgetOverride::Custom = "assign_colors_gradient")]
|
|
||||||
/// The range of colors to select from.
|
|
||||||
gradient: GradientStops,
|
|
||||||
/// Whether to reverse the gradient.
|
|
||||||
reverse: bool,
|
|
||||||
/// Whether to randomize the color selection for each element from throughout the gradient.
|
|
||||||
randomize: bool,
|
|
||||||
#[widget(ParsedWidgetOverride::Custom = "assign_colors_seed")]
|
|
||||||
/// The seed used for randomization.
|
|
||||||
seed: SeedValue,
|
|
||||||
#[widget(ParsedWidgetOverride::Custom = "assign_colors_repeat_every")]
|
|
||||||
/// The number of elements to span across the gradient before repeating. A 0 value will span the entire gradient once.
|
|
||||||
repeat_every: u32,
|
|
||||||
) -> T
|
|
||||||
where
|
|
||||||
T: VectorDataTableIterMut + 'n + Send,
|
|
||||||
{
|
|
||||||
let length = vector_group.vector_iter_mut().count();
|
|
||||||
let gradient = if reverse { gradient.reversed() } else { gradient };
|
|
||||||
|
|
||||||
let mut rng = rand::rngs::StdRng::seed_from_u64(seed.into());
|
|
||||||
|
|
||||||
for (i, vector_data) in vector_group.vector_iter_mut().enumerate() {
|
|
||||||
let factor = match randomize {
|
|
||||||
true => rng.random::<f64>(),
|
|
||||||
false => match repeat_every {
|
|
||||||
0 => i as f64 / (length - 1).max(1) as f64,
|
|
||||||
1 => 0.,
|
|
||||||
_ => i as f64 % repeat_every as f64 / (repeat_every - 1) as f64,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let color = gradient.evaluate(factor);
|
|
||||||
|
|
||||||
if fill {
|
|
||||||
vector_data.instance.style.set_fill(Fill::Solid(color));
|
|
||||||
}
|
|
||||||
if stroke {
|
|
||||||
if let Some(stroke) = vector_data.instance.style.stroke().and_then(|stroke| stroke.with_color(&Some(color))) {
|
|
||||||
vector_data.instance.style.set_stroke(stroke);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_group
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("fill_properties"))]
|
|
||||||
async fn fill<F: Into<Fill> + 'n + Send, V>(
|
|
||||||
_: impl Ctx,
|
|
||||||
#[implementations(
|
|
||||||
VectorDataTable,
|
|
||||||
VectorDataTable,
|
|
||||||
VectorDataTable,
|
|
||||||
VectorDataTable,
|
|
||||||
GraphicGroupTable,
|
|
||||||
GraphicGroupTable,
|
|
||||||
GraphicGroupTable,
|
|
||||||
GraphicGroupTable
|
|
||||||
)]
|
|
||||||
/// The vector elements, or group of vector elements, to apply the fill to.
|
|
||||||
mut vector_data: V,
|
|
||||||
#[implementations(
|
|
||||||
Fill,
|
|
||||||
Option<Color>,
|
|
||||||
Color,
|
|
||||||
Gradient,
|
|
||||||
Fill,
|
|
||||||
Option<Color>,
|
|
||||||
Color,
|
|
||||||
Gradient,
|
|
||||||
)]
|
|
||||||
#[default(Color::BLACK)]
|
|
||||||
/// The fill to paint the path with.
|
|
||||||
fill: F,
|
|
||||||
_backup_color: Option<Color>,
|
|
||||||
_backup_gradient: Gradient,
|
|
||||||
) -> V
|
|
||||||
where
|
|
||||||
V: VectorDataTableIterMut + 'n + Send,
|
|
||||||
{
|
|
||||||
let fill: Fill = fill.into();
|
|
||||||
for vector in vector_data.vector_iter_mut() {
|
|
||||||
let mut fill = fill.clone();
|
|
||||||
if let Fill::Gradient(gradient) = &mut fill {
|
|
||||||
gradient.transform *= *vector.transform;
|
|
||||||
}
|
|
||||||
vector.instance.style.set_fill(fill);
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Applies a stroke style to the vector data contained in the input.
|
|
||||||
#[node_macro::node(category("Vector: Style"), path(graphene_core::vector), properties("stroke_properties"))]
|
|
||||||
async fn stroke<C: Into<Option<Color>> + 'n + Send, V>(
|
|
||||||
_: impl Ctx,
|
|
||||||
#[implementations(VectorDataTable, VectorDataTable, GraphicGroupTable, GraphicGroupTable)]
|
|
||||||
/// The vector elements, or group of vector elements, to apply the stroke to.
|
|
||||||
mut vector_data: Instances<V>,
|
|
||||||
#[implementations(
|
|
||||||
Option<Color>,
|
|
||||||
Color,
|
|
||||||
Option<Color>,
|
|
||||||
Color,
|
|
||||||
)]
|
|
||||||
#[default(Color::BLACK)]
|
|
||||||
/// The stroke color.
|
|
||||||
color: C,
|
|
||||||
#[default(2.)]
|
|
||||||
/// The stroke weight.
|
|
||||||
weight: f64,
|
|
||||||
/// The alignment of stroke to the path's centerline or (for closed shapes) the inside or outside of the shape.
|
|
||||||
align: StrokeAlign,
|
|
||||||
/// The shape of the stroke at open endpoints.
|
|
||||||
cap: StrokeCap,
|
|
||||||
/// The curvature of the bent stroke at sharp corners.
|
|
||||||
join: StrokeJoin,
|
|
||||||
#[default(4.)]
|
|
||||||
/// The threshold for when a miter-joined stroke is converted to a bevel-joined stroke when a sharp angle becomes pointier than this ratio.
|
|
||||||
miter_limit: f64,
|
|
||||||
/// The order to paint the stroke on top of the fill, or the fill on top of the stroke.
|
|
||||||
/// <https://svgwg.org/svg2-draft/painting.html#PaintOrderProperty>
|
|
||||||
paint_order: PaintOrder,
|
|
||||||
/// The stroke dash lengths. Each length forms a distance in a pattern where the first length is a dash, the second is a gap, and so on. If the list is an odd length, the pattern repeats with solid-gap roles reversed.
|
|
||||||
dash_lengths: Vec<f64>,
|
|
||||||
/// The phase offset distance from the starting point of the dash pattern.
|
|
||||||
dash_offset: f64,
|
|
||||||
) -> Instances<V>
|
|
||||||
where
|
|
||||||
Instances<V>: VectorDataTableIterMut + 'n + Send,
|
|
||||||
{
|
|
||||||
let stroke = Stroke {
|
|
||||||
color: color.into(),
|
|
||||||
weight,
|
|
||||||
dash_lengths,
|
|
||||||
dash_offset,
|
|
||||||
cap,
|
|
||||||
join,
|
|
||||||
join_miter_limit: miter_limit,
|
|
||||||
align,
|
|
||||||
transform: DAffine2::IDENTITY,
|
|
||||||
non_scaling: false,
|
|
||||||
paint_order,
|
|
||||||
};
|
|
||||||
|
|
||||||
for vector in vector_data.vector_iter_mut() {
|
|
||||||
let mut stroke = stroke.clone();
|
|
||||||
stroke.transform *= *vector.transform;
|
|
||||||
vector.instance.style.set_stroke(stroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_data
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
|
||||||
async fn repeat<I: 'n + Send + Clone>(
|
|
||||||
_: impl Ctx,
|
|
||||||
// TODO: Implement other GraphicElementRendered types.
|
|
||||||
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)] instance: Instances<I>,
|
|
||||||
#[default(100., 100.)]
|
|
||||||
// TODO: When using a custom Properties panel layout in document_node_definitions.rs and this default is set, the widget weirdly doesn't show up in the Properties panel. Investigation is needed.
|
|
||||||
direction: PixelSize,
|
|
||||||
angle: Angle,
|
|
||||||
#[default(4)] instances: IntegerCount,
|
|
||||||
) -> Instances<I> {
|
|
||||||
let angle = angle.to_radians();
|
|
||||||
let count = instances.max(1);
|
|
||||||
let total = (count - 1) as f64;
|
|
||||||
|
|
||||||
let mut result_table = Instances::<I>::default();
|
|
||||||
|
|
||||||
for index in 0..count {
|
|
||||||
let angle = index as f64 * angle / total;
|
|
||||||
let translation = index as f64 * direction / total;
|
|
||||||
let transform = DAffine2::from_angle(angle) * DAffine2::from_translation(translation);
|
|
||||||
|
|
||||||
for instance in instance.instance_ref_iter() {
|
|
||||||
let mut instance = instance.to_instance_cloned();
|
|
||||||
|
|
||||||
let local_translation = DAffine2::from_translation(instance.transform.translation);
|
|
||||||
let local_matrix = DAffine2::from_mat2(instance.transform.matrix2);
|
|
||||||
instance.transform = local_translation * transform * local_matrix;
|
|
||||||
|
|
||||||
result_table.push(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result_table
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
|
||||||
async fn circular_repeat<I: 'n + Send + Clone>(
|
|
||||||
_: impl Ctx,
|
|
||||||
// TODO: Implement other GraphicElementRendered types.
|
|
||||||
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)] instance: Instances<I>,
|
|
||||||
angle_offset: Angle,
|
|
||||||
#[default(5)] radius: f64,
|
|
||||||
#[default(5)] instances: IntegerCount,
|
|
||||||
) -> Instances<I> {
|
|
||||||
let count = instances.max(1);
|
|
||||||
|
|
||||||
let mut result_table = Instances::<I>::default();
|
|
||||||
|
|
||||||
for index in 0..count {
|
|
||||||
let angle = DAffine2::from_angle((TAU / count as f64) * index as f64 + angle_offset.to_radians());
|
|
||||||
let translation = DAffine2::from_translation(radius * DVec2::Y);
|
|
||||||
let transform = angle * translation;
|
|
||||||
|
|
||||||
for instance in instance.instance_ref_iter() {
|
|
||||||
let mut instance = instance.to_instance_cloned();
|
|
||||||
|
|
||||||
let local_translation = DAffine2::from_translation(instance.transform.translation);
|
|
||||||
let local_matrix = DAffine2::from_mat2(instance.transform.matrix2);
|
|
||||||
instance.transform = local_translation * transform * local_matrix;
|
|
||||||
|
|
||||||
result_table.push(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result_table
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node(name("Copy to Points"), category("Instancing"), path(graphene_core::vector))]
|
|
||||||
async fn copy_to_points<I: 'n + Send + Clone>(
|
|
||||||
_: impl Ctx,
|
|
||||||
points: VectorDataTable,
|
|
||||||
#[expose]
|
|
||||||
/// Artwork to be copied and placed at each point.
|
|
||||||
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)]
|
|
||||||
instance: Instances<I>,
|
|
||||||
/// Minimum range of randomized sizes given to each instance.
|
|
||||||
#[default(1)]
|
|
||||||
#[range((0., 2.))]
|
|
||||||
#[unit("x")]
|
|
||||||
random_scale_min: Multiplier,
|
|
||||||
/// Maximum range of randomized sizes given to each instance.
|
|
||||||
#[default(1)]
|
|
||||||
#[range((0., 2.))]
|
|
||||||
#[unit("x")]
|
|
||||||
random_scale_max: Multiplier,
|
|
||||||
/// Bias for the probability distribution of randomized sizes (0 is uniform, negatives favor more of small sizes, positives favor more of large sizes).
|
|
||||||
#[range((-50., 50.))]
|
|
||||||
random_scale_bias: f64,
|
|
||||||
/// Seed to determine unique variations on all the randomized instance sizes.
|
|
||||||
random_scale_seed: SeedValue,
|
|
||||||
/// Range of randomized angles given to each instance, in degrees ranging from furthest clockwise to counterclockwise.
|
|
||||||
#[range((0., 360.))]
|
|
||||||
random_rotation: Angle,
|
|
||||||
/// Seed to determine unique variations on all the randomized instance angles.
|
|
||||||
random_rotation_seed: SeedValue,
|
|
||||||
) -> Instances<I> {
|
|
||||||
let mut result_table = Instances::<I>::default();
|
|
||||||
|
|
||||||
let random_scale_difference = random_scale_max - random_scale_min;
|
|
||||||
|
|
||||||
for point_instance in points.instance_iter() {
|
|
||||||
let mut scale_rng = rand::rngs::StdRng::seed_from_u64(random_scale_seed.into());
|
|
||||||
let mut rotation_rng = rand::rngs::StdRng::seed_from_u64(random_rotation_seed.into());
|
|
||||||
|
|
||||||
let do_scale = random_scale_difference.abs() > 1e-6;
|
|
||||||
let do_rotation = random_rotation.abs() > 1e-6;
|
|
||||||
|
|
||||||
let points_transform = point_instance.transform;
|
|
||||||
for &point in point_instance.instance.point_domain.positions() {
|
|
||||||
let translation = points_transform.transform_point2(point);
|
|
||||||
|
|
||||||
let rotation = if do_rotation {
|
|
||||||
let degrees = (rotation_rng.random::<f64>() - 0.5) * random_rotation;
|
|
||||||
degrees / 360. * TAU
|
|
||||||
} else {
|
|
||||||
0.
|
|
||||||
};
|
|
||||||
|
|
||||||
let scale = if do_scale {
|
|
||||||
if random_scale_bias.abs() < 1e-6 {
|
|
||||||
// Linear
|
|
||||||
random_scale_min + scale_rng.random::<f64>() * random_scale_difference
|
|
||||||
} else {
|
|
||||||
// Weighted (see <https://www.desmos.com/calculator/gmavd3m9bd>)
|
|
||||||
let horizontal_scale_factor = 1. - 2_f64.powf(random_scale_bias);
|
|
||||||
let scale_factor = (1. - scale_rng.random::<f64>() * horizontal_scale_factor).log2() / random_scale_bias;
|
|
||||||
random_scale_min + scale_factor * random_scale_difference
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
random_scale_min
|
|
||||||
};
|
|
||||||
|
|
||||||
let transform = DAffine2::from_scale_angle_translation(DVec2::splat(scale), rotation, translation);
|
|
||||||
|
|
||||||
for mut instance in instance.instance_ref_iter().map(|instance| instance.to_instance_cloned()) {
|
|
||||||
let local_matrix = DAffine2::from_mat2(instance.transform.matrix2);
|
|
||||||
instance.transform = transform * local_matrix;
|
|
||||||
|
|
||||||
result_table.push(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result_table
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node(category("Instancing"), path(graphene_core::vector))]
|
|
||||||
async fn mirror<I: 'n + Send + Clone>(
|
|
||||||
_: impl Ctx,
|
|
||||||
#[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>)] instance: Instances<I>,
|
|
||||||
#[default(ReferencePoint::Center)] relative_to_bounds: ReferencePoint,
|
|
||||||
offset: f64,
|
|
||||||
#[range((-90., 90.))] angle: Angle,
|
|
||||||
#[default(true)] keep_original: bool,
|
|
||||||
) -> Instances<I>
|
|
||||||
where
|
|
||||||
Instances<I>: BoundingBox,
|
|
||||||
{
|
|
||||||
let mut result_table = Instances::default();
|
|
||||||
|
|
||||||
// Normalize the direction vector
|
|
||||||
let normal = DVec2::from_angle(angle.to_radians());
|
|
||||||
|
|
||||||
// The mirror reference is based on the bounding box (at least for now, until we have proper local layer origins)
|
|
||||||
let Some(bounding_box) = instance.bounding_box(DAffine2::IDENTITY, false) else {
|
|
||||||
return result_table;
|
|
||||||
};
|
|
||||||
|
|
||||||
let reference_point_location = relative_to_bounds.point_in_bounding_box((bounding_box[0], bounding_box[1]).into());
|
|
||||||
let mirror_reference_point = reference_point_location.map(|point| point + normal * offset);
|
|
||||||
|
|
||||||
// Create the reflection matrix
|
|
||||||
let reflection = DAffine2::from_mat2_translation(
|
|
||||||
glam::DMat2::from_cols(
|
|
||||||
DVec2::new(1. - 2. * normal.x * normal.x, -2. * normal.y * normal.x),
|
|
||||||
DVec2::new(-2. * normal.x * normal.y, 1. - 2. * normal.y * normal.y),
|
|
||||||
),
|
|
||||||
DVec2::ZERO,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Apply reflection around the reference point
|
|
||||||
let reflected_transform = if let Some(mirror_reference_point) = mirror_reference_point {
|
|
||||||
DAffine2::from_translation(mirror_reference_point) * reflection * DAffine2::from_translation(-mirror_reference_point)
|
|
||||||
} else {
|
|
||||||
reflection * DAffine2::from_translation(DVec2::from_angle(angle.to_radians()) * DVec2::splat(-offset))
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add original instance depending on the keep_original flag
|
|
||||||
if keep_original {
|
|
||||||
for instance in instance.clone().instance_iter() {
|
|
||||||
result_table.push(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and add mirrored instance
|
|
||||||
for mut instance in instance.instance_iter() {
|
|
||||||
instance.transform = reflected_transform * instance.transform;
|
|
||||||
instance.source_node_id = None;
|
|
||||||
result_table.push(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
result_table
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Modifier"), path(graphene_core::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(graphene_core::vector))]
|
||||||
async fn round_corners(
|
async fn round_corners(
|
||||||
_: impl Ctx,
|
_: impl Ctx,
|
||||||
|
@ -1078,61 +683,6 @@ async fn solidify_stroke(_: impl Ctx, vector_data: VectorDataTable) -> VectorDat
|
||||||
result_table
|
result_table
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
|
||||||
async fn flatten_path<I: 'n + Send>(_: impl Ctx, #[implementations(GraphicGroupTable, VectorDataTable)] graphic_group_input: Instances<I>) -> VectorDataTable
|
|
||||||
where
|
|
||||||
GraphicElement: From<Instances<I>>,
|
|
||||||
{
|
|
||||||
// A node based solution to support passing through vector data could be a network node with a cache node connected to
|
|
||||||
// a Flatten Path connected to an if else node, another connection from the cache directly
|
|
||||||
// To the if else node, and another connection from the cache to a matches type node connected to the if else node.
|
|
||||||
fn flatten_group(graphic_group_table: &GraphicGroupTable, output: &mut InstanceMut<VectorData>) {
|
|
||||||
for (group_index, current_element) in graphic_group_table.instance_ref_iter().enumerate() {
|
|
||||||
match current_element.instance {
|
|
||||||
GraphicElement::VectorData(vector_data_table) => {
|
|
||||||
// Loop through every row of the VectorDataTable and concatenate each instance's subpath into the output VectorData instance.
|
|
||||||
for (vector_index, vector_data_instance) in vector_data_table.instance_ref_iter().enumerate() {
|
|
||||||
let other = vector_data_instance.instance;
|
|
||||||
let transform = *current_element.transform * *vector_data_instance.transform;
|
|
||||||
let node_id = current_element.source_node_id.map(|node_id| node_id.0).unwrap_or_default();
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
|
||||||
(group_index, vector_index, node_id).hash(&mut hasher);
|
|
||||||
let collision_hash_seed = hasher.finish();
|
|
||||||
|
|
||||||
output.instance.concat(other, transform, collision_hash_seed);
|
|
||||||
|
|
||||||
// Use the last encountered style as the output style
|
|
||||||
output.instance.style = vector_data_instance.instance.style.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GraphicElement::GraphicGroup(graphic_group) => {
|
|
||||||
let mut graphic_group = graphic_group.clone();
|
|
||||||
for instance in graphic_group.instance_mut_iter() {
|
|
||||||
*instance.transform = *current_element.transform * *instance.transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
flatten_group(&graphic_group, output);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a table with one instance of an empty VectorData, then get a mutable reference to it which we append flattened subpaths to
|
|
||||||
let mut output_table = VectorDataTable::new(VectorData::default());
|
|
||||||
let Some(mut output) = output_table.instance_mut_iter().next() else {
|
|
||||||
return output_table;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Flatten the graphic group input into the output VectorData instance
|
|
||||||
let base_graphic_group = GraphicGroupTable::new(GraphicElement::from(graphic_group_input));
|
|
||||||
flatten_group(&base_graphic_group, &mut output);
|
|
||||||
|
|
||||||
// Return the single-row VectorDataTable containing the flattened VectorData subpaths
|
|
||||||
output_table
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert vector geometry into a polyline composed of evenly spaced points.
|
/// Convert vector geometry into a polyline composed of evenly spaced points.
|
||||||
#[node_macro::node(category(""), path(graphene_core::vector))]
|
#[node_macro::node(category(""), path(graphene_core::vector))]
|
||||||
async fn sample_polyline(
|
async fn sample_polyline(
|
||||||
|
@ -1860,11 +1410,6 @@ fn point_inside(_: impl Ctx, source: VectorDataTable, point: DVec2) -> bool {
|
||||||
source.instance_iter().any(|instance| instance.instance.check_point_inside_shape(instance.transform, point))
|
source.instance_iter().any(|instance| instance.instance.check_point_inside_shape(instance.transform, point))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[node_macro::node(category("General"), path(graphene_core::vector))]
|
|
||||||
async fn count_elements<I>(_: impl Ctx, #[implementations(GraphicGroupTable, VectorDataTable, RasterDataTable<CPU>, RasterDataTable<GPU>)] source: Instances<I>) -> u64 {
|
|
||||||
source.instance_iter().count() as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
#[node_macro::node(category("Vector: Measure"), path(graphene_core::vector))]
|
||||||
async fn path_length(_: impl Ctx, source: VectorDataTable) -> f64 {
|
async fn path_length(_: impl Ctx, source: VectorDataTable) -> f64 {
|
||||||
source
|
source
|
||||||
|
@ -1953,8 +1498,9 @@ async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl N
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Node;
|
|
||||||
use bezier_rs::Bezier;
|
use bezier_rs::Bezier;
|
||||||
|
use graphene_core::Node;
|
||||||
|
use graphene_core::transform::Footprint;
|
||||||
use kurbo::Rect;
|
use kurbo::Rect;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
@ -1990,47 +1536,6 @@ mod test {
|
||||||
}
|
}
|
||||||
vector_data_table
|
vector_data_table
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn repeat() {
|
|
||||||
let direction = DVec2::X * 1.5;
|
|
||||||
let instances = 3;
|
|
||||||
let repeated = super::repeat(Footprint::default(), vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE)), direction, 0., instances).await;
|
|
||||||
let vector_data = super::flatten_path(Footprint::default(), repeated).await;
|
|
||||||
let vector_data = vector_data.instance_ref_iter().next().unwrap().instance;
|
|
||||||
assert_eq!(vector_data.region_bezier_paths().count(), 3);
|
|
||||||
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
|
||||||
assert!((subpath.manipulator_groups()[0].anchor - direction * index as f64 / (instances - 1) as f64).length() < 1e-5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[tokio::test]
|
|
||||||
async fn repeat_transform_position() {
|
|
||||||
let direction = DVec2::new(12., 10.);
|
|
||||||
let instances = 8;
|
|
||||||
let repeated = super::repeat(Footprint::default(), vector_node(Subpath::new_rect(DVec2::ZERO, DVec2::ONE)), direction, 0., instances).await;
|
|
||||||
let vector_data = super::flatten_path(Footprint::default(), repeated).await;
|
|
||||||
let vector_data = vector_data.instance_ref_iter().next().unwrap().instance;
|
|
||||||
assert_eq!(vector_data.region_bezier_paths().count(), 8);
|
|
||||||
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
|
||||||
assert!((subpath.manipulator_groups()[0].anchor - direction * index as f64 / (instances - 1) as f64).length() < 1e-5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[tokio::test]
|
|
||||||
async fn circular_repeat() {
|
|
||||||
let repeated = super::circular_repeat(Footprint::default(), vector_node(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE)), 45., 4., 8).await;
|
|
||||||
let vector_data = super::flatten_path(Footprint::default(), repeated).await;
|
|
||||||
let vector_data = vector_data.instance_ref_iter().next().unwrap().instance;
|
|
||||||
assert_eq!(vector_data.region_bezier_paths().count(), 8);
|
|
||||||
|
|
||||||
for (index, (_, subpath)) in vector_data.region_bezier_paths().enumerate() {
|
|
||||||
let expected_angle = (index as f64 + 1.) * 45.;
|
|
||||||
|
|
||||||
let center = (subpath.manipulator_groups()[0].anchor + subpath.manipulator_groups()[2].anchor) / 2.;
|
|
||||||
let actual_angle = DVec2::Y.angle_to(center).to_degrees();
|
|
||||||
|
|
||||||
assert!((actual_angle - expected_angle).abs() % 360. < 1e-5, "Expected {expected_angle} found {actual_angle}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn bounding_box() {
|
async fn bounding_box() {
|
||||||
let bounding_box = super::bounding_box((), vector_node(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE))).await;
|
let bounding_box = super::bounding_box((), vector_node(Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE))).await;
|
||||||
|
@ -2057,27 +1562,6 @@ mod test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn copy_to_points() {
|
|
||||||
let points = Subpath::new_rect(DVec2::NEG_ONE * 10., DVec2::ONE * 10.);
|
|
||||||
let instance = Subpath::new_rect(DVec2::NEG_ONE, DVec2::ONE);
|
|
||||||
|
|
||||||
let expected_points = VectorData::from_subpath(points.clone()).point_domain.positions().to_vec();
|
|
||||||
|
|
||||||
let copy_to_points = super::copy_to_points(Footprint::default(), vector_node(points), vector_node(instance), 1., 1., 0., 0, 0., 0).await;
|
|
||||||
let flatten_path = super::flatten_path(Footprint::default(), copy_to_points).await;
|
|
||||||
let flattened_copy_to_points = flatten_path.instance_ref_iter().next().unwrap().instance;
|
|
||||||
|
|
||||||
assert_eq!(flattened_copy_to_points.region_bezier_paths().count(), expected_points.len());
|
|
||||||
|
|
||||||
for (index, (_, subpath)) in flattened_copy_to_points.region_bezier_paths().enumerate() {
|
|
||||||
let offset = expected_points[index];
|
|
||||||
assert_eq!(
|
|
||||||
&subpath.anchors(),
|
|
||||||
&[offset + DVec2::NEG_ONE, offset + DVec2::new(1., -1.), offset + DVec2::ONE, offset + DVec2::new(-1., 1.),]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[tokio::test]
|
|
||||||
async fn sample_polyline() {
|
async fn sample_polyline() {
|
||||||
let path = Subpath::from_bezier(&Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::X * 100., DVec2::X * 100.));
|
let path = Subpath::from_bezier(&Bezier::from_cubic_dvec2(DVec2::ZERO, DVec2::ZERO, DVec2::X * 100., DVec2::X * 100.));
|
||||||
let sample_polyline = super::sample_polyline(Footprint::default(), vector_node(path), PointSpacingType::Separation, 30., 0., 0., 0., false, vec![100.]).await;
|
let sample_polyline = super::sample_polyline(Footprint::default(), vector_node(path), PointSpacingType::Separation, 30., 0., 0., 0., false, vec![100.]).await;
|
33
node-graph/gvector/Cargo.toml
Normal file
33
node-graph/gvector/Cargo.toml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
[package]
|
||||||
|
name = "graphene-vector"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
description = "graphene vector data format"
|
||||||
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["serde"]
|
||||||
|
serde = [
|
||||||
|
"dep:serde",
|
||||||
|
"bezier-rs/serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Local dependencies
|
||||||
|
dyn-any = { workspace = true }
|
||||||
|
bezier-rs = { workspace = true }
|
||||||
|
graphene-core = { workspace = true }
|
||||||
|
node-macro = { workspace = true }
|
||||||
|
|
||||||
|
# Workspace dependencies
|
||||||
|
kurbo = { workspace = true }
|
||||||
|
glam = { workspace = true }
|
||||||
|
specta = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
|
tinyvec = { workspace = true }
|
||||||
|
rustc-hash = { workspace = true }
|
||||||
|
petgraph = { workspace = true }
|
||||||
|
|
||||||
|
# Optional workspace dependencies
|
||||||
|
serde = { workspace = true, optional = true, features = ["derive"] }
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::math::math_ext::QuadExt;
|
use crate::math_ext::QuadExt;
|
||||||
use crate::math::quad::Quad;
|
use crate::vector_data::PointId;
|
||||||
use crate::vector::PointId;
|
|
||||||
use bezier_rs::Subpath;
|
use bezier_rs::Subpath;
|
||||||
use glam::{DAffine2, DMat2, DVec2};
|
use glam::{DAffine2, DMat2, DVec2};
|
||||||
|
use graphene_core::math::quad::Quad;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct FreePoint {
|
pub struct FreePoint {
|
17
node-graph/gvector/src/lib.rs
Normal file
17
node-graph/gvector/src/lib.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
pub mod click_target;
|
||||||
|
pub mod math_ext;
|
||||||
|
pub mod reference_point;
|
||||||
|
pub mod style;
|
||||||
|
mod vector_data;
|
||||||
|
|
||||||
|
pub use bezier_rs;
|
||||||
|
|
||||||
|
pub use vector_data::*;
|
||||||
|
|
||||||
|
pub fn point_to_dvec2(point: kurbo::Point) -> glam::DVec2 {
|
||||||
|
glam::DVec2 { x: point.x, y: point.y }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dvec2_to_point(value: glam::DVec2) -> kurbo::Point {
|
||||||
|
kurbo::Point { x: value.x, y: value.y }
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::math::quad::Quad;
|
|
||||||
use crate::math::rect::Rect;
|
|
||||||
use bezier_rs::Bezier;
|
use bezier_rs::Bezier;
|
||||||
|
use graphene_core::math::quad::Quad;
|
||||||
|
use graphene_core::math::rect::Rect;
|
||||||
|
|
||||||
pub trait QuadExt {
|
pub trait QuadExt {
|
||||||
/// Get all the edges in the rect as linear bezier curves
|
/// Get all the edges in the rect as linear bezier curves
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::math::bbox::AxisAlignedBbox;
|
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
|
use graphene_core::math::bbox::AxisAlignedBbox;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub enum ReferencePoint {
|
pub enum ReferencePoint {
|
|
@ -1,9 +1,9 @@
|
||||||
//! Contains stylistic options for SVG elements.
|
//! Contains stylistic options for SVG elements.
|
||||||
|
|
||||||
use crate::Color;
|
|
||||||
pub use crate::gradient::*;
|
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
|
use graphene_core::color::Color;
|
||||||
|
use graphene_core::gradient::{Gradient, GradientStops};
|
||||||
|
|
||||||
/// Describes the fill of a layer.
|
/// Describes the fill of a layer.
|
||||||
///
|
///
|
||||||
|
@ -528,8 +528,8 @@ impl PathStyle {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # use graphene_core::vector::style::{Fill, PathStyle};
|
/// # use graphene_vector::style::{Fill, PathStyle};
|
||||||
/// # use graphene_core::raster::color::Color;
|
/// # use graphene_core::color::Color;
|
||||||
/// let fill = Fill::solid(Color::RED);
|
/// let fill = Fill::solid(Color::RED);
|
||||||
/// let style = PathStyle::new(None, fill.clone());
|
/// let style = PathStyle::new(None, fill.clone());
|
||||||
///
|
///
|
||||||
|
@ -543,8 +543,8 @@ impl PathStyle {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # use graphene_core::vector::style::{Fill, Stroke, PathStyle};
|
/// # use graphene_vector::style::{Fill, Stroke, PathStyle};
|
||||||
/// # use graphene_core::raster::color::Color;
|
/// # use graphene_core::color::Color;
|
||||||
/// let stroke = Stroke::new(Some(Color::GREEN), 42.);
|
/// let stroke = Stroke::new(Some(Color::GREEN), 42.);
|
||||||
/// let style = PathStyle::new(Some(stroke.clone()), Fill::None);
|
/// let style = PathStyle::new(Some(stroke.clone()), Fill::None);
|
||||||
///
|
///
|
||||||
|
@ -558,8 +558,8 @@ impl PathStyle {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # use graphene_core::vector::style::{Fill, PathStyle};
|
/// # use graphene_vector::style::{Fill, PathStyle};
|
||||||
/// # use graphene_core::raster::color::Color;
|
/// # use graphene_core::color::Color;
|
||||||
/// let mut style = PathStyle::default();
|
/// let mut style = PathStyle::default();
|
||||||
///
|
///
|
||||||
/// assert_eq!(*style.fill(), Fill::None);
|
/// assert_eq!(*style.fill(), Fill::None);
|
||||||
|
@ -583,8 +583,8 @@ impl PathStyle {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # use graphene_core::vector::style::{Stroke, PathStyle};
|
/// # use graphene_vector::style::{Stroke, PathStyle};
|
||||||
/// # use graphene_core::raster::color::Color;
|
/// # use graphene_core::color::Color;
|
||||||
/// let mut style = PathStyle::default();
|
/// let mut style = PathStyle::default();
|
||||||
///
|
///
|
||||||
/// assert_eq!(style.stroke(), None);
|
/// assert_eq!(style.stroke(), None);
|
||||||
|
@ -602,8 +602,8 @@ impl PathStyle {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # use graphene_core::vector::style::{Fill, PathStyle};
|
/// # use graphene_vector::style::{Fill, PathStyle};
|
||||||
/// # use graphene_core::raster::color::Color;
|
/// # use graphene_core::color::Color;
|
||||||
/// let mut style = PathStyle::new(None, Fill::Solid(Color::RED));
|
/// let mut style = PathStyle::new(None, Fill::Solid(Color::RED));
|
||||||
///
|
///
|
||||||
/// assert_ne!(*style.fill(), Fill::None);
|
/// assert_ne!(*style.fill(), Fill::None);
|
||||||
|
@ -620,8 +620,8 @@ impl PathStyle {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # use graphene_core::vector::style::{Fill, Stroke, PathStyle};
|
/// # use graphene_vector::style::{Fill, Stroke, PathStyle};
|
||||||
/// # use graphene_core::raster::color::Color;
|
/// # use graphene_core::color::Color;
|
||||||
/// let mut style = PathStyle::new(Some(Stroke::new(Some(Color::GREEN), 42.)), Fill::None);
|
/// let mut style = PathStyle::new(Some(Stroke::new(Some(Color::GREEN), 42.)), Fill::None);
|
||||||
///
|
///
|
||||||
/// assert!(style.stroke().is_some());
|
/// assert!(style.stroke().is_some());
|
||||||
|
@ -634,15 +634,3 @@ impl PathStyle {
|
||||||
self.stroke = None;
|
self.stroke = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents different ways of rendering an object
|
|
||||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, DynAny, specta::Type)]
|
|
||||||
pub enum ViewMode {
|
|
||||||
/// Render with normal coloration at the current viewport resolution
|
|
||||||
#[default]
|
|
||||||
Normal,
|
|
||||||
/// Render only the outlines of shapes at the current viewport resolution
|
|
||||||
Outline,
|
|
||||||
/// Render with normal coloration at the document resolution, showing the pixels when the current viewport resolution is higher
|
|
||||||
Pixels,
|
|
||||||
}
|
|
|
@ -1,25 +1,31 @@
|
||||||
mod attributes;
|
mod attributes;
|
||||||
mod indexed;
|
mod indexed;
|
||||||
mod modification;
|
|
||||||
|
|
||||||
use super::misc::{dvec2_to_point, point_to_dvec2};
|
use crate::click_target::{ClickTargetType, FreePoint};
|
||||||
use super::style::{PathStyle, Stroke};
|
use crate::dvec2_to_point;
|
||||||
use crate::bounds::BoundingBox;
|
use crate::style::{PathStyle, Stroke};
|
||||||
use crate::instances::Instances;
|
|
||||||
use crate::math::quad::Quad;
|
|
||||||
use crate::transform::Transform;
|
|
||||||
use crate::vector::click_target::{ClickTargetType, FreePoint};
|
|
||||||
use crate::{AlphaBlending, Color, GraphicGroupTable};
|
|
||||||
pub use attributes::*;
|
pub use attributes::*;
|
||||||
use bezier_rs::{BezierHandles, ManipulatorGroup};
|
use bezier_rs::{BezierHandles, ManipulatorGroup};
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphene_core::blending::AlphaBlending;
|
||||||
|
use graphene_core::bounds::BoundingBox;
|
||||||
|
use graphene_core::color::Color;
|
||||||
|
use graphene_core::instances::Instances;
|
||||||
|
use graphene_core::math::quad::Quad;
|
||||||
|
use graphene_core::transform::Transform;
|
||||||
pub use indexed::VectorDataIndex;
|
pub use indexed::VectorDataIndex;
|
||||||
use kurbo::{Affine, Rect, Shape};
|
use kurbo::{Affine, Rect, Shape};
|
||||||
pub use modification::*;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub trait AnyUpstreamGraphicGroup: Any + serde::Serialize + for<'a> serde::Deserialize<'a> {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, DynAny, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct UpstreamGraphicGroup(Arc<dyn AnyUpstreamGraphicGroup>);
|
||||||
|
|
||||||
// TODO: Eventually remove this migration document upgrade code
|
// TODO: Eventually remove this migration document upgrade code
|
||||||
pub fn migrate_vector_data<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<VectorDataTable, D::Error> {
|
pub fn migrate_vector_data<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<VectorDataTable, D::Error> {
|
||||||
|
@ -41,7 +47,7 @@ pub fn migrate_vector_data<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
||||||
pub region_domain: RegionDomain,
|
pub region_domain: RegionDomain,
|
||||||
|
|
||||||
// Used to store the upstream graphic group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
// Used to store the upstream graphic group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
||||||
pub upstream_graphic_group: Option<GraphicGroupTable>,
|
pub upstream_graphic_group: Option<UpstreamGraphicGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
@ -91,7 +97,7 @@ pub struct VectorData {
|
||||||
pub region_domain: RegionDomain,
|
pub region_domain: RegionDomain,
|
||||||
|
|
||||||
// Used to store the upstream graphic group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
// Used to store the upstream graphic group during destructive Boolean Operations (and other nodes with a similar effect) so that click targets can be preserved.
|
||||||
pub upstream_graphic_group: Option<GraphicGroupTable>,
|
pub upstream_graphic_group: Option<UpstreamGraphicGroup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for VectorData {
|
impl Default for VectorData {
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::vector::misc::dvec2_to_point;
|
use crate::dvec2_to_point;
|
||||||
use crate::vector::vector_data::{HandleId, VectorData};
|
use crate::vector_data::{HandleId, VectorData};
|
||||||
use bezier_rs::{BezierHandles, ManipulatorGroup};
|
use bezier_rs::{BezierHandles, ManipulatorGroup};
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
|
use graphene_core::uuid::generate_uuid;
|
||||||
|
use log::warn;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::iter::zip;
|
use std::iter::zip;
|
||||||
|
@ -21,7 +23,7 @@ macro_rules! create_ids {
|
||||||
|
|
||||||
/// Generate a new random id
|
/// Generate a new random id
|
||||||
pub fn generate() -> Self {
|
pub fn generate() -> Self {
|
||||||
Self(crate::uuid::generate_uuid())
|
Self(generate_uuid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_from_hash(self, node_id: u64) -> Self {
|
pub fn generate_from_hash(self, node_id: u64) -> Self {
|
||||||
|
@ -82,7 +84,7 @@ impl std::hash::BuildHasher for NoHashBuilder {
|
||||||
pub struct PointDomain {
|
pub struct PointDomain {
|
||||||
id: Vec<PointId>,
|
id: Vec<PointId>,
|
||||||
#[serde(alias = "positions")]
|
#[serde(alias = "positions")]
|
||||||
pub(crate) position: Vec<DVec2>,
|
pub position: Vec<DVec2>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for PointDomain {
|
impl Hash for PointDomain {
|
||||||
|
@ -166,7 +168,7 @@ impl PointDomain {
|
||||||
pos
|
pos
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_id(&self, id: PointId) -> Option<usize> {
|
pub fn resolve_id(&self, id: PointId) -> Option<usize> {
|
||||||
self.id.iter().position(|&check_id| check_id == id)
|
self.id.iter().position(|&check_id| check_id == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,11 +282,11 @@ impl SegmentDomain {
|
||||||
self.ids().iter().copied().max_by(|a, b| a.0.cmp(&b.0)).map(|mut id| id.next_id()).unwrap_or(SegmentId::ZERO)
|
self.ids().iter().copied().max_by(|a, b| a.0.cmp(&b.0)).map(|mut id| id.next_id()).unwrap_or(SegmentId::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn start_point(&self) -> &[usize] {
|
pub fn start_point(&self) -> &[usize] {
|
||||||
&self.start_point
|
&self.start_point
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn end_point(&self) -> &[usize] {
|
pub fn end_point(&self) -> &[usize] {
|
||||||
&self.end_point
|
&self.end_point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +306,7 @@ impl SegmentDomain {
|
||||||
&self.stroke
|
&self.stroke
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn push(&mut self, id: SegmentId, start: usize, end: usize, handles: BezierHandles, stroke: StrokeId) {
|
pub fn push(&mut self, id: SegmentId, start: usize, end: usize, handles: BezierHandles, stroke: StrokeId) {
|
||||||
debug_assert!(!self.id.contains(&id), "Tried to push an existing point to a point domain");
|
debug_assert!(!self.id.contains(&id), "Tried to push an existing point to a point domain");
|
||||||
|
|
||||||
self.id.push(id);
|
self.id.push(id);
|
||||||
|
@ -314,20 +316,20 @@ impl SegmentDomain {
|
||||||
self.stroke.push(stroke);
|
self.stroke.push(stroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn start_point_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut usize)> {
|
pub fn start_point_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut usize)> {
|
||||||
self.id.iter().copied().zip(self.start_point.iter_mut())
|
self.id.iter().copied().zip(self.start_point.iter_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn end_point_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut usize)> {
|
pub fn end_point_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut usize)> {
|
||||||
self.id.iter().copied().zip(self.end_point.iter_mut())
|
self.id.iter().copied().zip(self.end_point.iter_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handles_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut BezierHandles, usize, usize)> {
|
pub fn handles_mut(&mut self) -> impl Iterator<Item = (SegmentId, &mut BezierHandles, usize, usize)> {
|
||||||
let nested = self.id.iter().zip(&mut self.handles).zip(&self.start_point).zip(&self.end_point);
|
let nested = self.id.iter().zip(&mut self.handles).zip(&self.start_point).zip(&self.end_point);
|
||||||
nested.map(|(((&a, b), &c), &d)| (a, b, c, d))
|
nested.map(|(((&a, b), &c), &d)| (a, b, c, d))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handles_and_points_mut(&mut self) -> impl Iterator<Item = (&mut BezierHandles, &mut usize, &mut usize)> {
|
pub fn handles_and_points_mut(&mut self) -> impl Iterator<Item = (&mut BezierHandles, &mut usize, &mut usize)> {
|
||||||
let nested = self.handles.iter_mut().zip(&mut self.start_point).zip(&mut self.end_point);
|
let nested = self.handles.iter_mut().zip(&mut self.start_point).zip(&mut self.end_point);
|
||||||
nested.map(|((a, b), c)| (a, b, c))
|
nested.map(|((a, b), c)| (a, b, c))
|
||||||
}
|
}
|
||||||
|
@ -336,26 +338,26 @@ impl SegmentDomain {
|
||||||
self.id.iter().copied().zip(self.stroke.iter_mut())
|
self.id.iter().copied().zip(self.stroke.iter_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn segment_start_from_id(&self, segment: SegmentId) -> Option<usize> {
|
pub fn segment_start_from_id(&self, segment: SegmentId) -> Option<usize> {
|
||||||
self.id_to_index(segment).and_then(|index| self.start_point.get(index)).copied()
|
self.id_to_index(segment).and_then(|index| self.start_point.get(index)).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn segment_end_from_id(&self, segment: SegmentId) -> Option<usize> {
|
pub fn segment_end_from_id(&self, segment: SegmentId) -> Option<usize> {
|
||||||
self.id_to_index(segment).and_then(|index| self.end_point.get(index)).copied()
|
self.id_to_index(segment).and_then(|index| self.end_point.get(index)).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an array for the start and end points of a segment.
|
/// Returns an array for the start and end points of a segment.
|
||||||
pub(crate) fn points_from_id(&self, segment: SegmentId) -> Option<[usize; 2]> {
|
pub fn points_from_id(&self, segment: SegmentId) -> Option<[usize; 2]> {
|
||||||
self.segment_start_from_id(segment).and_then(|start| self.segment_end_from_id(segment).map(|end| [start, end]))
|
self.segment_start_from_id(segment).and_then(|start| self.segment_end_from_id(segment).map(|end| [start, end]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to find another point in the segment that is not the one passed in.
|
/// Attempts to find another point in the segment that is not the one passed in.
|
||||||
pub(crate) fn other_point(&self, segment: SegmentId, current: usize) -> Option<usize> {
|
pub fn other_point(&self, segment: SegmentId, current: usize) -> Option<usize> {
|
||||||
self.points_from_id(segment).and_then(|points| points.into_iter().find(|&point| point != current))
|
self.points_from_id(segment).and_then(|points| points.into_iter().find(|&point| point != current))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets all points connected to the current one but not including the current one.
|
/// Gets all points connected to the current one but not including the current one.
|
||||||
pub(crate) fn connected_points(&self, current: usize) -> impl Iterator<Item = usize> + '_ {
|
pub fn connected_points(&self, current: usize) -> impl Iterator<Item = usize> + '_ {
|
||||||
self.start_point.iter().zip(&self.end_point).filter_map(move |(&a, &b)| match (a == current, b == current) {
|
self.start_point.iter().zip(&self.end_point).filter_map(move |(&a, &b)| match (a == current, b == current) {
|
||||||
(true, false) => Some(b),
|
(true, false) => Some(b),
|
||||||
(false, true) => Some(a),
|
(false, true) => Some(a),
|
||||||
|
@ -400,22 +402,22 @@ impl SegmentDomain {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumerate all segments that start at the point.
|
/// Enumerate all segments that start at the point.
|
||||||
pub(crate) fn start_connected(&self, point: usize) -> impl Iterator<Item = SegmentId> + '_ {
|
pub fn start_connected(&self, point: usize) -> impl Iterator<Item = SegmentId> + '_ {
|
||||||
self.start_point.iter().zip(&self.id).filter(move |&(&found_point, _)| found_point == point).map(|(_, &seg)| seg)
|
self.start_point.iter().zip(&self.id).filter(move |&(&found_point, _)| found_point == point).map(|(_, &seg)| seg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumerate all segments that end at the point.
|
/// Enumerate all segments that end at the point.
|
||||||
pub(crate) fn end_connected(&self, point: usize) -> impl Iterator<Item = SegmentId> + '_ {
|
pub fn end_connected(&self, point: usize) -> impl Iterator<Item = SegmentId> + '_ {
|
||||||
self.end_point.iter().zip(&self.id).filter(move |&(&found_point, _)| found_point == point).map(|(_, &seg)| seg)
|
self.end_point.iter().zip(&self.id).filter(move |&(&found_point, _)| found_point == point).map(|(_, &seg)| seg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumerate all segments that start or end at a point, converting them to [`HandleId`s]. Note that the handles may not exist e.g. for a linear segment.
|
/// Enumerate all segments that start or end at a point, converting them to [`HandleId`s]. Note that the handles may not exist e.g. for a linear segment.
|
||||||
pub(crate) fn all_connected(&self, point: usize) -> impl Iterator<Item = HandleId> + '_ {
|
pub fn all_connected(&self, point: usize) -> impl Iterator<Item = HandleId> + '_ {
|
||||||
self.start_connected(point).map(HandleId::primary).chain(self.end_connected(point).map(HandleId::end))
|
self.start_connected(point).map(HandleId::primary).chain(self.end_connected(point).map(HandleId::end))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enumerate the number of segments connected to a point. If a segment starts and ends at a point then it is counted twice.
|
/// Enumerate the number of segments connected to a point. If a segment starts and ends at a point then it is counted twice.
|
||||||
pub(crate) fn connected_count(&self, point: usize) -> usize {
|
pub fn connected_count(&self, point: usize) -> usize {
|
||||||
self.all_connected(point).count()
|
self.all_connected(point).count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +435,7 @@ impl SegmentDomain {
|
||||||
/// Iterates over segments in the domain, mutably.
|
/// Iterates over segments in the domain, mutably.
|
||||||
///
|
///
|
||||||
/// Tuple is: (id, start point, end point, handles)
|
/// Tuple is: (id, start point, end point, handles)
|
||||||
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = (&mut SegmentId, &mut usize, &mut usize, &mut BezierHandles)> + '_ {
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&mut SegmentId, &mut usize, &mut usize, &mut BezierHandles)> + '_ {
|
||||||
let ids = self.id.iter_mut();
|
let ids = self.id.iter_mut();
|
||||||
let start_point = self.start_point.iter_mut();
|
let start_point = self.start_point.iter_mut();
|
||||||
let end_point = self.end_point.iter_mut();
|
let end_point = self.end_point.iter_mut();
|
|
@ -16,10 +16,10 @@ pub struct VectorDataIndex {
|
||||||
/// Points and segments form a graph. Store it here in a form amenable to graph algorithms.
|
/// Points and segments form a graph. Store it here in a form amenable to graph algorithms.
|
||||||
///
|
///
|
||||||
/// Currently, segment data is not stored as it is not used, but it could easily be added.
|
/// Currently, segment data is not stored as it is not used, but it could easily be added.
|
||||||
pub(crate) point_graph: UnGraph<Point, ()>,
|
pub point_graph: UnGraph<Point, ()>,
|
||||||
pub(crate) segment_to_edge: FxHashMap<SegmentId, EdgeIndex>,
|
pub segment_to_edge: FxHashMap<SegmentId, EdgeIndex>,
|
||||||
/// Get the offset from the point ID.
|
/// Get the offset from the point ID.
|
||||||
pub(crate) point_to_offset: FxHashMap<PointId, usize>,
|
pub point_to_offset: FxHashMap<PointId, usize>,
|
||||||
// TODO: faces
|
// TODO: faces
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ mod benchmark_util;
|
||||||
|
|
||||||
use benchmark_util::{bench_for_each_demo, setup_network};
|
use benchmark_util::{bench_for_each_demo, setup_network};
|
||||||
use criterion::{Criterion, criterion_group, criterion_main};
|
use criterion::{Criterion, criterion_group, criterion_main};
|
||||||
use graphene_std::Context;
|
use graphene_std::context::Context;
|
||||||
|
|
||||||
fn subsequent_evaluations(c: &mut Criterion) {
|
fn subsequent_evaluations(c: &mut Criterion) {
|
||||||
let mut group = c.benchmark_group("Subsequent Evaluations");
|
let mut group = c.benchmark_group("Subsequent Evaluations");
|
||||||
|
|
|
@ -2,7 +2,7 @@ mod benchmark_util;
|
||||||
|
|
||||||
use benchmark_util::{bench_for_each_demo, setup_network};
|
use benchmark_util::{bench_for_each_demo, setup_network};
|
||||||
use criterion::{Criterion, criterion_group, criterion_main};
|
use criterion::{Criterion, criterion_group, criterion_main};
|
||||||
use graphene_std::Context;
|
use graphene_std::context::Context;
|
||||||
|
|
||||||
fn run_once(c: &mut Criterion) {
|
fn run_once(c: &mut Criterion) {
|
||||||
let mut group = c.benchmark_group("Run Once");
|
let mut group = c.benchmark_group("Run Once");
|
||||||
|
|
|
@ -2,22 +2,21 @@ use dyn_any::StaticType;
|
||||||
use glam::{DVec2, IVec2, UVec2};
|
use glam::{DVec2, IVec2, UVec2};
|
||||||
use graph_craft::document::value::RenderOutput;
|
use graph_craft::document::value::RenderOutput;
|
||||||
use graph_craft::proto::{NodeConstructor, TypeErasedBox};
|
use graph_craft::proto::{NodeConstructor, TypeErasedBox};
|
||||||
use graphene_core::raster::color::Color;
|
|
||||||
use graphene_core::raster::*;
|
|
||||||
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
|
||||||
use graphene_core::vector::VectorDataTable;
|
|
||||||
use graphene_core::{Artboard, GraphicGroupTable, concrete, generic};
|
|
||||||
use graphene_core::{Cow, ProtoNodeIdentifier, Type};
|
|
||||||
use graphene_core::{NodeIO, NodeIOTypes};
|
|
||||||
use graphene_core::{fn_type_fut, future};
|
|
||||||
use graphene_std::Context;
|
|
||||||
use graphene_std::GraphicElement;
|
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use graphene_std::any::DowncastBothNode;
|
use graphene_std::any::DowncastBothNode;
|
||||||
use graphene_std::any::{ComposeTypeErased, DynAnyNode, IntoTypeErasedNode};
|
use graphene_std::any::{ComposeTypeErased, DynAnyNode, IntoTypeErasedNode};
|
||||||
use graphene_std::application_io::{ImageTexture, SurfaceFrame};
|
use graphene_std::application_io::{ImageTexture, SurfaceFrame};
|
||||||
|
use graphene_std::color::Color;
|
||||||
|
use graphene_std::context::Context;
|
||||||
|
use graphene_std::element::{Artboard, GraphicElement, GraphicGroupTable};
|
||||||
|
use graphene_std::raster::*;
|
||||||
|
use graphene_std::vector::VectorDataTable;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use graphene_std::wasm_application_io::{WasmEditorApi, WasmSurfaceHandle};
|
use graphene_std::wasm_application_io::{WasmEditorApi, WasmSurfaceHandle};
|
||||||
|
use graphene_std::{Cow, ProtoNodeIdentifier, Type};
|
||||||
|
use graphene_std::{NodeIO, NodeIOTypes};
|
||||||
|
use graphene_std::{concrete, generic};
|
||||||
|
use graphene_std::{fn_type_fut, future};
|
||||||
use node_registry_macros::{async_node, convert_node, into_node};
|
use node_registry_macros::{async_node, convert_node, into_node};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -73,7 +72,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||||
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 => Vec<graphene_core::uuid::NodeId>]),
|
||||||
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Color]),
|
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Color]),
|
||||||
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 => Box<graphene_core::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::std_nodes::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 => graphene_std::vector::misc::PointSpacingType]),
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Image<Color>]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => Image<Color>]),
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => VectorDataTable]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => VectorDataTable]),
|
||||||
|
|
|
@ -4,7 +4,7 @@ use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork};
|
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeInput, NodeNetwork};
|
||||||
use graph_craft::generic;
|
use graph_craft::generic;
|
||||||
use graph_craft::wasm_application_io::WasmEditorApi;
|
use graph_craft::wasm_application_io::WasmEditorApi;
|
||||||
use graphene_std::Context;
|
use graphene_std::context::Context;
|
||||||
use graphene_std::uuid::NodeId;
|
use graphene_std::uuid::NodeId;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
|
@ -517,7 +517,7 @@ fn generate_register_node_impl(parsed: &ParsedNodeFn, field_names: &[&Ident], st
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut constructors = Vec::new();
|
let mut constructors = Vec::new();
|
||||||
let unit = parse_quote!(gcore::Context);
|
let unit = parse_quote!(gcore::context::Context);
|
||||||
let parameter_types: Vec<_> = parsed
|
let parameter_types: Vec<_> = parsed
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -643,7 +643,7 @@ impl ParsedNodeFn {
|
||||||
}));
|
}));
|
||||||
self.input.ty = parse_quote!(#ident);
|
self.input.ty = parse_quote!(#ident);
|
||||||
if self.input.implementations.is_empty() {
|
if self.input.implementations.is_empty() {
|
||||||
self.input.implementations.push(parse_quote!(gcore::Context));
|
self.input.implementations.push(parse_quote!(gcore::context::Context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.input.pat_ident.ident == "_" {
|
if self.input.pat_ident.ident == "_" {
|
||||||
|
|
|
@ -5,7 +5,8 @@ pub use context::Context;
|
||||||
use dyn_any::StaticType;
|
use dyn_any::StaticType;
|
||||||
use glam::UVec2;
|
use glam::UVec2;
|
||||||
use graphene_application_io::{ApplicationIo, EditorApi, SurfaceHandle};
|
use graphene_application_io::{ApplicationIo, EditorApi, SurfaceHandle};
|
||||||
use graphene_core::{Color, Ctx};
|
use graphene_core::color::Color;
|
||||||
|
use graphene_core::context::Ctx;
|
||||||
pub use graphene_svg_renderer::RenderContext;
|
pub use graphene_svg_renderer::RenderContext;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vello::{AaConfig, AaSupport, RenderParams, Renderer, RendererOptions, Scene};
|
use vello::{AaConfig, AaSupport, RenderParams, Renderer, RendererOptions, Scene};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue