mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
Add Layer and Artboard node definitions and underlying data structures (#1204)
* Add basic node defenitions * Code review * change widget code * Artboard node changes --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
7e1b452757
commit
4e0c673a35
12 changed files with 295 additions and 36 deletions
|
@ -34,6 +34,10 @@ pub enum FrontendGraphDataType {
|
|||
Boolean,
|
||||
#[serde(rename = "vec2")]
|
||||
Vector,
|
||||
#[serde(rename = "graphic")]
|
||||
GraphicGroup,
|
||||
#[serde(rename = "artboard")]
|
||||
Artboard,
|
||||
}
|
||||
impl FrontendGraphDataType {
|
||||
pub const fn with_tagged_value(value: &TaggedValue) -> Self {
|
||||
|
@ -46,6 +50,8 @@ impl FrontendGraphDataType {
|
|||
TaggedValue::ImageFrame(_) => Self::Raster,
|
||||
TaggedValue::Color(_) => Self::Color,
|
||||
TaggedValue::RcSubpath(_) | TaggedValue::Subpaths(_) | TaggedValue::VectorData(_) => Self::Subpath,
|
||||
TaggedValue::GraphicGroup(_) => Self::GraphicGroup,
|
||||
TaggedValue::Artboard(_) => Self::Artboard,
|
||||
_ => Self::General,
|
||||
}
|
||||
}
|
||||
|
@ -750,6 +756,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
|||
warn!("No network");
|
||||
return;
|
||||
};
|
||||
debug_assert!(network.is_acyclic(), "Not acyclic. Network: {network:#?}");
|
||||
let outwards_links = network.collect_outwards_links();
|
||||
let required_shift = |left: NodeId, right: NodeId, network: &NodeNetwork| {
|
||||
if let (Some(left), Some(right)) = (network.nodes.get(&left), network.nodes.get(&right)) {
|
||||
|
|
|
@ -118,6 +118,34 @@ fn static_nodes() -> Vec<DocumentNodeType> {
|
|||
outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::General)],
|
||||
properties: |_document_node, _node_id, _context| node_properties::string_properties("The Monitor node stores the value of its last evaluation"),
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Layer",
|
||||
category: "General",
|
||||
identifier: NodeImplementation::proto("graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::value("Vector Data", TaggedValue::VectorData(graphene_core::vector::VectorData::empty()), true),
|
||||
DocumentInputType::value("Name", TaggedValue::String(String::new()), false),
|
||||
DocumentInputType::value("Blend Mode", TaggedValue::BlendMode(BlendMode::Normal), false),
|
||||
DocumentInputType::value("Opacity", TaggedValue::F32(100.), false),
|
||||
DocumentInputType::value("Visible", TaggedValue::Bool(true), false),
|
||||
DocumentInputType::value("Locked", TaggedValue::Bool(false), false),
|
||||
DocumentInputType::value("Collapsed", TaggedValue::Bool(false), false),
|
||||
DocumentInputType::value("Stack", TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::GraphicGroup)],
|
||||
properties: node_properties::layer_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Artboard",
|
||||
category: "General",
|
||||
identifier: NodeImplementation::proto("graphene_core::ConstructArtboardNode<_>"),
|
||||
inputs: vec![
|
||||
DocumentInputType::value("Graphic Group", TaggedValue::GraphicGroup(GraphicGroup::EMPTY), true),
|
||||
DocumentInputType::value("Bounds", TaggedValue::Optional2IVec2(None), false),
|
||||
],
|
||||
outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::Artboard)],
|
||||
properties: node_properties::artboard_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Downres",
|
||||
category: "Ignore",
|
||||
|
|
|
@ -75,7 +75,6 @@ fn start_widgets(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
widgets
|
||||
}
|
||||
|
||||
#[cfg(feature = "gpu")]
|
||||
fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
|
||||
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Text, blank_assist);
|
||||
|
||||
|
@ -203,7 +202,7 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
WidgetHolder::unrelated_separator(),
|
||||
number_props
|
||||
.value(Some(x))
|
||||
.on_update(update_value(|x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index))
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index))
|
||||
.widget_holder(),
|
||||
])
|
||||
} else if let NodeInput::Value {
|
||||
|
@ -215,7 +214,19 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
WidgetHolder::unrelated_separator(),
|
||||
number_props
|
||||
.value(Some(x as f64))
|
||||
.on_update(update_value(|x: &NumberInput| TaggedValue::U32(x.value.unwrap() as u32), node_id, index))
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::U32((x.value.unwrap()) as u32), node_id, index))
|
||||
.widget_holder(),
|
||||
])
|
||||
} else if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F32(x),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
WidgetHolder::unrelated_separator(),
|
||||
number_props
|
||||
.value(Some(x as f64))
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::F32((x.value.unwrap()) as f32), node_id, index))
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
|
@ -788,14 +799,8 @@ pub fn quantize_properties(document_node: &DocumentNode, node_id: NodeId, _conte
|
|||
pub fn exposure_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let exposure = number_widget(document_node, node_id, 1, "Exposure", NumberInput::default().min(-20.).max(20.), true);
|
||||
let offset = number_widget(document_node, node_id, 2, "Offset", NumberInput::default().min(-0.5).max(0.5), true);
|
||||
let gamma_correction = number_widget(
|
||||
document_node,
|
||||
node_id,
|
||||
3,
|
||||
"Gamma Correction",
|
||||
NumberInput::default().min(0.01).max(9.99).mode_increment().increment_step(0.1),
|
||||
true,
|
||||
);
|
||||
let gamma_input = NumberInput::default().min(0.01).max(9.99).mode_increment().increment_step(0.1);
|
||||
let gamma_correction = number_widget(document_node, node_id, 3, "Gamma Correction", gamma_input, true);
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: exposure },
|
||||
|
@ -1434,7 +1439,8 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
};
|
||||
|
||||
let blur_radius = {
|
||||
let widgets = number_widget(document_node, node_id, mask_blur_index, "Mask Blur", NumberInput::default().unit(" px").min(0.).max(25.).int(), true);
|
||||
let number_props = NumberInput::default().unit(" px").min(0.).max(25.).int();
|
||||
let widgets = number_widget(document_node, node_id, mask_blur_index, "Mask Blur", number_props, true);
|
||||
LayoutGroup::Row { widgets }.with_tooltip("Blur radius for the mask. Useful for softening sharp edges to blend the masked area with the rest of the image.")
|
||||
};
|
||||
|
||||
|
@ -1590,3 +1596,25 @@ pub fn fill_properties(document_node: &DocumentNode, node_id: NodeId, _context:
|
|||
|
||||
widgets
|
||||
}
|
||||
|
||||
pub fn layer_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let name = text_widget(document_node, node_id, 1, "Name", true);
|
||||
let blend_mode = blend_mode(document_node, node_id, 2, "Blend Mode", true);
|
||||
let opacity = number_widget(document_node, node_id, 3, "Opacity", NumberInput::default().min(0.).max(100.).unit("%"), true);
|
||||
let visible = bool_widget(document_node, node_id, 4, "Visible", true);
|
||||
let locked = bool_widget(document_node, node_id, 5, "Locked", true);
|
||||
let collapsed = bool_widget(document_node, node_id, 6, "Collapsed", true);
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: name },
|
||||
blend_mode,
|
||||
LayoutGroup::Row { widgets: opacity },
|
||||
LayoutGroup::Row { widgets: visible },
|
||||
LayoutGroup::Row { widgets: locked },
|
||||
LayoutGroup::Row { widgets: collapsed },
|
||||
]
|
||||
}
|
||||
pub fn artboard_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||
let label = text_widget(document_node, node_id, 1, "Label", true);
|
||||
vec![LayoutGroup::Row { widgets: label }]
|
||||
}
|
||||
|
|
|
@ -286,7 +286,7 @@ impl NodeGraphExecutor {
|
|||
self.last_output_type.insert(layer_path.clone(), Some(concrete!(VectorData)));
|
||||
responses.add(Operation::SetLayerTransform { path: layer_path.clone(), transform });
|
||||
responses.add(Operation::SetVectorData { path: layer_path, vector_data });
|
||||
} else {
|
||||
} else if core::any::TypeId::of::<ImageFrame<Color>>() == DynAny::type_id(boxed_node_graph_output.as_ref()) {
|
||||
// Attempt to downcast to an image frame
|
||||
let ImageFrame { image, transform } = dyn_any::downcast(boxed_node_graph_output).map(|image_frame| *image_frame)?;
|
||||
self.last_output_type.insert(layer_path.clone(), Some(concrete!(ImageFrame<Color>)));
|
||||
|
@ -316,6 +316,14 @@ impl NodeGraphExecutor {
|
|||
}];
|
||||
responses.add(FrontendMessage::UpdateImageData { document_id, image_data });
|
||||
}
|
||||
} else if core::any::TypeId::of::<graphene_core::Artboard>() == DynAny::type_id(boxed_node_graph_output.as_ref()) {
|
||||
let artboard: graphene_core::Artboard = dyn_any::downcast(boxed_node_graph_output).map(|artboard| *artboard)?;
|
||||
info!("{artboard:#?}");
|
||||
return Err(format!("Artboard (see console)"));
|
||||
} else if core::any::TypeId::of::<graphene_core::GraphicGroup>() == DynAny::type_id(boxed_node_graph_output.as_ref()) {
|
||||
let graphic_group: graphene_core::GraphicGroup = dyn_any::downcast(boxed_node_graph_output).map(|graphic| *graphic)?;
|
||||
info!("{graphic_group:#?}");
|
||||
return Err(format!("Graphic group (see console)"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -118,6 +118,10 @@
|
|||
--color-data-vec2-dim: #71008d;
|
||||
--color-data-color: #70a898;
|
||||
--color-data-color-dim: #43645b;
|
||||
--color-data-graphic: #e4bb72;
|
||||
--color-data-graphic-dim: #8b7752;
|
||||
--color-data-artboard: #70a898;
|
||||
--color-data-artboard-dim: #3a6156;
|
||||
|
||||
--color-none: white;
|
||||
--color-none-repeat: no-repeat;
|
||||
|
|
|
@ -74,7 +74,7 @@ export class FrontendDocumentDetails extends DocumentDetails {
|
|||
readonly id!: bigint;
|
||||
}
|
||||
|
||||
export type FrontendGraphDataType = "general" | "raster" | "color" | "vector" | "number";
|
||||
export type FrontendGraphDataType = "general" | "raster" | "color" | "vector" | "vec2" | "graphic" | "artboard";
|
||||
|
||||
export class NodeGraphInput {
|
||||
readonly dataType!: FrontendGraphDataType;
|
||||
|
|
140
node-graph/gcore/src/graphic_element.rs
Normal file
140
node-graph/gcore/src/graphic_element.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
use crate::raster::{BlendMode, ImageFrame};
|
||||
use crate::vector::VectorData;
|
||||
use crate::{Color, Node};
|
||||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use glam::IVec2;
|
||||
use node_macro::node_fn;
|
||||
|
||||
/// A list of [`GraphicElement`]s
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct GraphicGroup(Vec<GraphicElement>);
|
||||
|
||||
/// Internal data for a [`GraphicElement`]. Can be [`VectorData`], [`ImageFrame`], text, or a nested [`GraphicGroup`]
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum GraphicElementData {
|
||||
VectorShape(Box<VectorData>),
|
||||
ImageFrame(ImageFrame<Color>),
|
||||
Text(String),
|
||||
GraphicGroup(GraphicGroup),
|
||||
Artboard(Artboard),
|
||||
}
|
||||
|
||||
/// A named [`GraphicElementData`] with a blend mode, opacity, as well as visibility, locked, and collapsed states.
|
||||
#[derive(Clone, Debug, PartialEq, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct GraphicElement {
|
||||
pub name: String,
|
||||
pub blend_mode: BlendMode,
|
||||
/// In range 0..=1
|
||||
pub opacity: f32,
|
||||
pub visible: bool,
|
||||
pub locked: bool,
|
||||
pub collapsed: bool,
|
||||
pub graphic_element_data: GraphicElementData,
|
||||
}
|
||||
|
||||
/// Some [`ArtboardData`] with some optional clipping bounds that can be exported.
|
||||
/// Similar to an Inkscape page: https://media.inkscape.org/media/doc/release_notes/1.2/Inkscape_1.2.html#Page_tool
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Artboard {
|
||||
pub graphic_group: GraphicGroup,
|
||||
pub bounds: Option<[IVec2; 2]>,
|
||||
}
|
||||
|
||||
pub struct ConstructLayerNode<Name, BlendMode, Opacity, Visible, Locked, Collapsed, Stack> {
|
||||
name: Name,
|
||||
blend_mode: BlendMode,
|
||||
opacity: Opacity,
|
||||
visible: Visible,
|
||||
locked: Locked,
|
||||
collapsed: Collapsed,
|
||||
stack: Stack,
|
||||
}
|
||||
|
||||
#[node_fn(ConstructLayerNode)]
|
||||
fn construct_layer<Data: Into<GraphicElementData>>(
|
||||
graphic_element_data: Data,
|
||||
name: String,
|
||||
blend_mode: BlendMode,
|
||||
opacity: f32,
|
||||
visible: bool,
|
||||
locked: bool,
|
||||
collapsed: bool,
|
||||
mut stack: GraphicGroup,
|
||||
) -> GraphicGroup {
|
||||
stack.push(GraphicElement {
|
||||
name,
|
||||
blend_mode,
|
||||
opacity: opacity / 100.,
|
||||
visible,
|
||||
locked,
|
||||
collapsed,
|
||||
graphic_element_data: graphic_element_data.into(),
|
||||
});
|
||||
stack
|
||||
}
|
||||
|
||||
pub struct ConstructArtboardNode<Bounds> {
|
||||
bounds: Bounds,
|
||||
}
|
||||
|
||||
#[node_fn(ConstructArtboardNode)]
|
||||
fn construct_artboard(graphic_group: GraphicGroup, bounds: Option<[IVec2; 2]>) -> Artboard {
|
||||
Artboard { graphic_group, bounds }
|
||||
}
|
||||
|
||||
impl From<ImageFrame<Color>> for GraphicElementData {
|
||||
fn from(image_frame: ImageFrame<Color>) -> Self {
|
||||
GraphicElementData::ImageFrame(image_frame)
|
||||
}
|
||||
}
|
||||
impl From<VectorData> for GraphicElementData {
|
||||
fn from(vector_data: VectorData) -> Self {
|
||||
GraphicElementData::VectorShape(Box::new(vector_data))
|
||||
}
|
||||
}
|
||||
impl From<GraphicGroup> for GraphicElementData {
|
||||
fn from(graphic_group: GraphicGroup) -> Self {
|
||||
GraphicElementData::GraphicGroup(graphic_group)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Artboard> for GraphicElementData {
|
||||
fn from(artboard: Artboard) -> Self {
|
||||
GraphicElementData::Artboard(artboard)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for GraphicGroup {
|
||||
type Target = Vec<GraphicElement>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl DerefMut for GraphicGroup {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphicGroup {
|
||||
pub const EMPTY: Self = Self(Vec::new());
|
||||
}
|
||||
|
||||
impl core::hash::Hash for GraphicElement {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
self.blend_mode.hash(state);
|
||||
self.opacity.to_bits().hash(state);
|
||||
self.visible.hash(state);
|
||||
self.locked.hash(state);
|
||||
self.collapsed.hash(state);
|
||||
self.graphic_element_data.hash(state);
|
||||
}
|
||||
}
|
|
@ -26,6 +26,10 @@ pub mod raster;
|
|||
#[cfg(feature = "alloc")]
|
||||
pub mod transform;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
mod graphic_element;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use graphic_element::*;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod vector;
|
||||
|
||||
|
|
|
@ -17,6 +17,15 @@ pub struct VectorData {
|
|||
pub mirror_angle: Vec<ManipulatorGroupId>,
|
||||
}
|
||||
|
||||
impl core::hash::Hash for VectorData {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.subpaths.hash(state);
|
||||
self.transform.to_cols_array().iter().for_each(|x| x.to_bits().hash(state));
|
||||
self.style.hash(state);
|
||||
self.mirror_angle.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl VectorData {
|
||||
/// An empty subpath with no data, an identity transform, and a black fill.
|
||||
pub const fn empty() -> Self {
|
||||
|
|
|
@ -505,6 +505,30 @@ impl NodeNetwork {
|
|||
network: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_acyclic(&self) -> bool {
|
||||
let mut dependencies: HashMap<u64, Vec<u64>> = HashMap::new();
|
||||
for (node_id, node) in &self.nodes {
|
||||
dependencies.insert(
|
||||
*node_id,
|
||||
node.inputs
|
||||
.iter()
|
||||
.filter_map(|input| if let NodeInput::Node { node_id: ref_id, .. } = input { Some(*ref_id) } else { None })
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
while !dependencies.is_empty() {
|
||||
let Some((&disconnected, _)) = dependencies.iter().find(|(_, l)| l.is_empty()) else {
|
||||
error!("Dependencies {dependencies:?}");
|
||||
return false
|
||||
};
|
||||
dependencies.remove(&disconnected);
|
||||
for connections in dependencies.values_mut() {
|
||||
connections.retain(|&id| id != disconnected);
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Functions for compiling the network
|
||||
|
@ -762,19 +786,16 @@ impl NodeNetwork {
|
|||
self.nodes.retain(|_, node| !matches!(node.implementation, DocumentNodeImplementation::Extract));
|
||||
|
||||
for (_, node) in &mut extraction_nodes {
|
||||
match node.implementation {
|
||||
DocumentNodeImplementation::Extract => {
|
||||
assert_eq!(node.inputs.len(), 1);
|
||||
let NodeInput::Node { node_id, output_index, lambda } = node.inputs.pop().unwrap() else {
|
||||
panic!("Extract node has no input");
|
||||
};
|
||||
assert_eq!(output_index, 0);
|
||||
assert!(lambda);
|
||||
let input_node = self.nodes.get_mut(&node_id).unwrap();
|
||||
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into());
|
||||
node.inputs = vec![NodeInput::value(TaggedValue::DocumentNode(input_node.clone()), false)];
|
||||
}
|
||||
_ => (),
|
||||
if let DocumentNodeImplementation::Extract = node.implementation {
|
||||
assert_eq!(node.inputs.len(), 1);
|
||||
let NodeInput::Node { node_id, output_index, lambda } = node.inputs.pop().unwrap() else {
|
||||
panic!("Extract node has no input");
|
||||
};
|
||||
assert_eq!(output_index, 0);
|
||||
assert!(lambda);
|
||||
let input_node = self.nodes.get_mut(&node_id).unwrap();
|
||||
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into());
|
||||
node.inputs = vec![NodeInput::value(TaggedValue::DocumentNode(input_node.clone()), false)];
|
||||
}
|
||||
}
|
||||
self.nodes.extend(extraction_nodes);
|
||||
|
|
|
@ -57,6 +57,9 @@ pub enum TaggedValue {
|
|||
Segments(Vec<graphene_core::raster::ImageFrame<Color>>),
|
||||
EditorApi(graphene_core::EditorApi<'static>),
|
||||
DocumentNode(DocumentNode),
|
||||
GraphicGroup(graphene_core::GraphicGroup),
|
||||
Artboard(graphene_core::Artboard),
|
||||
Optional2IVec2(Option<[glam::IVec2; 2]>),
|
||||
}
|
||||
|
||||
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||
|
@ -88,15 +91,8 @@ impl Hash for TaggedValue {
|
|||
Self::ImaginateMaskStartingFill(f) => f.hash(state),
|
||||
Self::ImaginateStatus(s) => s.hash(state),
|
||||
Self::LayerPath(p) => p.hash(state),
|
||||
Self::ImageFrame(i) => {
|
||||
i.image.hash(state);
|
||||
i.transform.to_cols_array().iter().for_each(|x| x.to_bits().hash(state))
|
||||
}
|
||||
Self::VectorData(vector_data) => {
|
||||
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::ImageFrame(i) => i.hash(state),
|
||||
Self::VectorData(vector_data) => vector_data.hash(state),
|
||||
Self::Fill(fill) => fill.hash(state),
|
||||
Self::Stroke(stroke) => stroke.hash(state),
|
||||
Self::VecF32(vec_f32) => vec_f32.iter().for_each(|val| val.to_bits().hash(state)),
|
||||
|
@ -131,6 +127,9 @@ impl Hash for TaggedValue {
|
|||
}
|
||||
Self::EditorApi(editor_api) => editor_api.hash(state),
|
||||
Self::DocumentNode(document_node) => document_node.hash(state),
|
||||
Self::GraphicGroup(graphic_group) => graphic_group.hash(state),
|
||||
Self::Artboard(artboard) => artboard.hash(state),
|
||||
Self::Optional2IVec2(v) => v.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,6 +179,9 @@ impl<'a> TaggedValue {
|
|||
TaggedValue::Segments(x) => Box::new(x),
|
||||
TaggedValue::EditorApi(x) => Box::new(x),
|
||||
TaggedValue::DocumentNode(x) => Box::new(x),
|
||||
TaggedValue::GraphicGroup(x) => Box::new(x),
|
||||
TaggedValue::Artboard(x) => Box::new(x),
|
||||
TaggedValue::Optional2IVec2(x) => Box::new(x),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,6 +242,9 @@ impl<'a> TaggedValue {
|
|||
TaggedValue::Segments(_) => concrete!(graphene_core::raster::IndexNode<Vec<graphene_core::raster::ImageFrame<Color>>>),
|
||||
TaggedValue::EditorApi(_) => concrete!(graphene_core::EditorApi),
|
||||
TaggedValue::DocumentNode(_) => concrete!(crate::document::DocumentNode),
|
||||
TaggedValue::GraphicGroup(_) => concrete!(graphene_core::GraphicGroup),
|
||||
TaggedValue::Artboard(_) => concrete!(graphene_core::Artboard),
|
||||
TaggedValue::Optional2IVec2(_) => concrete!(Option<[glam::IVec2; 2]>),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -532,6 +532,11 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
|
|||
register_node!(graphene_core::text::TextGenerator<_, _, _>, input: graphene_core::EditorApi, params: [String, graphene_core::text::Font, f64]),
|
||||
register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []),
|
||||
register_node!(graphene_core::ExtractImageFrame, input: graphene_core::EditorApi, params: []),
|
||||
register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::vector::VectorData, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]),
|
||||
register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: ImageFrame<Color>, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]),
|
||||
register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::GraphicGroup, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]),
|
||||
register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::Artboard, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]),
|
||||
register_node!(graphene_core::ConstructArtboardNode<_>, input: graphene_core::GraphicGroup, params: [Option<[glam::IVec2; 2]>]),
|
||||
];
|
||||
let mut map: HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
|
||||
for (id, c, types) in node_types.into_iter().flatten() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue