mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Add a basic API and rudimentary frontend for node graph layers (#846)
* Node graph API stub * Rename and fix SetInputValue * Get list of links from network * Test populating node graph UI * Node properties * Fix viewport bounds * Slightly change promise usage * A tiny bit of cleanup I did while reading code * Cleanup and work towards hooking up node links in Vue template * Add the brighten colour node * Run cargo fmt * Add to and from hsla * GrayscaleImage node with small perf improvement * Fix gutter panel resizing * Display node links from backend * Add support for connecting node links * Use existing message * Fix formatting error * Add a (currently crashing) brighten node * Replace brighten node with proto node implementation * Add support for connecting node links * Update watch dirs * Add hue shift node * Add create_node function to editor api * Basic insert node UI * Fix broken names * Add log * Fix positioning * Set connector index to 0 * Add properties for Heu shift / brighten * Allow deselecting nodes * Redesign Properties panel collapsible sections Co-authored-by: Keavon Chambers <keavon@keavon.com> Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
committed by
Keavon Chambers
parent
e8256dd350
commit
504136b61b
@@ -62,7 +62,12 @@ impl DocumentNode {
|
||||
NodeInput::Network => (ProtoNodeInput::Network, ConstructionArgs::Nodes(vec![])),
|
||||
};
|
||||
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network)), "recieved non resolved parameter");
|
||||
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Value(_))), "recieved value as parameter");
|
||||
assert!(
|
||||
!self.inputs.iter().any(|input| matches!(input, NodeInput::Value(_))),
|
||||
"recieved value as parameter. inupts: {:#?}, construction_args: {:#?}",
|
||||
&self.inputs,
|
||||
&args
|
||||
);
|
||||
|
||||
if let ConstructionArgs::Nodes(nodes) = &mut args {
|
||||
nodes.extend(self.inputs.iter().map(|input| match input {
|
||||
@@ -143,7 +148,10 @@ impl NodeNetwork {
|
||||
|
||||
/// Recursively dissolve non primitive document nodes and return a single flattened network of nodes.
|
||||
pub fn flatten_with_fns(&mut self, node: NodeId, map_ids: impl Fn(NodeId, NodeId) -> NodeId + Copy, gen_id: impl Fn() -> NodeId + Copy) {
|
||||
let (id, mut node) = self.nodes.remove_entry(&node).expect("The node which was supposed to be flattened does not exist in the network");
|
||||
let (id, mut node) = self
|
||||
.nodes
|
||||
.remove_entry(&node)
|
||||
.unwrap_or_else(|| panic!("The node which was supposed to be flattened does not exist in the network, id {} network {:#?}", node, self));
|
||||
|
||||
match node.implementation {
|
||||
DocumentNodeImplementation::Network(mut inner_network) => {
|
||||
|
||||
@@ -10,6 +10,7 @@ use dyn_any::{DynAny, Upcast};
|
||||
pub enum TaggedValue {
|
||||
String(String),
|
||||
U32(u32),
|
||||
F32(f32),
|
||||
//Image(graphene_std::raster::Image),
|
||||
Color(graphene_core::raster::color::Color),
|
||||
}
|
||||
@@ -19,6 +20,7 @@ impl TaggedValue {
|
||||
match self {
|
||||
TaggedValue::String(x) => Box::new(x),
|
||||
TaggedValue::U32(x) => Box::new(x),
|
||||
TaggedValue::F32(x) => Box::new(x),
|
||||
TaggedValue::Color(x) => Box::new(x),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod node_registry;
|
||||
|
||||
pub mod document;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use borrow_stack::FixedSizeStack;
|
||||
use graphene_core::generic::FnNode;
|
||||
use graphene_core::ops::AddNode;
|
||||
@@ -200,6 +202,44 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
||||
}
|
||||
})
|
||||
}),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::raster::BrightenColorNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
|proto_node, stack| {
|
||||
info!("proto node {:?}", proto_node);
|
||||
stack.push_fn(|nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Brighten Color Node constructed with out brightness input node") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), f32> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_core::raster::BrightenColorNode::new(input_node));
|
||||
|
||||
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
|
||||
let pre_node = nodes.get(pre_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
(
|
||||
NodeIdentifier::new("graphene_core::raster::HueShiftNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
|proto_node, stack| {
|
||||
info!("proto node {:?}", proto_node);
|
||||
stack.push_fn(|nodes| {
|
||||
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("Hue Shift Color Node constructed with out shift input node") };
|
||||
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
|
||||
let input_node: DowncastBothNode<_, (), f32> = DowncastBothNode::new(value_node);
|
||||
let node = DynAnyNode::new(graphene_core::raster::HueShiftNode::new(input_node));
|
||||
|
||||
if let ProtoNodeInput::Node(pre_id) = proto_node.input {
|
||||
let pre_node = nodes.get(pre_id as usize).unwrap();
|
||||
(pre_node).then(node).into_type_erased()
|
||||
} else {
|
||||
node.into_type_erased()
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
(NodeIdentifier::new("graphene_std::raster::MapImageNode", &[]), |proto_node, stack| {
|
||||
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
|
||||
stack.push_fn(move |nodes| {
|
||||
@@ -218,6 +258,45 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
|
||||
unimplemented!()
|
||||
}
|
||||
}),
|
||||
(NodeIdentifier::new("graph_craft::node_registry::GrayscaleImage", &[]), |proto_node, stack| {
|
||||
stack.push_fn(move |nodes| {
|
||||
let grayscale_node = DynAnyNode::new(FnNode::new(|mut image: Image| {
|
||||
for pixel in &mut image.data {
|
||||
let avg = (pixel.r() + pixel.g() + pixel.b()) / 3.;
|
||||
*pixel = Color::from_rgbaf32_unchecked(avg, avg, avg, pixel.a());
|
||||
}
|
||||
image
|
||||
}));
|
||||
|
||||
if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
(pre_node).then(grayscale_node).into_type_erased()
|
||||
} else {
|
||||
grayscale_node.into_type_erased()
|
||||
}
|
||||
})
|
||||
}),
|
||||
(NodeIdentifier::new("graph_craft::node_registry::HueShiftImage", &[]), |_proto_node, _stack| {
|
||||
todo!();
|
||||
// stack.push_fn(move |nodes| {
|
||||
|
||||
// let hue_shift_node = DynAnyNode::new(FnNode::new(|(mut image, amount): (Image, f32)| {
|
||||
// for pixel in &mut image.data {
|
||||
// let [mut hue, saturation, luminance, alpha] = pixel.to_hsla();
|
||||
// hue += amount;
|
||||
// *pixel = Color::from_hsla(hue, saturation, luminance, alpha);
|
||||
// }
|
||||
// image
|
||||
// }));
|
||||
|
||||
// if let ProtoNodeInput::Node(node_id) = proto_node.input {
|
||||
// let pre_node = nodes.get(node_id as usize).unwrap();
|
||||
// (pre_node).then(hue_shift_node).into_type_erased()
|
||||
// } else {
|
||||
// hue_shift_node.into_type_erased()
|
||||
// }
|
||||
// })
|
||||
}),
|
||||
(
|
||||
NodeIdentifier::new("graphene_std::raster::ImageNode", &[Concrete(std::borrow::Cow::Borrowed("&str"))]),
|
||||
|_proto_node, stack| {
|
||||
|
||||
@@ -121,14 +121,26 @@ impl ProtoNode {
|
||||
}
|
||||
|
||||
impl ProtoNetwork {
|
||||
fn check_ref(&self, ref_id: &NodeId, id: &NodeId) {
|
||||
assert!(
|
||||
self.nodes.iter().any(|(check_id, _)| check_id == ref_id),
|
||||
"Node id:{} has a reference which uses node id:{} which doesn't exist in network {:#?}",
|
||||
id,
|
||||
ref_id,
|
||||
self
|
||||
);
|
||||
}
|
||||
|
||||
pub fn collect_outwards_edges(&self) -> HashMap<NodeId, Vec<NodeId>> {
|
||||
let mut edges: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
|
||||
for (id, node) in &self.nodes {
|
||||
if let ProtoNodeInput::Node(ref_id) = &node.input {
|
||||
self.check_ref(ref_id, id);
|
||||
edges.entry(*ref_id).or_default().push(*id)
|
||||
}
|
||||
if let ConstructionArgs::Nodes(ref_nodes) = &node.construction_args {
|
||||
for ref_id in ref_nodes {
|
||||
self.check_ref(ref_id, id);
|
||||
edges.entry(*ref_id).or_default().push(*id)
|
||||
}
|
||||
}
|
||||
@@ -140,10 +152,12 @@ impl ProtoNetwork {
|
||||
let mut edges: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
|
||||
for (id, node) in &self.nodes {
|
||||
if let ProtoNodeInput::Node(ref_id) = &node.input {
|
||||
self.check_ref(ref_id, id);
|
||||
edges.entry(*id).or_default().push(*ref_id)
|
||||
}
|
||||
if let ConstructionArgs::Nodes(ref_nodes) = &node.construction_args {
|
||||
for ref_id in ref_nodes {
|
||||
self.check_ref(ref_id, id);
|
||||
edges.entry(*id).or_default().push(*ref_id)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user