mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-03 21:08:18 +00:00
Add basic vector nodes (#1059)
* Add some derives * Allow node_fn with mutable inputs * Add basic vector nodes * Revert elipse tool changes * Fix the elipse tool again * Change spacer width * Bubble serde feature in graph craft --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
7e37fb41a4
commit
a2046a51b1
22 changed files with 707 additions and 151 deletions
|
@ -1,15 +1,15 @@
|
|||
use crate::uuid::ManipulatorGroupId;
|
||||
use crate::vector::VectorData;
|
||||
use crate::Node;
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
use super::subpath::Subpath;
|
||||
|
||||
type VectorData = Subpath;
|
||||
use bezier_rs::Subpath;
|
||||
use glam::DVec2;
|
||||
|
||||
pub struct UnitCircleGenerator;
|
||||
|
||||
#[node_macro::node_fn(UnitCircleGenerator)]
|
||||
fn unit_circle(_input: ()) -> VectorData {
|
||||
Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE)
|
||||
super::VectorData::from_subpath(Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -17,19 +17,17 @@ pub struct UnitSquareGenerator;
|
|||
|
||||
#[node_macro::node_fn(UnitSquareGenerator)]
|
||||
fn unit_square(_input: ()) -> VectorData {
|
||||
Subpath::new_rect(DVec2::ZERO, DVec2::ONE)
|
||||
super::VectorData::from_subpath(Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE))
|
||||
}
|
||||
|
||||
// TODO: I removed the Arc requirement we shouuld think about when it makes sense to use its
|
||||
// vs making a generic value node
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PathGenerator<P> {
|
||||
path_data: P,
|
||||
}
|
||||
pub struct PathGenerator;
|
||||
|
||||
#[node_macro::node_fn(PathGenerator)]
|
||||
fn generate_path(_input: (), path_data: Subpath) -> VectorData {
|
||||
path_data
|
||||
fn generate_path(path_data: Subpath<ManipulatorGroupId>) -> super::VectorData {
|
||||
super::VectorData::from_subpath(path_data)
|
||||
}
|
||||
|
||||
use crate::raster::Image;
|
||||
|
@ -40,7 +38,7 @@ pub struct BlitSubpath<P> {
|
|||
}
|
||||
|
||||
#[node_macro::node_fn(BlitSubpath)]
|
||||
fn bilt_subpath(base_image: Image, path_data: Subpath) -> Image {
|
||||
fn bilt_subpath(base_image: Image, path_data: VectorData) -> Image {
|
||||
log::info!("Blitting subpath {path_data:#?}");
|
||||
// TODO: Get forma to compile
|
||||
/*use forma::prelude::*;
|
||||
|
@ -58,20 +56,3 @@ fn bilt_subpath(base_image: Image, path_data: Subpath) -> Image {
|
|||
|
||||
base_image
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TransformSubpathNode<Translation, Rotation, Scale, Shear> {
|
||||
translate: Translation,
|
||||
rotate: Rotation,
|
||||
scale: Scale,
|
||||
shear: Shear,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(TransformSubpathNode)]
|
||||
fn transform_subpath(subpath: Subpath, translate: DVec2, rotate: f64, scale: DVec2, shear: DVec2) -> VectorData {
|
||||
let (sin, cos) = rotate.sin_cos();
|
||||
|
||||
let mut subpath = subpath;
|
||||
subpath.apply_affine(DAffine2::from_cols_array(&[scale.x + cos, shear.y + sin, shear.x - sin, scale.y + cos, translate.x, translate.y]));
|
||||
subpath
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ impl Default for ManipulatorPoint {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||
impl Hash for ManipulatorPoint {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.position.to_array().iter().for_each(|x| x.to_bits().hash(state));
|
||||
|
|
|
@ -14,3 +14,8 @@ pub use vector_data::VectorData;
|
|||
|
||||
mod id_vec;
|
||||
pub use id_vec::IdBackedVec;
|
||||
|
||||
mod vector_nodes;
|
||||
pub use vector_nodes::*;
|
||||
|
||||
pub use bezier_rs;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use crate::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
|
||||
use crate::Color;
|
||||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Display, Write};
|
||||
|
@ -19,7 +20,7 @@ fn format_opacity(name: &str, opacity: f32) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Hash, Serialize, Deserialize, DynAny, specta::Type)]
|
||||
pub enum GradientType {
|
||||
#[default]
|
||||
Linear,
|
||||
|
@ -30,30 +31,41 @@ pub enum GradientType {
|
|||
///
|
||||
/// Contains the start and end points, along with the colors at varying points along the length.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, PartialEq, Default, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, DynAny, specta::Type)]
|
||||
pub struct Gradient {
|
||||
pub start: DVec2,
|
||||
pub end: DVec2,
|
||||
pub transform: DAffine2,
|
||||
pub positions: Vec<(f64, Option<Color>)>,
|
||||
uuid: u64,
|
||||
pub gradient_type: GradientType,
|
||||
}
|
||||
impl core::hash::Hash for Gradient {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.positions.len().hash(state);
|
||||
[].iter()
|
||||
.chain(self.start.to_array().iter())
|
||||
.chain(self.end.to_array().iter())
|
||||
.chain(self.transform.to_cols_array().iter())
|
||||
.chain(self.positions.iter().map(|(position, _)| position))
|
||||
.for_each(|x| x.to_bits().hash(state));
|
||||
self.positions.iter().for_each(|(_, color)| color.hash(state));
|
||||
self.gradient_type.hash(state);
|
||||
}
|
||||
}
|
||||
impl Gradient {
|
||||
/// Constructs a new gradient with the colors at 0 and 1 specified.
|
||||
pub fn new(start: DVec2, start_color: Color, end: DVec2, end_color: Color, transform: DAffine2, uuid: u64, gradient_type: GradientType) -> Self {
|
||||
pub fn new(start: DVec2, start_color: Color, end: DVec2, end_color: Color, transform: DAffine2, _uuid: u64, gradient_type: GradientType) -> Self {
|
||||
Gradient {
|
||||
start,
|
||||
end,
|
||||
positions: vec![(0., Some(start_color)), (1., Some(end_color))],
|
||||
transform,
|
||||
uuid,
|
||||
gradient_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the gradient def with the uuid specified
|
||||
fn render_defs(&self, svg_defs: &mut String, multiplied_transform: DAffine2, bounds: [DVec2; 2], transformed_bounds: [DVec2; 2]) {
|
||||
/// Adds the gradient def, returning the gradient id
|
||||
fn render_defs(&self, svg_defs: &mut String, multiplied_transform: DAffine2, bounds: [DVec2; 2], transformed_bounds: [DVec2; 2]) -> u64 {
|
||||
let bound_transform = DAffine2::from_scale_angle_translation(bounds[1] - bounds[0], 0., bounds[0]);
|
||||
let transformed_bound_transform = DAffine2::from_scale_angle_translation(transformed_bounds[1] - transformed_bounds[0], 0., transformed_bounds[0]);
|
||||
let updated_transform = multiplied_transform * bound_transform;
|
||||
|
@ -78,12 +90,13 @@ impl Gradient {
|
|||
.map(|(i, entry)| entry.to_string() + if i == 5 { "" } else { "," })
|
||||
.collect::<String>();
|
||||
|
||||
let gradient_id = crate::uuid::generate_uuid();
|
||||
match self.gradient_type {
|
||||
GradientType::Linear => {
|
||||
let _ = write!(
|
||||
svg_defs,
|
||||
r#"<linearGradient id="{}" x1="{}" x2="{}" y1="{}" y2="{}" gradientTransform="matrix({})">{}</linearGradient>"#,
|
||||
self.uuid, start.x, end.x, start.y, end.y, transform, positions
|
||||
gradient_id, start.x, end.x, start.y, end.y, transform, positions
|
||||
);
|
||||
}
|
||||
GradientType::Radial => {
|
||||
|
@ -91,10 +104,12 @@ impl Gradient {
|
|||
let _ = write!(
|
||||
svg_defs,
|
||||
r#"<radialGradient id="{}" cx="{}" cy="{}" r="{}" gradientTransform="matrix({})">{}</radialGradient>"#,
|
||||
self.uuid, start.x, start.y, radius, transform, positions
|
||||
gradient_id, start.x, start.y, radius, transform, positions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
gradient_id
|
||||
}
|
||||
|
||||
/// Insert a stop into the gradient, the index if successful
|
||||
|
@ -137,26 +152,11 @@ impl Gradient {
|
|||
}
|
||||
}
|
||||
|
||||
impl Clone for Gradient {
|
||||
/// Clones the gradient, with the cloned gradient having the new uuid.
|
||||
/// If multiple gradients have the same id then only one gradient will be shown in the final svg output.
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
start: self.start,
|
||||
end: self.end,
|
||||
transform: self.transform,
|
||||
positions: self.positions.clone(),
|
||||
uuid: crate::uuid::generate_uuid(),
|
||||
gradient_type: self.gradient_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the fill of a layer.
|
||||
///
|
||||
/// Can be None, a solid [Color], a linear [Gradient], a radial [Gradient] or potentially some sort of image or pattern in the future
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, DynAny, Hash, specta::Type)]
|
||||
pub enum Fill {
|
||||
#[default]
|
||||
None,
|
||||
|
@ -186,8 +186,8 @@ impl Fill {
|
|||
Self::None => r#" fill="none""#.to_string(),
|
||||
Self::Solid(color) => format!(r##" fill="#{}"{}"##, color.rgb_hex(), format_opacity("fill", color.a())),
|
||||
Self::Gradient(gradient) => {
|
||||
gradient.render_defs(svg_defs, multiplied_transform, bounds, transformed_bounds);
|
||||
format!(r##" fill="url('#{}')""##, gradient.uuid)
|
||||
let gradient_id = gradient.render_defs(svg_defs, multiplied_transform, bounds, transformed_bounds);
|
||||
format!(r##" fill="url('#{}')""##, gradient_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,9 +207,18 @@ impl Fill {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enum describing the type of [Fill]
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, DynAny, Hash, specta::Type)]
|
||||
pub enum FillType {
|
||||
None,
|
||||
Solid,
|
||||
Gradient,
|
||||
}
|
||||
|
||||
/// The stroke (outline) style of an SVG element.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, DynAny, specta::Type)]
|
||||
pub enum LineCap {
|
||||
Butt,
|
||||
Round,
|
||||
|
@ -227,7 +236,7 @@ impl Display for LineCap {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, DynAny, specta::Type)]
|
||||
pub enum LineJoin {
|
||||
Miter,
|
||||
Bevel,
|
||||
|
@ -245,25 +254,42 @@ impl Display for LineJoin {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, DynAny, specta::Type)]
|
||||
pub struct Stroke {
|
||||
/// Stroke color
|
||||
color: Option<Color>,
|
||||
pub color: Option<Color>,
|
||||
/// Line thickness
|
||||
weight: f64,
|
||||
dash_lengths: Vec<f32>,
|
||||
dash_offset: f64,
|
||||
line_cap: LineCap,
|
||||
line_join: LineJoin,
|
||||
line_join_miter_limit: f64,
|
||||
pub weight: f64,
|
||||
pub dash_lengths: Vec<f32>,
|
||||
pub dash_offset: f64,
|
||||
pub line_cap: LineCap,
|
||||
pub line_join: LineJoin,
|
||||
pub line_join_miter_limit: f64,
|
||||
}
|
||||
|
||||
impl core::hash::Hash for Stroke {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.color.hash(state);
|
||||
self.weight.to_bits().hash(state);
|
||||
self.dash_lengths.len().hash(state);
|
||||
self.dash_lengths.iter().for_each(|length| length.to_bits().hash(state));
|
||||
self.dash_offset.to_bits().hash(state);
|
||||
self.line_cap.hash(state);
|
||||
self.line_join.hash(state);
|
||||
self.line_join_miter_limit.to_bits().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Stroke {
|
||||
pub fn new(color: Color, weight: f64) -> Self {
|
||||
pub const fn new(color: Color, weight: f64) -> Self {
|
||||
Self {
|
||||
color: Some(color),
|
||||
weight,
|
||||
..Default::default()
|
||||
dash_lengths: Vec::new(),
|
||||
dash_offset: 0.,
|
||||
line_cap: LineCap::Butt,
|
||||
line_join: LineJoin::Miter,
|
||||
line_join_miter_limit: 4.,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,7 +304,11 @@ impl Stroke {
|
|||
}
|
||||
|
||||
pub fn dash_lengths(&self) -> String {
|
||||
self.dash_lengths.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", ")
|
||||
if self.dash_lengths.is_empty() {
|
||||
"none".to_string()
|
||||
} else {
|
||||
self.dash_lengths.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(", ")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dash_offset(&self) -> f64 {
|
||||
|
@ -367,7 +397,7 @@ impl Default for Stroke {
|
|||
Self {
|
||||
weight: 0.,
|
||||
color: Some(Color::from_rgba8(0, 0, 0, 255)),
|
||||
dash_lengths: vec![0.],
|
||||
dash_lengths: Vec::new(),
|
||||
dash_offset: 0.,
|
||||
line_cap: LineCap::Butt,
|
||||
line_join: LineJoin::Miter,
|
||||
|
@ -377,14 +407,14 @@ impl Default for Stroke {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, specta::Type)]
|
||||
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, DynAny, Hash, specta::Type)]
|
||||
pub struct PathStyle {
|
||||
stroke: Option<Stroke>,
|
||||
fill: Fill,
|
||||
}
|
||||
|
||||
impl PathStyle {
|
||||
pub fn new(stroke: Option<Stroke>, fill: Fill) -> Self {
|
||||
pub const fn new(stroke: Option<Stroke>, fill: Fill) -> Self {
|
||||
Self { stroke, fill }
|
||||
}
|
||||
|
||||
|
@ -508,7 +538,7 @@ impl PathStyle {
|
|||
}
|
||||
|
||||
/// Represents different ways of rendering an object
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, specta::Type)]
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Hash, DynAny, specta::Type)]
|
||||
pub enum ViewMode {
|
||||
/// Render with normal coloration at the current viewport resolution
|
||||
#[default]
|
||||
|
|
|
@ -1,12 +1,33 @@
|
|||
use glam::DAffine2;
|
||||
use super::style::{PathStyle, Stroke};
|
||||
use crate::{uuid::ManipulatorGroupId, Color};
|
||||
|
||||
use super::style::PathStyle;
|
||||
use crate::uuid::ManipulatorGroupId;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use glam::DAffine2;
|
||||
|
||||
/// [VectorData] is passed between nodes.
|
||||
/// It contains a list of subpaths (that may be open or closed), a transform and some style information.
|
||||
#[derive(Clone, Debug, PartialEq, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct VectorData {
|
||||
pub subpaths: Vec<bezier_rs::Subpath<ManipulatorGroupId>>,
|
||||
pub transform: DAffine2,
|
||||
pub style: PathStyle,
|
||||
}
|
||||
|
||||
impl VectorData {
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
subpaths: Vec::new(),
|
||||
transform: DAffine2::IDENTITY,
|
||||
style: PathStyle::new(Some(Stroke::new(Color::BLACK, 0.)), super::style::Fill::Solid(Color::BLACK)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_subpath(subpath: bezier_rs::Subpath<ManipulatorGroupId>) -> Self {
|
||||
super::VectorData {
|
||||
subpaths: vec![subpath],
|
||||
transform: DAffine2::IDENTITY,
|
||||
style: PathStyle::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
90
node-graph/gcore/src/vector/vector_nodes.rs
Normal file
90
node-graph/gcore/src/vector/vector_nodes.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use super::style::{Fill, FillType, Gradient, GradientType, Stroke};
|
||||
use super::VectorData;
|
||||
use crate::{Color, Node};
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TransformNode<Translation, Rotation, Scale, Shear> {
|
||||
translate: Translation,
|
||||
rotate: Rotation,
|
||||
scale: Scale,
|
||||
shear: Shear,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(TransformNode)]
|
||||
fn transform_vector_data(mut vector_data: VectorData, translate: DVec2, rotate: f64, scale: DVec2, shear: DVec2) -> VectorData {
|
||||
let (sin, cos) = rotate.sin_cos();
|
||||
|
||||
vector_data.transform = vector_data.transform * DAffine2::from_cols_array(&[scale.x + cos, shear.y + sin, shear.x - sin, scale.y + cos, translate.x, translate.y]);
|
||||
vector_data
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SetFillNode<FillType, SolidColor, GradientType, Start, End, Transform, Positions> {
|
||||
fill_type: FillType,
|
||||
solid_color: SolidColor,
|
||||
gradient_type: GradientType,
|
||||
start: Start,
|
||||
end: End,
|
||||
transform: Transform,
|
||||
positions: Positions,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(SetFillNode)]
|
||||
fn set_vector_data_fill(
|
||||
mut vector_data: VectorData,
|
||||
fill_type: FillType,
|
||||
solid_color: Color,
|
||||
gradient_type: GradientType,
|
||||
start: DVec2,
|
||||
end: DVec2,
|
||||
transform: DAffine2,
|
||||
positions: Vec<(f64, Option<Color>)>,
|
||||
) -> VectorData {
|
||||
vector_data.style.set_fill(match fill_type {
|
||||
FillType::None => Fill::None,
|
||||
FillType::Solid => Fill::Solid(solid_color),
|
||||
FillType::Gradient => Fill::Gradient(Gradient {
|
||||
start,
|
||||
end,
|
||||
transform,
|
||||
positions,
|
||||
gradient_type,
|
||||
}),
|
||||
});
|
||||
vector_data
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SetStrokeNode<Color, Weight, DashLengths, DashOffset, LineCap, LineJoin, MiterLimit> {
|
||||
color: Color,
|
||||
weight: Weight,
|
||||
dash_lengths: DashLengths,
|
||||
dash_offset: DashOffset,
|
||||
line_cap: LineCap,
|
||||
line_join: LineJoin,
|
||||
miter_limit: MiterLimit,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(SetStrokeNode)]
|
||||
fn set_vector_data_stroke(
|
||||
mut vector_data: VectorData,
|
||||
color: crate::Color,
|
||||
weight: f64,
|
||||
dash_lengths: Vec<f32>,
|
||||
dash_offset: f64,
|
||||
line_cap: super::style::LineCap,
|
||||
line_join: super::style::LineJoin,
|
||||
miter_limit: f64,
|
||||
) -> VectorData {
|
||||
vector_data.style.set_stroke(Stroke {
|
||||
color: Some(color),
|
||||
weight,
|
||||
dash_lengths,
|
||||
dash_offset,
|
||||
line_cap,
|
||||
line_join,
|
||||
line_join_miter_limit: miter_limit,
|
||||
});
|
||||
vector_data
|
||||
}
|
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
|||
|
||||
[features]
|
||||
default = []
|
||||
serde = ["dep:serde", "graphene-core/serde", "glam/serde"]
|
||||
serde = ["dep:serde", "graphene-core/serde", "glam/serde", "bezier-rs/serde"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -19,6 +19,7 @@ log = "0.4"
|
|||
serde = { version = "1", features = ["derive", "rc"], optional = true }
|
||||
glam = { version = "0.22" }
|
||||
base64 = "0.13"
|
||||
bezier-rs = { path = "../../libraries/bezier-rs", features = ["dyn-any"] }
|
||||
specta.workspace = true
|
||||
|
||||
bytemuck = {version = "1.8" }
|
||||
|
|
|
@ -33,7 +33,7 @@ impl DocumentNodeMetadata {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, specta::Type)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct DocumentNode {
|
||||
pub name: String,
|
||||
|
@ -122,7 +122,7 @@ impl DocumentNode {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash, specta::Type)]
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum NodeInput {
|
||||
Node { node_id: NodeId, output_index: usize, lambda: bool },
|
||||
|
@ -165,7 +165,7 @@ impl NodeInput {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, specta::Type)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum DocumentNodeImplementation {
|
||||
Network(NodeNetwork),
|
||||
|
@ -202,7 +202,7 @@ impl NodeOutput {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, DynAny, specta::Type)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct NodeNetwork {
|
||||
pub inputs: Vec<NodeId>,
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::executor::Any;
|
|||
pub use crate::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateStatus};
|
||||
|
||||
/// A type that is known, allowing serialization (serde::Deserialize is not object safe)
|
||||
#[derive(Clone, Debug, PartialEq, specta::Type)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum TaggedValue {
|
||||
None,
|
||||
|
@ -27,17 +27,26 @@ pub enum TaggedValue {
|
|||
RcImage(Option<Arc<graphene_core::raster::Image>>),
|
||||
ImageFrame(graphene_core::raster::ImageFrame),
|
||||
Color(graphene_core::raster::color::Color),
|
||||
Subpath(graphene_core::vector::subpath::Subpath),
|
||||
RcSubpath(Arc<graphene_core::vector::subpath::Subpath>),
|
||||
Subpath(bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>),
|
||||
RcSubpath(Arc<bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>),
|
||||
BlendMode(BlendMode),
|
||||
LuminanceCalculation(LuminanceCalculation),
|
||||
ImaginateSamplingMethod(ImaginateSamplingMethod),
|
||||
ImaginateMaskStartingFill(ImaginateMaskStartingFill),
|
||||
ImaginateStatus(ImaginateStatus),
|
||||
LayerPath(Option<Vec<u64>>),
|
||||
VectorData(graphene_core::vector::VectorData),
|
||||
Fill(graphene_core::vector::style::Fill),
|
||||
Stroke(graphene_core::vector::style::Stroke),
|
||||
VecF32(Vec<f32>),
|
||||
LineCap(graphene_core::vector::style::LineCap),
|
||||
LineJoin(graphene_core::vector::style::LineJoin),
|
||||
FillType(graphene_core::vector::style::FillType),
|
||||
GradientType(graphene_core::vector::style::GradientType),
|
||||
GradientPositions(Vec<(f64, Option<graphene_core::Color>)>),
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||
impl Hash for TaggedValue {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
|
@ -120,10 +129,52 @@ impl Hash for TaggedValue {
|
|||
p.hash(state)
|
||||
}
|
||||
Self::ImageFrame(i) => {
|
||||
20.hash(state);
|
||||
21.hash(state);
|
||||
i.image.hash(state);
|
||||
i.transform.to_cols_array().iter().for_each(|x| x.to_bits().hash(state))
|
||||
}
|
||||
Self::VectorData(vector_data) => {
|
||||
22.hash(state);
|
||||
vector_data.subpaths.hash(state);
|
||||
vector_data.transform.to_cols_array().iter().for_each(|x| x.to_bits().hash(state));
|
||||
vector_data.style.hash(state);
|
||||
}
|
||||
Self::Fill(fill) => {
|
||||
23.hash(state);
|
||||
fill.hash(state);
|
||||
}
|
||||
Self::Stroke(stroke) => {
|
||||
24.hash(state);
|
||||
stroke.hash(state);
|
||||
}
|
||||
Self::VecF32(vec_f32) => {
|
||||
25.hash(state);
|
||||
vec_f32.iter().for_each(|val| val.to_bits().hash(state));
|
||||
}
|
||||
Self::LineCap(line_cap) => {
|
||||
26.hash(state);
|
||||
line_cap.hash(state);
|
||||
}
|
||||
Self::LineJoin(line_join) => {
|
||||
27.hash(state);
|
||||
line_join.hash(state);
|
||||
}
|
||||
Self::FillType(fill_type) => {
|
||||
28.hash(state);
|
||||
fill_type.hash(state);
|
||||
}
|
||||
Self::GradientType(gradient_type) => {
|
||||
29.hash(state);
|
||||
gradient_type.hash(state);
|
||||
}
|
||||
Self::GradientPositions(gradient_positions) => {
|
||||
30.hash(state);
|
||||
gradient_positions.len().hash(state);
|
||||
for (position, color) in gradient_positions {
|
||||
position.to_bits().hash(state);
|
||||
color.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +204,15 @@ impl<'a> TaggedValue {
|
|||
TaggedValue::ImaginateMaskStartingFill(x) => Box::new(x),
|
||||
TaggedValue::ImaginateStatus(x) => Box::new(x),
|
||||
TaggedValue::LayerPath(x) => Box::new(x),
|
||||
TaggedValue::VectorData(x) => Box::new(x),
|
||||
TaggedValue::Fill(x) => Box::new(x),
|
||||
TaggedValue::Stroke(x) => Box::new(x),
|
||||
TaggedValue::VecF32(x) => Box::new(x),
|
||||
TaggedValue::LineCap(x) => Box::new(x),
|
||||
TaggedValue::LineJoin(x) => Box::new(x),
|
||||
TaggedValue::FillType(x) => Box::new(x),
|
||||
TaggedValue::GradientType(x) => Box::new(x),
|
||||
TaggedValue::GradientPositions(x) => Box::new(x),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,8 +232,8 @@ impl<'a> TaggedValue {
|
|||
TaggedValue::RcImage(_) => concrete!(Option<Arc<graphene_core::raster::Image>>),
|
||||
TaggedValue::ImageFrame(_) => concrete!(graphene_core::raster::ImageFrame),
|
||||
TaggedValue::Color(_) => concrete!(graphene_core::raster::Color),
|
||||
TaggedValue::Subpath(_) => concrete!(graphene_core::vector::subpath::Subpath),
|
||||
TaggedValue::RcSubpath(_) => concrete!(Arc<graphene_core::vector::subpath::Subpath>),
|
||||
TaggedValue::Subpath(_) => concrete!(bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>),
|
||||
TaggedValue::RcSubpath(_) => concrete!(Arc<bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>),
|
||||
TaggedValue::BlendMode(_) => concrete!(BlendMode),
|
||||
TaggedValue::ImaginateSamplingMethod(_) => concrete!(ImaginateSamplingMethod),
|
||||
TaggedValue::ImaginateMaskStartingFill(_) => concrete!(ImaginateMaskStartingFill),
|
||||
|
@ -181,6 +241,15 @@ impl<'a> TaggedValue {
|
|||
TaggedValue::LayerPath(_) => concrete!(Option<Vec<u64>>),
|
||||
TaggedValue::DAffine2(_) => concrete!(DAffine2),
|
||||
TaggedValue::LuminanceCalculation(_) => concrete!(LuminanceCalculation),
|
||||
TaggedValue::VectorData(_) => concrete!(graphene_core::vector::VectorData),
|
||||
TaggedValue::Fill(_) => concrete!(graphene_core::vector::style::Fill),
|
||||
TaggedValue::Stroke(_) => concrete!(graphene_core::vector::style::Stroke),
|
||||
TaggedValue::VecF32(_) => concrete!(Vec<f32>),
|
||||
TaggedValue::LineCap(_) => concrete!(graphene_core::vector::style::LineCap),
|
||||
TaggedValue::LineJoin(_) => concrete!(graphene_core::vector::style::LineJoin),
|
||||
TaggedValue::FillType(_) => concrete!(graphene_core::vector::style::FillType),
|
||||
TaggedValue::GradientType(_) => concrete!(graphene_core::vector::style::GradientType),
|
||||
TaggedValue::GradientPositions(_) => concrete!(Vec<(f64, Option<graphene_core::Color>)>),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ pub enum ImaginateStatus {
|
|||
Terminated,
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)]
|
||||
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||
impl core::hash::Hash for ImaginateStatus {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use glam::{DAffine2, DVec2};
|
||||
use graph_craft::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateStatus};
|
||||
use graphene_core::ops::{CloneNode, IdNode, TypeNode};
|
||||
use graphene_core::vector::VectorData;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -294,6 +295,15 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||
],
|
||||
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image, params: [&str]),
|
||||
register_node!(graphene_std::raster::ImageFrameNode<_>, input: Image, params: [DAffine2]),
|
||||
register_node!(graphene_core::vector::TransformNode<_, _, _, _>, input: VectorData, params: [DVec2, f64, DVec2, DVec2]),
|
||||
register_node!(graphene_core::vector::SetFillNode<_, _, _, _, _, _, _>, input: VectorData, params: [ graphene_core::vector::style::FillType, graphene_core::Color, graphene_core::vector::style::GradientType, DVec2, DVec2, DAffine2, Vec<(f64, Option<graphene_core::Color>)>]),
|
||||
register_node!(graphene_core::vector::SetStrokeNode<_, _, _, _, _, _, _>, input: VectorData, params: [graphene_core::Color, f64, Vec<f32>, f64, graphene_core::vector::style::LineCap, graphene_core::vector::style::LineJoin, f64]),
|
||||
register_node!(graphene_core::vector::generator_nodes::UnitCircleGenerator, input: (), params: []),
|
||||
register_node!(
|
||||
graphene_core::vector::generator_nodes::PathGenerator,
|
||||
input: graphene_core::vector::bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>,
|
||||
params: []
|
||||
),
|
||||
/*
|
||||
(NodeIdentifier::new("graphene_std::raster::ImageNode", &[concrete!("&str")]), |_proto_node, stack| {
|
||||
stack.push_fn(|_nodes| {
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
|
||||
// Extract primary input as first argument
|
||||
let primary_input = function_inputs.next().expect("Primary input required - set to `()` if not needed.");
|
||||
let Pat::Ident(PatIdent{ident: primary_input_ident,..} ) =&*primary_input.pat else {
|
||||
let Pat::Ident(PatIdent{ident: primary_input_ident, mutability: primary_input_mutability,..} ) =&*primary_input.pat else {
|
||||
panic!("Expected ident as primary input.");
|
||||
};
|
||||
let primary_input_ty = &primary_input.ty;
|
||||
|
@ -51,13 +51,15 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
|
||||
// Extract secondary inputs as all other arguments
|
||||
let parameter_inputs = function_inputs.collect::<Vec<_>>();
|
||||
let parameter_idents = parameter_inputs
|
||||
let parameter_pat_ident_patterns = parameter_inputs
|
||||
.iter()
|
||||
.map(|input| {
|
||||
let Pat::Ident(PatIdent { ident: primary_input_ident,.. }) = &*input.pat else { panic!("Expected ident for secondary input."); };
|
||||
primary_input_ident
|
||||
let Pat::Ident(pat_ident) = &*input.pat else { panic!("Expected ident for secondary input."); };
|
||||
pat_ident
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let parameter_idents = parameter_pat_ident_patterns.iter().map(|pat_ident| &pat_ident.ident).collect::<Vec<_>>();
|
||||
let parameter_mutability = parameter_pat_ident_patterns.iter().map(|pat_ident| &pat_ident.mutability);
|
||||
|
||||
// Extract the output type of the entire node - `()` by default
|
||||
let output = if let ReturnType::Type(_, ty) = &function.sig.output {
|
||||
|
@ -129,9 +131,9 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
{
|
||||
type Output = #output;
|
||||
#[inline]
|
||||
fn eval<'node: 'input>(&'node self, #primary_input_ident: #primary_input_ty) -> Self::Output {
|
||||
fn eval<'node: 'input>(&'node self, #primary_input_mutability #primary_input_ident: #primary_input_ty) -> Self::Output {
|
||||
#(
|
||||
let #parameter_idents = self.#parameter_idents.eval(());
|
||||
let #parameter_mutability #parameter_idents = self.#parameter_idents.eval(());
|
||||
)*
|
||||
|
||||
#body
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue