mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55: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-brush",
|
||||
"graphene-core",
|
||||
"graphene-element",
|
||||
"graphene-element-nodes",
|
||||
"graphene-math-nodes",
|
||||
"graphene-path-bool",
|
||||
"graphene-raster",
|
||||
"graphene-raster-nodes",
|
||||
"graphene-svg-renderer",
|
||||
"graphene-text",
|
||||
"graphene-vector",
|
||||
"graphene-vector-nodes",
|
||||
"iai-callgrind",
|
||||
"js-sys",
|
||||
"log",
|
||||
|
@ -2161,6 +2167,7 @@ dependencies = [
|
|||
"dyn-any",
|
||||
"glam",
|
||||
"graphene-core",
|
||||
"graphene-text",
|
||||
"log",
|
||||
"serde",
|
||||
"web-sys",
|
||||
|
@ -2174,9 +2181,15 @@ dependencies = [
|
|||
"dyn-any",
|
||||
"glam",
|
||||
"graphene-core",
|
||||
"graphene-element",
|
||||
"graphene-raster",
|
||||
"graphene-raster-nodes",
|
||||
"graphene-svg-renderer",
|
||||
"graphene-vector",
|
||||
"node-macro",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"specta",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -2189,7 +2202,6 @@ dependencies = [
|
|||
"fern",
|
||||
"futures",
|
||||
"graph-craft",
|
||||
"graphene-core",
|
||||
"graphene-std",
|
||||
"interpreted-executor",
|
||||
"log",
|
||||
|
@ -2204,14 +2216,12 @@ name = "graphene-core"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bezier-rs",
|
||||
"bytemuck",
|
||||
"ctor",
|
||||
"dyn-any",
|
||||
"glam",
|
||||
"half",
|
||||
"image",
|
||||
"kurbo",
|
||||
"log",
|
||||
"node-macro",
|
||||
"num-derive",
|
||||
|
@ -2229,6 +2239,22 @@ dependencies = [
|
|||
"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]]
|
||||
name = "graphene-element-nodes"
|
||||
version = "0.1.0"
|
||||
|
@ -2237,7 +2263,10 @@ dependencies = [
|
|||
"dyn-any",
|
||||
"glam",
|
||||
"graphene-core",
|
||||
"graphene-element",
|
||||
"graphene-math-nodes",
|
||||
"graphene-raster",
|
||||
"graphene-vector",
|
||||
"log",
|
||||
"node-macro",
|
||||
"rand 0.9.0",
|
||||
|
@ -2266,6 +2295,8 @@ dependencies = [
|
|||
"dyn-any",
|
||||
"glam",
|
||||
"graphene-core",
|
||||
"graphene-element",
|
||||
"graphene-vector",
|
||||
"log",
|
||||
"node-macro",
|
||||
"path-bool",
|
||||
|
@ -2273,6 +2304,23 @@ dependencies = [
|
|||
"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]]
|
||||
name = "graphene-raster-nodes"
|
||||
version = "0.1.0"
|
||||
|
@ -2284,6 +2332,7 @@ dependencies = [
|
|||
"futures",
|
||||
"glam",
|
||||
"graphene-core",
|
||||
"graphene-raster",
|
||||
"image",
|
||||
"ndarray",
|
||||
"node-macro",
|
||||
|
@ -2308,14 +2357,18 @@ dependencies = [
|
|||
"graphene-application-io",
|
||||
"graphene-brush",
|
||||
"graphene-core",
|
||||
"graphene-element",
|
||||
"graphene-element-nodes",
|
||||
"graphene-math-nodes",
|
||||
"graphene-path-bool",
|
||||
"graphene-raster",
|
||||
"graphene-raster-nodes",
|
||||
"graphene-svg-renderer",
|
||||
"graphene-text",
|
||||
"graphene-vector",
|
||||
"graphene-vector-nodes",
|
||||
"image",
|
||||
"log",
|
||||
"ndarray",
|
||||
"node-macro",
|
||||
"rand 0.9.0",
|
||||
"rand_chacha 0.9.0",
|
||||
|
@ -2337,13 +2390,73 @@ dependencies = [
|
|||
"dyn-any",
|
||||
"glam",
|
||||
"graphene-core",
|
||||
"graphene-element",
|
||||
"graphene-raster",
|
||||
"graphene-vector",
|
||||
"log",
|
||||
"node-macro",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"specta",
|
||||
"usvg",
|
||||
"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]]
|
||||
name = "graphite-desktop"
|
||||
version = "0.1.0"
|
||||
|
|
25
Cargo.toml
25
Cargo.toml
|
@ -7,14 +7,22 @@ members = [
|
|||
"node-graph/gapplication-io",
|
||||
"node-graph/gbrush",
|
||||
"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/gmath-nodes",
|
||||
"node-graph/gpath-bool",
|
||||
"node-graph/graph-craft",
|
||||
"node-graph/graphene-cli",
|
||||
"node-graph/graster",
|
||||
"node-graph/graster-nodes",
|
||||
"node-graph/gstd",
|
||||
"node-graph/gsvg-renderer",
|
||||
"node-graph/gtext",
|
||||
"node-graph/gvector",
|
||||
"node-graph/gvector-nodes",
|
||||
"node-graph/interpreted-executor",
|
||||
"node-graph/node-macro",
|
||||
"node-graph/preprocessor",
|
||||
|
@ -29,14 +37,22 @@ default-members = [
|
|||
"frontend/wasm",
|
||||
"node-graph/gbrush",
|
||||
"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/gmath-nodes",
|
||||
"node-graph/gpath-bool",
|
||||
"node-graph/graph-craft",
|
||||
"node-graph/graphene-cli",
|
||||
"node-graph/graster",
|
||||
"node-graph/graster-nodes",
|
||||
"node-graph/gstd",
|
||||
"node-graph/gsvg-renderer",
|
||||
"node-graph/gtext",
|
||||
"node-graph/gvector",
|
||||
"node-graph/gvector-nodes",
|
||||
"node-graph/interpreted-executor",
|
||||
"node-graph/node-macro",
|
||||
]
|
||||
|
@ -52,13 +68,18 @@ path-bool = { path = "libraries/path-bool" }
|
|||
graphene-application-io = { path = "node-graph/gapplication-io" }
|
||||
graphene-brush = { path = "node-graph/gbrush" }
|
||||
graphene-core = { path = "node-graph/gcore" }
|
||||
graphene-element = { path = "node-graph/gelement" }
|
||||
graphene-element-nodes = { path = "node-graph/gelement-nodes" }
|
||||
graphene-math-nodes = { path = "node-graph/gmath-nodes" }
|
||||
graphene-path-bool = { path = "node-graph/gpath-bool" }
|
||||
graph-craft = { path = "node-graph/graph-craft" }
|
||||
graphene-raster = { path = "node-graph/graster" }
|
||||
graphene-raster-nodes = { path = "node-graph/graster-nodes" }
|
||||
graphene-std = { path = "node-graph/gstd" }
|
||||
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" }
|
||||
node-macro = { path = "node-graph/node-macro" }
|
||||
wgpu-executor = { path = "node-graph/wgpu-executor" }
|
||||
|
|
|
@ -14,6 +14,7 @@ wgpu = ["dep:wgpu"]
|
|||
# Local dependencies
|
||||
dyn-any = { workspace = true }
|
||||
graphene-core = { workspace = true }
|
||||
graphene-text = { workspace = true }
|
||||
|
||||
# Workspace dependencies
|
||||
glam = { workspace = true }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use dyn_any::{DynAny, StaticType, StaticTypeSized};
|
||||
use glam::{DAffine2, UVec2};
|
||||
use graphene_core::text::FontCache;
|
||||
use graphene_core::transform::Footprint;
|
||||
use graphene_core::vector::style::ViewMode;
|
||||
use graphene_core::view_mode::ViewMode;
|
||||
use graphene_text::FontCache;
|
||||
use std::fmt::Debug;
|
||||
use std::future::Future;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
|
|
@ -14,11 +14,16 @@ serde = ["dep:serde"]
|
|||
# Local dependencies
|
||||
dyn-any = { workspace = true }
|
||||
graphene-core = { workspace = true }
|
||||
graphene-vector = { workspace = true }
|
||||
graphene-raster = { workspace = true }
|
||||
graphene-element = { workspace = true }
|
||||
graphene-raster-nodes = { workspace = true }
|
||||
graphene-svg-renderer = { workspace = true }
|
||||
node-macro = { workspace = true }
|
||||
|
||||
# Workspace dependencies
|
||||
glam = { workspace = true }
|
||||
specta = { workspace = true }
|
||||
|
||||
# Optional workspace dependencies
|
||||
serde = { workspace = true, optional = true, features = ["derive"] }
|
||||
|
@ -26,3 +31,4 @@ serde = { workspace = true, optional = true, features = ["derive"] }
|
|||
[dev-dependencies]
|
||||
# Workspace dependencies
|
||||
tokio = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
use crate::brush_cache::BrushCache;
|
||||
use crate::brush_stroke::{BrushStroke, BrushStyle};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::Node;
|
||||
use graphene_core::blending::BlendMode;
|
||||
use graphene_core::bounds::BoundingBox;
|
||||
use graphene_core::color::{Alpha, Color, Pixel, Sample};
|
||||
use graphene_core::generic::FnNode;
|
||||
use graphene_core::instances::Instance;
|
||||
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::transform::Transform;
|
||||
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::std_nodes::{empty_image, extend_image_to_bounds};
|
||||
use graphene_svg_renderer::renderer::GraphicElementRendered;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct BrushStampGenerator<P: Pixel + Alpha> {
|
||||
|
|
|
@ -27,13 +27,11 @@ rustc-hash = { workspace = true }
|
|||
dyn-any = { workspace = true }
|
||||
ctor = { workspace = true }
|
||||
rand_chacha = { workspace = true }
|
||||
bezier-rs = { workspace = true }
|
||||
specta = { workspace = true }
|
||||
rustybuzz = { workspace = true }
|
||||
image = { workspace = true }
|
||||
half = { workspace = true }
|
||||
tinyvec = { workspace = true }
|
||||
kurbo = { workspace = true }
|
||||
log = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use dyn_any::DynAny;
|
||||
use log::warn;
|
||||
use std::hash::Hash;
|
||||
|
||||
#[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};
|
||||
|
||||
pub trait BoundingBox {
|
||||
|
|
|
@ -5,8 +5,6 @@ use std::fmt::Debug;
|
|||
#[cfg(target_arch = "spirv")]
|
||||
use spirv_std::num_traits::float::Float;
|
||||
|
||||
pub use crate::blending::*;
|
||||
|
||||
pub trait Linear {
|
||||
fn from_f32(x: f32) -> Self;
|
||||
fn to_f32(self) -> f32;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::raster::Color;
|
||||
use crate::color::Color;
|
||||
|
||||
// RENDERING
|
||||
pub const LAYER_OUTLINE_STROKE_COLOR: Color = Color::BLACK;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::raster_types::{CPU, RasterDataTable};
|
||||
use crate::{Color, Ctx};
|
||||
use crate::color::Color;
|
||||
use crate::context::Ctx;
|
||||
|
||||
/// Meant for debugging purposes, not general use. Returns the size of the input type in bytes.
|
||||
#[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()
|
||||
}
|
||||
|
||||
/// Meant for debugging purposes, not general use. Clones the input value.
|
||||
#[node_macro::node(category("Debug"))]
|
||||
fn clone<'i, T: Clone + 'i>(_: impl Ctx, #[implementations(&RasterDataTable<CPU>)] value: &'i T) -> T {
|
||||
value.clone()
|
||||
}
|
||||
// FIXME am I allowed to just remove clone?
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Ctx;
|
||||
use crate::context::Ctx;
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DVec2, IVec2, UVec2};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Color;
|
||||
use crate::color::Color;
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::AlphaBlending;
|
||||
use crate::blending::AlphaBlending;
|
||||
use crate::uuid::NodeId;
|
||||
use dyn_any::StaticType;
|
||||
use glam::DAffine2;
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod blending;
|
||||
pub mod bounds;
|
||||
pub mod color;
|
||||
|
@ -10,35 +7,26 @@ pub mod debug;
|
|||
pub mod extract_xy;
|
||||
pub mod generic;
|
||||
pub mod gradient;
|
||||
mod graphic_element;
|
||||
pub mod instances;
|
||||
pub mod math;
|
||||
pub mod memo;
|
||||
pub mod misc;
|
||||
pub mod ops;
|
||||
pub mod raster;
|
||||
pub mod raster_types;
|
||||
pub mod registry;
|
||||
pub mod structural;
|
||||
pub mod text;
|
||||
pub mod transform;
|
||||
pub mod uuid;
|
||||
pub mod value;
|
||||
pub mod vector;
|
||||
|
||||
pub use crate as graphene_core;
|
||||
pub use blending::*;
|
||||
pub use context::*;
|
||||
pub use ctor;
|
||||
pub use dyn_any::{StaticTypeSized, WasmNotSend, WasmNotSync};
|
||||
pub use graphic_element::*;
|
||||
pub use memo::MemoHash;
|
||||
pub use num_traits;
|
||||
pub use raster::Color;
|
||||
use std::any::TypeId;
|
||||
pub use std::borrow::Cow;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
pub use types::Cow;
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pub mod bbox;
|
||||
pub mod math_ext;
|
||||
pub mod quad;
|
||||
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};
|
||||
|
||||
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
|
||||
impl Transform for DAffine2 {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
|
@ -110,13 +96,6 @@ impl Footprint {
|
|||
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 {
|
||||
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
|
||||
dyn-any = { workspace = true }
|
||||
graphene-core = { workspace = true }
|
||||
graphene-raster = { workspace = true }
|
||||
graphene-vector = { workspace = true }
|
||||
graphene-element = { workspace = true }
|
||||
node-macro = { workspace = true }
|
||||
bezier-rs = { workspace = true }
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use graphene_core::GraphicGroupTable;
|
||||
use graphene_core::blending::BlendMode;
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::context::Ctx;
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
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 {
|
||||
fn multiply_alpha(&mut self, factor: f64);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use glam::DVec2;
|
||||
use graphene_core::GraphicElement;
|
||||
use graphene_core::GraphicGroupTable;
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::context::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractIndex, ExtractVarArgs, OwnedContextImpl};
|
||||
use graphene_core::instances::{InstanceRef, Instances};
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_core::vector::VectorDataTable;
|
||||
use graphene_element::GraphicElement;
|
||||
use graphene_element::GraphicGroupTable;
|
||||
use graphene_raster::{CPU, GPU, RasterDataTable};
|
||||
use graphene_vector::VectorDataTable;
|
||||
use log::warn;
|
||||
|
||||
#[node_macro::node(name("Instance on Points"), category("Instancing"), path(graphene_core::vector))]
|
||||
|
@ -105,7 +106,7 @@ mod test {
|
|||
use glam::DVec2;
|
||||
use graphene_core::Node;
|
||||
use graphene_core::extract_xy::{ExtractXyNode, XY};
|
||||
use graphene_core::vector::VectorData;
|
||||
use graphene_vector::VectorData;
|
||||
use std::pin::Pin;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -4,3 +4,4 @@ pub mod conversion;
|
|||
pub mod instance;
|
||||
pub mod logic;
|
||||
pub mod transform_nodes;
|
||||
pub mod vector_element_nodes;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::context::{Context, Ctx};
|
||||
use graphene_core::vector::VectorDataTable;
|
||||
use graphene_vector::VectorDataTable;
|
||||
|
||||
#[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 {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use core::f64;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::GraphicGroupTable;
|
||||
use graphene_core::context::{CloneVarArgs, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||
use graphene_core::instances::Instances;
|
||||
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
||||
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(""))]
|
||||
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 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;
|
||||
|
||||
// TODO: Eventually remove this migration document upgrade code
|
||||
|
@ -246,6 +245,16 @@ pub struct Artboard {
|
|||
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 {
|
||||
fn default() -> Self {
|
||||
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 graphene_core::color::Color;
|
||||
use graphene_core::context::Ctx;
|
||||
use graphene_core::gradient::GradientStops;
|
||||
use graphene_core::num_traits;
|
||||
use graphene_core::registry::types::{Fraction, Percentage};
|
||||
use graphene_core::{Color, Ctx, num_traits};
|
||||
use log::warn;
|
||||
use math_parser::ast;
|
||||
use math_parser::context::{EvalContext, NothingMap, ValueProvider};
|
||||
|
|
|
@ -11,6 +11,8 @@ license = "MIT OR Apache-2.0"
|
|||
dyn-any = { workspace = true }
|
||||
bezier-rs = { workspace = true }
|
||||
graphene-core = { workspace = true }
|
||||
graphene-vector = { workspace = true }
|
||||
graphene-element = { workspace = true }
|
||||
node-macro = { workspace = true }
|
||||
glam = { workspace = true }
|
||||
specta = { workspace = true }
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::context::{CloneVarArgs, Context, Ctx, ExtractAll, OwnedContextImpl};
|
||||
use graphene_core::instances::{Instance, InstanceRef};
|
||||
use graphene_core::vector::algorithms::merge_by_distance::MergeByDistanceExt;
|
||||
use graphene_core::vector::style::Fill;
|
||||
use graphene_core::vector::{PointId, VectorData, VectorDataTable};
|
||||
use graphene_core::{Color, Ctx, GraphicElement, GraphicGroupTable};
|
||||
pub use path_bool as path_bool_lib;
|
||||
use graphene_element::{GraphicElement, GraphicGroupTable};
|
||||
use graphene_vector::style::Fill;
|
||||
use graphene_vector::{PointId, VectorData, VectorDataTable};
|
||||
use path_bool::{FillRule, PathBooleanOperation};
|
||||
use std::ops::Mul;
|
||||
|
||||
// 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: 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-brush = { 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-raster-nodes = { workspace = true }
|
||||
graphene-text = { workspace = true }
|
||||
graphene-vector = { workspace = true }
|
||||
graphene-vector-nodes = { workspace = true }
|
||||
|
||||
# Workspace dependencies
|
||||
log = { workspace = true }
|
||||
|
|
|
@ -7,12 +7,13 @@ pub use glam::{DAffine2, DVec2, IVec2, UVec2};
|
|||
use graphene_application_io::SurfaceFrame;
|
||||
use graphene_brush::brush_cache::BrushCache;
|
||||
use graphene_brush::brush_stroke::BrushStroke;
|
||||
use graphene_core::raster_types::CPU;
|
||||
use graphene_core::transform::ReferencePoint;
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::uuid::NodeId;
|
||||
use graphene_core::vector::style::Fill;
|
||||
use graphene_core::{Color, MemoHash, Node, Type};
|
||||
use graphene_core::{MemoHash, Node, Type};
|
||||
use graphene_raster::CPU;
|
||||
use graphene_svg_renderer::RenderMetadata;
|
||||
use graphene_vector::reference_point::ReferencePoint;
|
||||
use graphene_vector::style::Fill;
|
||||
use std::fmt::Display;
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
|
@ -181,41 +182,41 @@ tagged_value! {
|
|||
F64Array4([f64; 4]),
|
||||
NodePath(Vec<NodeId>),
|
||||
#[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
|
||||
// ====================
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
ArtboardGroup(graphene_core::ArtboardGroupTable),
|
||||
ArtboardGroup(graphene_element::ArtboardGroupTable),
|
||||
// ============
|
||||
// STRUCT TYPES
|
||||
// ============
|
||||
Artboard(graphene_core::Artboard),
|
||||
Image(graphene_core::raster::Image<Color>),
|
||||
Color(graphene_core::raster::color::Color),
|
||||
OptionalColor(Option<graphene_core::raster::color::Color>),
|
||||
Artboard(graphene_element::Artboard),
|
||||
Image(graphene_raster::image::Image<Color>),
|
||||
Color(Color),
|
||||
OptionalColor(Option<Color>),
|
||||
Palette(Vec<Color>),
|
||||
Subpaths(Vec<bezier_rs::Subpath<graphene_core::vector::PointId>>),
|
||||
Fill(graphene_core::vector::style::Fill),
|
||||
Stroke(graphene_core::vector::style::Stroke),
|
||||
Gradient(graphene_core::vector::style::Gradient),
|
||||
Subpaths(Vec<bezier_rs::Subpath<graphene_vector::PointId>>),
|
||||
Fill(graphene_vector::style::Fill),
|
||||
Stroke(graphene_vector::style::Stroke),
|
||||
Gradient(graphene_core::gradient::Gradient),
|
||||
#[serde(alias = "GradientPositions")] // TODO: Eventually remove this alias document upgrade code
|
||||
GradientStops(graphene_core::vector::style::GradientStops),
|
||||
Font(graphene_core::text::Font),
|
||||
GradientStops(graphene_core::gradient::GradientStops),
|
||||
Font(graphene_text::Font),
|
||||
BrushStrokes(Vec<BrushStroke>),
|
||||
BrushCache(BrushCache),
|
||||
DocumentNode(DocumentNode),
|
||||
Curve(graphene_raster_nodes::curve::Curve),
|
||||
Footprint(graphene_core::transform::Footprint),
|
||||
VectorModification(Box<graphene_core::vector::VectorModification>),
|
||||
FontCache(Arc<graphene_core::text::FontCache>),
|
||||
VectorModification(Box<graphene_vector_nodes::modification::VectorModification>),
|
||||
FontCache(Arc<graphene_text::FontCache>),
|
||||
// ==========
|
||||
// ENUM TYPES
|
||||
// ==========
|
||||
|
@ -232,21 +233,21 @@ tagged_value! {
|
|||
DomainWarpType(graphene_raster_nodes::adjustments::DomainWarpType),
|
||||
RelativeAbsolute(graphene_raster_nodes::adjustments::RelativeAbsolute),
|
||||
SelectiveColorChoice(graphene_raster_nodes::adjustments::SelectiveColorChoice),
|
||||
GridType(graphene_core::vector::misc::GridType),
|
||||
ArcType(graphene_core::vector::misc::ArcType),
|
||||
MergeByDistanceAlgorithm(graphene_core::vector::misc::MergeByDistanceAlgorithm),
|
||||
PointSpacingType(graphene_core::vector::misc::PointSpacingType),
|
||||
GridType(graphene_vector_nodes::misc::GridType),
|
||||
ArcType(graphene_vector_nodes::misc::ArcType),
|
||||
MergeByDistanceAlgorithm(graphene_vector_nodes::misc::MergeByDistanceAlgorithm),
|
||||
PointSpacingType(graphene_vector_nodes::misc::PointSpacingType),
|
||||
#[serde(alias = "LineCap")]
|
||||
StrokeCap(graphene_core::vector::style::StrokeCap),
|
||||
StrokeCap(graphene_vector::style::StrokeCap),
|
||||
#[serde(alias = "LineJoin")]
|
||||
StrokeJoin(graphene_core::vector::style::StrokeJoin),
|
||||
StrokeAlign(graphene_core::vector::style::StrokeAlign),
|
||||
PaintOrder(graphene_core::vector::style::PaintOrder),
|
||||
FillType(graphene_core::vector::style::FillType),
|
||||
FillChoice(graphene_core::vector::style::FillChoice),
|
||||
GradientType(graphene_core::vector::style::GradientType),
|
||||
ReferencePoint(graphene_core::transform::ReferencePoint),
|
||||
CentroidType(graphene_core::vector::misc::CentroidType),
|
||||
StrokeJoin(graphene_vector::style::StrokeJoin),
|
||||
StrokeAlign(graphene_vector::style::StrokeAlign),
|
||||
PaintOrder(graphene_vector::style::PaintOrder),
|
||||
FillType(graphene_vector::style::FillType),
|
||||
FillChoice(graphene_vector::style::FillChoice),
|
||||
GradientType(graphene_core::gradient::GradientType),
|
||||
ReferencePoint(graphene_vector::reference_point::ReferencePoint),
|
||||
CentroidType(graphene_vector_nodes::misc::CentroidType),
|
||||
BooleanOperation(graphene_path_bool::BooleanOperation),
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ gpu = ["interpreted-executor/gpu", "graphene-std/gpu", "wgpu-executor"]
|
|||
|
||||
[dependencies]
|
||||
# Local dependencies
|
||||
graphene-core = { workspace = true }
|
||||
graphene-std = { workspace = true }
|
||||
interpreted-executor = { workspace = true }
|
||||
graph-craft = { workspace = true, features = ["loading"] }
|
||||
|
|
|
@ -14,6 +14,7 @@ serde = ["dep:serde"]
|
|||
# Local dependencies
|
||||
dyn-any = { workspace = true }
|
||||
graphene-core = { workspace = true }
|
||||
graphene-raster = { workspace = true }
|
||||
node-macro = { workspace = true }
|
||||
|
||||
# Workspace dependencies
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
use crate::curve::CubicSplines;
|
||||
use dyn_any::DynAny;
|
||||
use graphene_core::Node;
|
||||
use graphene_core::blending::BlendMode;
|
||||
|
@ -8,12 +7,12 @@ use graphene_core::color::Color;
|
|||
use graphene_core::color::Pixel;
|
||||
use graphene_core::context::Ctx;
|
||||
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_raster::curve::CubicSplines;
|
||||
use graphene_raster::image::Image;
|
||||
use graphene_raster::{CPU, Raster, RasterDataTable};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Debug;
|
||||
|
||||
// TODO: Implement the following:
|
||||
// Color Balance
|
||||
// Aims for interoperable compatibility with:
|
||||
|
@ -1181,8 +1180,8 @@ fn color_overlay<T: Adjust<Color>>(
|
|||
mod test {
|
||||
use graphene_core::blending::BlendMode;
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::raster::image::Image;
|
||||
use graphene_core::raster_types::{Raster, RasterDataTable};
|
||||
use graphene_raster::image::Image;
|
||||
use graphene_raster::{Raster, RasterDataTable};
|
||||
|
||||
#[tokio::test]
|
||||
async fn color_overlay_multiply() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
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_raster::image::Image;
|
||||
use graphene_raster::{CPU, Raster, RasterDataTable};
|
||||
use image::{DynamicImage, GenericImage, GenericImageView, GrayImage, ImageBuffer, Luma, Rgba, RgbaImage};
|
||||
use ndarray::{Array2, ArrayBase, Dim, OwnedRepr};
|
||||
use std::cmp::{max, min};
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use graphene_core::color::Color;
|
||||
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_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.
|
||||
#[node_macro::node(category("Raster: Filter"))]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use graphene_core::color::Color;
|
||||
use graphene_core::context::Ctx;
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_raster::{CPU, RasterDataTable};
|
||||
|
||||
#[node_macro::node(category("Color"))]
|
||||
async fn image_color_palette(
|
||||
|
@ -65,8 +65,8 @@ async fn image_color_palette(
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use graphene_core::raster::image::Image;
|
||||
use graphene_core::raster_types::{Raster, RasterDataTable};
|
||||
use graphene_raster::image::Image;
|
||||
use graphene_raster::{Raster, RasterDataTable};
|
||||
|
||||
#[test]
|
||||
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::instances::Instance;
|
||||
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_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_chacha::ChaCha8Rng;
|
||||
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;
|
||||
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;
|
||||
use graphene_core::color::Pixel;
|
||||
|
||||
pub trait Bitmap {
|
||||
type Pixel: Pixel;
|
|
@ -1,18 +1,19 @@
|
|||
use super::Color;
|
||||
use crate::AlphaBlending;
|
||||
use crate::color::float_to_srgb_u8;
|
||||
use crate::instances::{Instance, Instances};
|
||||
use crate::raster_types::Raster;
|
||||
use crate::bitmap::{Bitmap, BitmapMut};
|
||||
use crate::{CPU, Raster, RasterDataTable};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
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;
|
||||
|
||||
mod base64_serde {
|
||||
//! Basic wrapper for [`serde`] to perform [`base64`] encoding
|
||||
|
||||
use super::super::Pixel;
|
||||
use base64::Engine;
|
||||
use graphene_core::color::Pixel;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
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>
|
||||
where
|
||||
P::ColorChannel: Linear,
|
||||
|
@ -485,7 +485,6 @@ mod test {
|
|||
#[test]
|
||||
fn test_image_serialization_roundtrip() {
|
||||
use super::*;
|
||||
use crate::Color;
|
||||
let image = Image {
|
||||
width: 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::bounds::BoundingBox;
|
||||
use crate::instances::Instances;
|
||||
use crate::math::quad::Quad;
|
||||
use crate::raster::Image;
|
||||
use crate::image::Image;
|
||||
use core::ops::Deref;
|
||||
use dyn_any::DynAny;
|
||||
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")]
|
||||
use std::sync::Arc;
|
||||
|
|
@ -32,9 +32,14 @@ graphene-path-bool = { workspace = true }
|
|||
graphene-math-nodes = { workspace = true }
|
||||
graphene-svg-renderer = { workspace = true }
|
||||
graphene-application-io = { workspace = true }
|
||||
graphene-element = { workspace = true }
|
||||
graphene-element-nodes = { workspace = true }
|
||||
graphene-raster = { workspace = true }
|
||||
graphene-raster-nodes = { workspace = true }
|
||||
graphene-vector = { workspace = true }
|
||||
graphene-vector-nodes = { workspace = true }
|
||||
graphene-brush = { workspace = true }
|
||||
graphene-text = { workspace = true }
|
||||
|
||||
# Workspace dependencies
|
||||
fastnoise-lite = { workspace = true }
|
||||
|
@ -66,8 +71,5 @@ web-sys = { workspace = true, optional = true, features = [
|
|||
"ImageBitmapRenderingContext",
|
||||
] }
|
||||
|
||||
# Required dependencies
|
||||
ndarray = "0.16.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true }
|
||||
|
|
|
@ -6,13 +6,14 @@ pub mod wasm_application_io;
|
|||
|
||||
pub use graphene_application_io as application_io;
|
||||
pub use graphene_brush as brush;
|
||||
pub use graphene_core::vector;
|
||||
pub use graphene_core::*;
|
||||
pub use graphene_element as element;
|
||||
pub use graphene_element_nodes as element_nodes;
|
||||
pub use graphene_element_nodes::animation;
|
||||
pub use graphene_math_nodes as math_nodes;
|
||||
pub use graphene_path_bool as path_bool;
|
||||
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
|
||||
pub mod renderer {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::vector::{VectorData, VectorDataTable};
|
||||
use graph_craft::wasm_application_io::WasmEditorApi;
|
||||
use graphene_core::Ctx;
|
||||
pub use graphene_core::text::*;
|
||||
use graphene_core::context::Ctx;
|
||||
pub use graphene_text::*;
|
||||
use graphene_vector::{VectorData, VectorDataTable};
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
fn text<'i: 'n>(
|
||||
|
|
|
@ -13,11 +13,17 @@ vello = ["dep:vello", "bezier-rs/kurbo"]
|
|||
# Local dependencies
|
||||
dyn-any = { 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 }
|
||||
|
||||
# Workspace dependencies
|
||||
glam = { workspace = true }
|
||||
specta = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
log = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||
use glam::DVec2;
|
||||
use graphene_core::vector::PointId;
|
||||
use graphene_vector::PointId;
|
||||
|
||||
pub fn convert_usvg_path(path: &usvg::Path) -> Vec<Subpath<PointId>> {
|
||||
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::gradient::{Gradient, GradientType};
|
||||
use graphene_core::uuid::generate_uuid;
|
||||
use graphene_core::vector::style::{Fill, PaintOrder, PathStyle, Stroke, StrokeAlign, StrokeCap, StrokeJoin, ViewMode};
|
||||
use std::fmt::Write;
|
||||
use graphene_core::view_mode::ViewMode;
|
||||
use graphene_vector::style::{Fill, PaintOrder, PathStyle, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||
|
||||
pub trait RenderExt {
|
||||
type Output;
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
use crate::render_ext::RenderExt;
|
||||
use crate::to_peniko::BlendModeExt;
|
||||
use base64::Engine;
|
||||
use bezier_rs::Subpath;
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::blending::BlendMode;
|
||||
use graphene_core::bounds::BoundingBox;
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::instances::Instance;
|
||||
use graphene_core::math::quad::Quad;
|
||||
use graphene_core::raster::Image;
|
||||
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
||||
use graphene_core::math::rect::Rect;
|
||||
use graphene_core::transform::{Footprint, Transform};
|
||||
use graphene_core::uuid::{NodeId, generate_uuid};
|
||||
use graphene_core::vector::VectorDataTable;
|
||||
use graphene_core::vector::click_target::{ClickTarget, FreePoint};
|
||||
use graphene_core::vector::style::{Fill, Stroke, StrokeAlign, ViewMode};
|
||||
use graphene_core::{AlphaBlending, Artboard, ArtboardGroupTable, GraphicElement, GraphicGroupTable};
|
||||
use graphene_core::view_mode::ViewMode;
|
||||
use graphene_element::{Artboard, ArtboardGroupTable, GraphicElement, GraphicGroupTable};
|
||||
use graphene_raster::image::Image;
|
||||
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 std::collections::{HashMap, HashSet};
|
||||
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 graphene_core::consts::{DEFAULT_FONT_FAMILY, DEFAULT_FONT_STYLE};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A font type (storing font family and font style and an optional preview URL)
|
||||
|
@ -16,7 +17,7 @@ impl Font {
|
|||
}
|
||||
impl Default for Font {
|
||||
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`)
|
||||
|
@ -33,9 +34,7 @@ impl FontCache {
|
|||
if self.font_file_data.contains_key(font) {
|
||||
Some(font)
|
||||
} else {
|
||||
self.font_file_data
|
||||
.keys()
|
||||
.find(|font| font.font_family == crate::consts::DEFAULT_FONT_FAMILY && font.font_style == crate::consts::DEFAULT_FONT_STYLE)
|
||||
self.font_file_data.keys().find(|font| font.font_family == DEFAULT_FONT_FAMILY && font.font_style == DEFAULT_FONT_STYLE)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::vector::PointId;
|
||||
use bezier_rs::{ManipulatorGroup, Subpath};
|
||||
use glam::DVec2;
|
||||
use graphene_vector::PointId;
|
||||
use rustybuzz::ttf_parser::{GlyphId, OutlineBuilder};
|
||||
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 crate::vector::misc::{PointSpacingType, dvec2_to_point};
|
||||
use crate::misc::{PointSpacingType, dvec2_to_point};
|
||||
use glam::DVec2;
|
||||
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 graphene_vector::{PointDomain, PointId, SegmentDomain, VectorData, VectorDataIndex};
|
||||
use petgraph::prelude::UnGraphMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::vector::PointId;
|
||||
use bezier_rs::{Bezier, BezierHandles, Join, Subpath, TValue};
|
||||
use graphene_vector::PointId;
|
||||
|
||||
/// Value to control smoothness and mathematical accuracy to offset a cubic Bezier.
|
||||
const CUBIC_REGULARIZATION_ACCURACY: f64 = 0.5;
|
|
@ -141,7 +141,7 @@ mod tests {
|
|||
use super::*;
|
||||
#[test]
|
||||
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};
|
||||
|
||||
// These points are just chosen arbitrary
|
|
@ -1,10 +1,9 @@
|
|||
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 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 {
|
||||
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::Ctx;
|
||||
use crate::instances::Instance;
|
||||
use crate::uuid::generate_uuid;
|
||||
use crate::misc::point_to_dvec2;
|
||||
use bezier_rs::BezierHandles;
|
||||
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 log::warn;
|
||||
use serde::de::{SeqAccess, Visitor};
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt;
|
||||
use std::hash::BuildHasher;
|
||||
use std::hash::Hash;
|
||||
|
||||
/// Represents a procedural change to the [`PointDomain`] in [`VectorData`].
|
||||
#[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?
|
||||
// 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>
|
||||
where
|
||||
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::offset_subpath::offset_subpath;
|
||||
use super::algorithms::spline::{solve_spline_first_handle_closed, solve_spline_first_handle_open};
|
||||
use super::misc::{CentroidType, point_to_dvec2};
|
||||
use super::style::{Fill, Gradient, GradientStops, Stroke};
|
||||
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 super::misc::{CentroidType, MergeByDistanceAlgorithm, PointSpacingType, dvec2_to_point, point_to_dvec2};
|
||||
use crate::modification::VectorDataExt;
|
||||
use bezier_rs::{Join, ManipulatorGroup, Subpath};
|
||||
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 std::collections::hash_map::DefaultHasher;
|
||||
use std::f64::consts::PI;
|
||||
use std::f64::consts::TAU;
|
||||
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))]
|
||||
async fn round_corners(
|
||||
_: impl Ctx,
|
||||
|
@ -1078,61 +683,6 @@ async fn solidify_stroke(_: impl Ctx, vector_data: VectorDataTable) -> VectorDat
|
|||
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.
|
||||
#[node_macro::node(category(""), path(graphene_core::vector))]
|
||||
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))
|
||||
}
|
||||
|
||||
#[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))]
|
||||
async fn path_length(_: impl Ctx, source: VectorDataTable) -> f64 {
|
||||
source
|
||||
|
@ -1953,8 +1498,9 @@ async fn centroid(ctx: impl Ctx + CloneVarArgs + ExtractAll, vector_data: impl N
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::Node;
|
||||
use bezier_rs::Bezier;
|
||||
use graphene_core::Node;
|
||||
use graphene_core::transform::Footprint;
|
||||
use kurbo::Rect;
|
||||
use std::pin::Pin;
|
||||
|
||||
|
@ -1990,47 +1536,6 @@ mod test {
|
|||
}
|
||||
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]
|
||||
async fn bounding_box() {
|
||||
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]
|
||||
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() {
|
||||
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;
|
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::quad::Quad;
|
||||
use crate::vector::PointId;
|
||||
use crate::math_ext::QuadExt;
|
||||
use crate::vector_data::PointId;
|
||||
use bezier_rs::Subpath;
|
||||
use glam::{DAffine2, DMat2, DVec2};
|
||||
use graphene_core::math::quad::Quad;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
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 graphene_core::math::quad::Quad;
|
||||
use graphene_core::math::rect::Rect;
|
||||
|
||||
pub trait QuadExt {
|
||||
/// Get all the edges in the rect as linear bezier curves
|
|
@ -1,5 +1,5 @@
|
|||
use crate::math::bbox::AxisAlignedBbox;
|
||||
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)]
|
||||
pub enum ReferencePoint {
|
|
@ -1,9 +1,9 @@
|
|||
//! Contains stylistic options for SVG elements.
|
||||
|
||||
use crate::Color;
|
||||
pub use crate::gradient::*;
|
||||
use dyn_any::DynAny;
|
||||
use glam::DAffine2;
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::gradient::{Gradient, GradientStops};
|
||||
|
||||
/// Describes the fill of a layer.
|
||||
///
|
||||
|
@ -528,8 +528,8 @@ impl PathStyle {
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use graphene_core::vector::style::{Fill, PathStyle};
|
||||
/// # use graphene_core::raster::color::Color;
|
||||
/// # use graphene_vector::style::{Fill, PathStyle};
|
||||
/// # use graphene_core::color::Color;
|
||||
/// let fill = Fill::solid(Color::RED);
|
||||
/// let style = PathStyle::new(None, fill.clone());
|
||||
///
|
||||
|
@ -543,8 +543,8 @@ impl PathStyle {
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use graphene_core::vector::style::{Fill, Stroke, PathStyle};
|
||||
/// # use graphene_core::raster::color::Color;
|
||||
/// # use graphene_vector::style::{Fill, Stroke, PathStyle};
|
||||
/// # use graphene_core::color::Color;
|
||||
/// let stroke = Stroke::new(Some(Color::GREEN), 42.);
|
||||
/// let style = PathStyle::new(Some(stroke.clone()), Fill::None);
|
||||
///
|
||||
|
@ -558,8 +558,8 @@ impl PathStyle {
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use graphene_core::vector::style::{Fill, PathStyle};
|
||||
/// # use graphene_core::raster::color::Color;
|
||||
/// # use graphene_vector::style::{Fill, PathStyle};
|
||||
/// # use graphene_core::color::Color;
|
||||
/// let mut style = PathStyle::default();
|
||||
///
|
||||
/// assert_eq!(*style.fill(), Fill::None);
|
||||
|
@ -583,8 +583,8 @@ impl PathStyle {
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use graphene_core::vector::style::{Stroke, PathStyle};
|
||||
/// # use graphene_core::raster::color::Color;
|
||||
/// # use graphene_vector::style::{Stroke, PathStyle};
|
||||
/// # use graphene_core::color::Color;
|
||||
/// let mut style = PathStyle::default();
|
||||
///
|
||||
/// assert_eq!(style.stroke(), None);
|
||||
|
@ -602,8 +602,8 @@ impl PathStyle {
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use graphene_core::vector::style::{Fill, PathStyle};
|
||||
/// # use graphene_core::raster::color::Color;
|
||||
/// # use graphene_vector::style::{Fill, PathStyle};
|
||||
/// # use graphene_core::color::Color;
|
||||
/// let mut style = PathStyle::new(None, Fill::Solid(Color::RED));
|
||||
///
|
||||
/// assert_ne!(*style.fill(), Fill::None);
|
||||
|
@ -620,8 +620,8 @@ impl PathStyle {
|
|||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use graphene_core::vector::style::{Fill, Stroke, PathStyle};
|
||||
/// # use graphene_core::raster::color::Color;
|
||||
/// # use graphene_vector::style::{Fill, Stroke, PathStyle};
|
||||
/// # use graphene_core::color::Color;
|
||||
/// let mut style = PathStyle::new(Some(Stroke::new(Some(Color::GREEN), 42.)), Fill::None);
|
||||
///
|
||||
/// assert!(style.stroke().is_some());
|
||||
|
@ -634,15 +634,3 @@ impl PathStyle {
|
|||
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 indexed;
|
||||
mod modification;
|
||||
|
||||
use super::misc::{dvec2_to_point, point_to_dvec2};
|
||||
use super::style::{PathStyle, Stroke};
|
||||
use crate::bounds::BoundingBox;
|
||||
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};
|
||||
use crate::click_target::{ClickTargetType, FreePoint};
|
||||
use crate::dvec2_to_point;
|
||||
use crate::style::{PathStyle, Stroke};
|
||||
pub use attributes::*;
|
||||
use bezier_rs::{BezierHandles, ManipulatorGroup};
|
||||
use core::borrow::Borrow;
|
||||
use core::hash::Hash;
|
||||
use dyn_any::DynAny;
|
||||
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;
|
||||
use kurbo::{Affine, Rect, Shape};
|
||||
pub use modification::*;
|
||||
use std::any::Any;
|
||||
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
|
||||
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,
|
||||
|
||||
// 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)]
|
||||
|
@ -91,7 +97,7 @@ pub struct VectorData {
|
|||
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.
|
||||
pub upstream_graphic_group: Option<GraphicGroupTable>,
|
||||
pub upstream_graphic_group: Option<UpstreamGraphicGroup>,
|
||||
}
|
||||
|
||||
impl Default for VectorData {
|
|
@ -1,8 +1,10 @@
|
|||
use crate::vector::misc::dvec2_to_point;
|
||||
use crate::vector::vector_data::{HandleId, VectorData};
|
||||
use crate::dvec2_to_point;
|
||||
use crate::vector_data::{HandleId, VectorData};
|
||||
use bezier_rs::{BezierHandles, ManipulatorGroup};
|
||||
use dyn_any::DynAny;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::uuid::generate_uuid;
|
||||
use log::warn;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter::zip;
|
||||
|
@ -21,7 +23,7 @@ macro_rules! create_ids {
|
|||
|
||||
/// Generate a new random id
|
||||
pub fn generate() -> Self {
|
||||
Self(crate::uuid::generate_uuid())
|
||||
Self(generate_uuid())
|
||||
}
|
||||
|
||||
pub fn generate_from_hash(self, node_id: u64) -> Self {
|
||||
|
@ -82,7 +84,7 @@ impl std::hash::BuildHasher for NoHashBuilder {
|
|||
pub struct PointDomain {
|
||||
id: Vec<PointId>,
|
||||
#[serde(alias = "positions")]
|
||||
pub(crate) position: Vec<DVec2>,
|
||||
pub position: Vec<DVec2>,
|
||||
}
|
||||
|
||||
impl Hash for PointDomain {
|
||||
|
@ -166,7 +168,7 @@ impl PointDomain {
|
|||
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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
pub(crate) fn start_point(&self) -> &[usize] {
|
||||
pub fn start_point(&self) -> &[usize] {
|
||||
&self.start_point
|
||||
}
|
||||
|
||||
pub(crate) fn end_point(&self) -> &[usize] {
|
||||
pub fn end_point(&self) -> &[usize] {
|
||||
&self.end_point
|
||||
}
|
||||
|
||||
|
@ -304,7 +306,7 @@ impl SegmentDomain {
|
|||
&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");
|
||||
|
||||
self.id.push(id);
|
||||
|
@ -314,20 +316,20 @@ impl SegmentDomain {
|
|||
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())
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
nested.map(|((a, b), c)| (a, b, c))
|
||||
}
|
||||
|
@ -336,26 +338,26 @@ impl SegmentDomain {
|
|||
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()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
/// 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]))
|
||||
}
|
||||
|
||||
/// 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))
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
(true, false) => Some(b),
|
||||
(false, true) => Some(a),
|
||||
|
@ -400,22 +402,22 @@ impl SegmentDomain {
|
|||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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))
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
|
@ -433,7 +435,7 @@ impl SegmentDomain {
|
|||
/// Iterates over segments in the domain, mutably.
|
||||
///
|
||||
/// 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 start_point = self.start_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.
|
||||
///
|
||||
/// Currently, segment data is not stored as it is not used, but it could easily be added.
|
||||
pub(crate) point_graph: UnGraph<Point, ()>,
|
||||
pub(crate) segment_to_edge: FxHashMap<SegmentId, EdgeIndex>,
|
||||
pub point_graph: UnGraph<Point, ()>,
|
||||
pub segment_to_edge: FxHashMap<SegmentId, EdgeIndex>,
|
||||
/// Get the offset from the point ID.
|
||||
pub(crate) point_to_offset: FxHashMap<PointId, usize>,
|
||||
pub point_to_offset: FxHashMap<PointId, usize>,
|
||||
// TODO: faces
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ mod benchmark_util;
|
|||
|
||||
use benchmark_util::{bench_for_each_demo, setup_network};
|
||||
use criterion::{Criterion, criterion_group, criterion_main};
|
||||
use graphene_std::Context;
|
||||
use graphene_std::context::Context;
|
||||
|
||||
fn subsequent_evaluations(c: &mut Criterion) {
|
||||
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 criterion::{Criterion, criterion_group, criterion_main};
|
||||
use graphene_std::Context;
|
||||
use graphene_std::context::Context;
|
||||
|
||||
fn run_once(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("Run Once");
|
||||
|
|
|
@ -2,22 +2,21 @@ use dyn_any::StaticType;
|
|||
use glam::{DVec2, IVec2, UVec2};
|
||||
use graph_craft::document::value::RenderOutput;
|
||||
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")]
|
||||
use graphene_std::any::DowncastBothNode;
|
||||
use graphene_std::any::{ComposeTypeErased, DynAnyNode, IntoTypeErasedNode};
|
||||
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")]
|
||||
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 once_cell::sync::Lazy;
|
||||
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 => 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 => 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::MemoNode<_, _>, input: Context, fn_params: [Context => Image<Color>]),
|
||||
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::generic;
|
||||
use graph_craft::wasm_application_io::WasmEditorApi;
|
||||
use graphene_std::Context;
|
||||
use graphene_std::context::Context;
|
||||
use graphene_std::uuid::NodeId;
|
||||
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 unit = parse_quote!(gcore::Context);
|
||||
let unit = parse_quote!(gcore::context::Context);
|
||||
let parameter_types: Vec<_> = parsed
|
||||
.fields
|
||||
.iter()
|
||||
|
|
|
@ -643,7 +643,7 @@ impl ParsedNodeFn {
|
|||
}));
|
||||
self.input.ty = parse_quote!(#ident);
|
||||
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 == "_" {
|
||||
|
|
|
@ -5,7 +5,8 @@ pub use context::Context;
|
|||
use dyn_any::StaticType;
|
||||
use glam::UVec2;
|
||||
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;
|
||||
use std::sync::Arc;
|
||||
use vello::{AaConfig, AaSupport, RenderParams, Renderer, RendererOptions, Scene};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue