mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
Update Imaginate to output bitmap data to the graph via Image Frame node (#1001)
* Multiple node outputs * Add new nodes * gcore use std by default to allow for testing * Allow multiple node outputs * Multiple outputs to frontend * Add ImageFrameNode to node registry * Minor cleanup * Basic transform implementation * Add some logging to image encoding * Fix ImageFrameNode * Add transform input to Imaginate node (#1014) * Add transform input to imaginate node * Force the resolution to be edited with no transform * Add transform to imaginate generation * Fix compilation --------- Co-authored-by: Keavon Chambers <keavon@keavon.com> * Add labels to node outputs * Fix seed; disable mask when transform is disconnected; add Imaginate tooltips * Rename 'Input Multiple' node to 'Input' * Code review * Replicate to Svelte * Show only the primary input chain in the Properties panel --------- Co-authored-by: Dennis Kobert <dennis@kobert.dev> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
a709a772d5
commit
1b0e1b9bdf
35 changed files with 1172 additions and 553 deletions
|
@ -38,6 +38,12 @@ pub struct DocumentNodeMetadata {
|
|||
pub position: IVec2,
|
||||
}
|
||||
|
||||
impl DocumentNodeMetadata {
|
||||
pub fn position(position: impl Into<IVec2>) -> Self {
|
||||
Self { position: position.into() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct DocumentNode {
|
||||
|
@ -48,7 +54,7 @@ pub struct DocumentNode {
|
|||
}
|
||||
|
||||
impl DocumentNode {
|
||||
pub fn populate_first_network_input(&mut self, node: NodeId, offset: usize) {
|
||||
pub fn populate_first_network_input(&mut self, node_id: NodeId, output_index: usize, offset: usize) {
|
||||
let input = self
|
||||
.inputs
|
||||
.iter()
|
||||
|
@ -58,7 +64,7 @@ impl DocumentNode {
|
|||
.expect("no network input");
|
||||
|
||||
let index = input.0;
|
||||
self.inputs[index] = NodeInput::Node(node);
|
||||
self.inputs[index] = NodeInput::Node { node_id, output_index };
|
||||
}
|
||||
|
||||
fn resolve_proto_node(mut self) -> ProtoNode {
|
||||
|
@ -70,7 +76,10 @@ impl DocumentNode {
|
|||
assert_eq!(self.inputs.len(), 0);
|
||||
(ProtoNodeInput::None, ConstructionArgs::Value(tagged_value))
|
||||
}
|
||||
NodeInput::Node(id) => (ProtoNodeInput::Node(id), ConstructionArgs::Nodes(vec![])),
|
||||
NodeInput::Node { node_id, output_index } => {
|
||||
assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode.");
|
||||
(ProtoNodeInput::Node(node_id), ConstructionArgs::Nodes(vec![]))
|
||||
}
|
||||
NodeInput::Network => (ProtoNodeInput::Network, ConstructionArgs::Nodes(vec![])),
|
||||
};
|
||||
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network)), "recieved non resolved parameter");
|
||||
|
@ -83,7 +92,7 @@ impl DocumentNode {
|
|||
|
||||
if let ConstructionArgs::Nodes(nodes) = &mut args {
|
||||
nodes.extend(self.inputs.iter().map(|input| match input {
|
||||
NodeInput::Node(id) => *id,
|
||||
NodeInput::Node { node_id, .. } => *node_id,
|
||||
_ => unreachable!(),
|
||||
}));
|
||||
}
|
||||
|
@ -105,11 +114,11 @@ impl DocumentNode {
|
|||
P: Fn(String, usize) -> Option<NodeInput>,
|
||||
{
|
||||
for (index, input) in self.inputs.iter_mut().enumerate() {
|
||||
let &mut NodeInput::Node(id) = input else {
|
||||
let &mut NodeInput::Node{node_id: id, output_index} = input else {
|
||||
continue;
|
||||
};
|
||||
if let Some(&new_id) = new_ids.get(&id) {
|
||||
*input = NodeInput::Node(new_id);
|
||||
*input = NodeInput::Node { node_id: new_id, output_index };
|
||||
} else if let Some(new_input) = default_input(self.name.clone(), index) {
|
||||
*input = new_input;
|
||||
} else {
|
||||
|
@ -123,23 +132,26 @@ impl DocumentNode {
|
|||
#[derive(Clone, Debug, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum NodeInput {
|
||||
Node(NodeId),
|
||||
Node { node_id: NodeId, output_index: usize },
|
||||
Value { tagged_value: value::TaggedValue, exposed: bool },
|
||||
Network,
|
||||
}
|
||||
|
||||
impl NodeInput {
|
||||
pub const fn node(node_id: NodeId, output_index: usize) -> Self {
|
||||
Self::Node { node_id, output_index }
|
||||
}
|
||||
pub const fn value(tagged_value: value::TaggedValue, exposed: bool) -> Self {
|
||||
Self::Value { tagged_value, exposed }
|
||||
}
|
||||
fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId) {
|
||||
if let NodeInput::Node(id) = self {
|
||||
*self = NodeInput::Node(f(*id))
|
||||
if let &mut NodeInput::Node { node_id, output_index } = self {
|
||||
*self = NodeInput::Node { node_id: f(node_id), output_index }
|
||||
}
|
||||
}
|
||||
pub fn is_exposed(&self) -> bool {
|
||||
match self {
|
||||
NodeInput::Node(_) => true,
|
||||
NodeInput::Node { .. } => true,
|
||||
NodeInput::Value { exposed, .. } => *exposed,
|
||||
NodeInput::Network => false,
|
||||
}
|
||||
|
@ -149,7 +161,7 @@ impl NodeInput {
|
|||
impl PartialEq for NodeInput {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (&self, &other) {
|
||||
(Self::Node(n1), Self::Node(n2)) => n1 == n2,
|
||||
(Self::Node { node_id: n0, output_index: o0 }, Self::Node { node_id: n1, output_index: o1 }) => n0 == n1 && o0 == o1,
|
||||
(Self::Value { tagged_value: v1, .. }, Self::Value { tagged_value: v2, .. }) => v1 == v2,
|
||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
||||
}
|
||||
|
@ -181,24 +193,38 @@ impl DocumentNodeImplementation {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, DynAny, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct NodeOutput {
|
||||
pub node_id: NodeId,
|
||||
pub node_output_index: usize,
|
||||
}
|
||||
impl NodeOutput {
|
||||
pub fn new(node_id: NodeId, node_output_index: usize) -> Self {
|
||||
Self { node_id, node_output_index }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, DynAny, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct NodeNetwork {
|
||||
pub inputs: Vec<NodeId>,
|
||||
pub output: NodeId,
|
||||
pub outputs: Vec<NodeOutput>,
|
||||
pub nodes: HashMap<NodeId, DocumentNode>,
|
||||
/// These nodes are replaced with identity nodes when flattening
|
||||
pub disabled: Vec<NodeId>,
|
||||
/// In the case where a new node is chosen as output - what was the origional
|
||||
pub previous_output: Option<NodeId>,
|
||||
/// In the case where a new node is chosen as output - what was the original
|
||||
pub previous_outputs: Option<Vec<NodeOutput>>,
|
||||
}
|
||||
|
||||
impl NodeNetwork {
|
||||
pub fn map_ids(&mut self, f: impl Fn(NodeId) -> NodeId + Copy) {
|
||||
self.inputs.iter_mut().for_each(|id| *id = f(*id));
|
||||
self.output = f(self.output);
|
||||
self.outputs.iter_mut().for_each(|output| output.node_id = f(output.node_id));
|
||||
self.disabled.iter_mut().for_each(|id| *id = f(*id));
|
||||
self.previous_output = self.previous_output.map(f);
|
||||
self.previous_outputs
|
||||
.iter_mut()
|
||||
.for_each(|nodes| nodes.iter_mut().for_each(|output| output.node_id = f(output.node_id)));
|
||||
let mut empty = HashMap::new();
|
||||
std::mem::swap(&mut self.nodes, &mut empty);
|
||||
self.nodes = empty
|
||||
|
@ -215,7 +241,7 @@ impl NodeNetwork {
|
|||
let mut outwards_links: HashMap<u64, Vec<u64>> = HashMap::new();
|
||||
for (node_id, node) in &self.nodes {
|
||||
for input in &node.inputs {
|
||||
if let NodeInput::Node(ref_id) = input {
|
||||
if let NodeInput::Node { node_id: ref_id, .. } = input {
|
||||
outwards_links.entry(*ref_id).or_default().push(*node_id)
|
||||
}
|
||||
}
|
||||
|
@ -223,6 +249,102 @@ impl NodeNetwork {
|
|||
outwards_links
|
||||
}
|
||||
|
||||
/// When a node has multiple outputs, we actually just duplicate the node and evaluate each output separately
|
||||
pub fn duplicate_outputs(&mut self, mut gen_id: &mut impl FnMut() -> NodeId) {
|
||||
let mut duplicating_nodes = HashMap::new();
|
||||
// Find the nodes where the inputs require duplicating
|
||||
for node in &mut self.nodes.values_mut() {
|
||||
// Recursivly duplicate children
|
||||
if let DocumentNodeImplementation::Network(network) = &mut node.implementation {
|
||||
network.duplicate_outputs(gen_id);
|
||||
}
|
||||
|
||||
for input in &mut node.inputs {
|
||||
let &mut NodeInput::Node { node_id, output_index} = input else {
|
||||
continue;
|
||||
};
|
||||
// Use the initial node when getting the first output
|
||||
if output_index == 0 {
|
||||
continue;
|
||||
}
|
||||
// Get the existing duplicated node id (or create a new one)
|
||||
let duplicated_node_id = *duplicating_nodes.entry((node_id, output_index)).or_insert_with(&mut gen_id);
|
||||
// Use the first output from the duplicated node
|
||||
*input = NodeInput::node(duplicated_node_id, 0);
|
||||
}
|
||||
}
|
||||
// Find the network outputs that require duplicating
|
||||
for network_output in &mut self.outputs {
|
||||
// Use the initial node when getting the first output
|
||||
if network_output.node_output_index == 0 {
|
||||
continue;
|
||||
}
|
||||
// Get the existing duplicated node id (or create a new one)
|
||||
let duplicated_node_id = *duplicating_nodes.entry((network_output.node_id, network_output.node_output_index)).or_insert_with(&mut gen_id);
|
||||
// Use the first output from the duplicated node
|
||||
*network_output = NodeOutput::new(duplicated_node_id, 0);
|
||||
}
|
||||
// Duplicate the nodes
|
||||
for ((original_node_id, output_index), new_node_id) in duplicating_nodes {
|
||||
let Some(original_node) = self.nodes.get(&original_node_id) else {
|
||||
continue;
|
||||
};
|
||||
let mut new_node = original_node.clone();
|
||||
// Update the required outputs from a nested network to be just the relevant output
|
||||
if let DocumentNodeImplementation::Network(network) = &mut new_node.implementation {
|
||||
if network.outputs.is_empty() {
|
||||
continue;
|
||||
}
|
||||
network.outputs = vec![network.outputs[output_index]];
|
||||
}
|
||||
self.nodes.insert(new_node_id, new_node);
|
||||
}
|
||||
|
||||
// Ensure all nodes only have one output
|
||||
for node in self.nodes.values_mut() {
|
||||
if let DocumentNodeImplementation::Network(network) = &mut node.implementation {
|
||||
if network.outputs.is_empty() {
|
||||
continue;
|
||||
}
|
||||
network.outputs = vec![network.outputs[0]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes unused nodes from the graph. Returns a list of bools which represent if each of the inputs have been retained
|
||||
pub fn remove_dead_nodes(&mut self) -> Vec<bool> {
|
||||
// Take all the nodes out of the nodes list
|
||||
let mut old_nodes = std::mem::take(&mut self.nodes);
|
||||
let mut stack = self.outputs.iter().map(|output| output.node_id).collect::<Vec<_>>();
|
||||
while let Some(node_id) = stack.pop() {
|
||||
let Some((node_id, mut document_node)) = old_nodes.remove_entry(&node_id) else {
|
||||
continue;
|
||||
};
|
||||
// Remove dead nodes from child networks
|
||||
if let DocumentNodeImplementation::Network(network) = &mut document_node.implementation {
|
||||
// Remove inputs to the parent node if they have been removed from the child
|
||||
let mut retain_inputs = network.remove_dead_nodes().into_iter();
|
||||
document_node.inputs.retain(|_| retain_inputs.next().unwrap_or(true))
|
||||
}
|
||||
// Visit all nodes that this node references
|
||||
stack.extend(
|
||||
document_node
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|input| if let NodeInput::Node { node_id, .. } = input { Some(node_id) } else { None }),
|
||||
);
|
||||
// Add the node back to the list of nodes
|
||||
self.nodes.insert(node_id, document_node);
|
||||
}
|
||||
|
||||
// Check if inputs are used and store for return value
|
||||
let are_inputs_used = self.inputs.iter().map(|input| self.nodes.contains_key(input)).collect();
|
||||
// Remove unused inputs from graph
|
||||
self.inputs.retain(|input| self.nodes.contains_key(input));
|
||||
|
||||
are_inputs_used
|
||||
}
|
||||
|
||||
pub fn flatten(&mut self, node: NodeId) {
|
||||
self.flatten_with_fns(node, merge_ids, generate_uuid)
|
||||
}
|
||||
|
@ -254,9 +376,9 @@ impl NodeNetwork {
|
|||
for (document_input, network_input) in node.inputs.into_iter().zip(inner_network.inputs.iter()) {
|
||||
let offset = network_offsets.entry(network_input).or_insert(0);
|
||||
match document_input {
|
||||
NodeInput::Node(node) => {
|
||||
NodeInput::Node { node_id, output_index } => {
|
||||
let network_input = self.nodes.get_mut(network_input).unwrap();
|
||||
network_input.populate_first_network_input(node, *offset);
|
||||
network_input.populate_first_network_input(node_id, output_index, *offset);
|
||||
}
|
||||
NodeInput::Value { tagged_value, exposed } => {
|
||||
// Skip formatting very large values for seconds in performance speedup
|
||||
|
@ -278,7 +400,7 @@ impl NodeNetwork {
|
|||
assert!(!self.nodes.contains_key(&new_id));
|
||||
self.nodes.insert(new_id, value_node);
|
||||
let network_input = self.nodes.get_mut(network_input).unwrap();
|
||||
network_input.populate_first_network_input(new_id, *offset);
|
||||
network_input.populate_first_network_input(new_id, 0, *offset);
|
||||
}
|
||||
NodeInput::Network => {
|
||||
*network_offsets.get_mut(network_input).unwrap() += 1;
|
||||
|
@ -289,7 +411,14 @@ impl NodeNetwork {
|
|||
}
|
||||
}
|
||||
node.implementation = DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]));
|
||||
node.inputs = vec![NodeInput::Node(inner_network.output)];
|
||||
node.inputs = inner_network
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|&NodeOutput { node_id, node_output_index }| NodeInput::Node {
|
||||
node_id,
|
||||
output_index: node_output_index,
|
||||
})
|
||||
.collect();
|
||||
for node_id in new_nodes {
|
||||
self.flatten_with_fns(node_id, map_ids, gen_id);
|
||||
}
|
||||
|
@ -300,26 +429,28 @@ impl NodeNetwork {
|
|||
self.nodes.insert(id, node);
|
||||
}
|
||||
|
||||
pub fn into_proto_network(self) -> ProtoNetwork {
|
||||
pub fn into_proto_networks(self) -> impl Iterator<Item = ProtoNetwork> {
|
||||
let mut nodes: Vec<_> = self.nodes.into_iter().map(|(id, node)| (id, node.resolve_proto_node())).collect();
|
||||
nodes.sort_unstable_by_key(|(i, _)| *i);
|
||||
ProtoNetwork {
|
||||
inputs: self.inputs,
|
||||
output: self.output,
|
||||
nodes,
|
||||
}
|
||||
|
||||
// Create a network to evaluate each output
|
||||
self.outputs.into_iter().map(move |output| ProtoNetwork {
|
||||
inputs: self.inputs.clone(),
|
||||
output: output.node_id,
|
||||
nodes: nodes.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the original output node of this network, ignoring any preview node
|
||||
pub fn original_output(&self) -> NodeId {
|
||||
self.previous_output.unwrap_or(self.output)
|
||||
/// Get the original output nodes of this network, ignoring any preview node
|
||||
pub fn original_outputs(&self) -> &Vec<NodeOutput> {
|
||||
self.previous_outputs.as_ref().unwrap_or(&self.outputs)
|
||||
}
|
||||
|
||||
/// A graph with just an input and output node
|
||||
pub fn new_network(output_offset: i32, output_node_id: NodeId) -> Self {
|
||||
Self {
|
||||
inputs: vec![0],
|
||||
output: 1,
|
||||
outputs: vec![NodeOutput::new(1, 0)],
|
||||
nodes: [
|
||||
(
|
||||
0,
|
||||
|
@ -334,7 +465,7 @@ impl NodeNetwork {
|
|||
1,
|
||||
DocumentNode {
|
||||
name: "Output".into(),
|
||||
inputs: vec![NodeInput::Node(output_node_id)],
|
||||
inputs: vec![NodeInput::node(output_node_id, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
metadata: DocumentNodeMetadata { position: (output_offset, 4).into() },
|
||||
},
|
||||
|
@ -367,28 +498,27 @@ impl NodeNetwork {
|
|||
}
|
||||
|
||||
/// Check if the specified node id is connected to the output
|
||||
pub fn connected_to_output(&self, node_id: NodeId) -> bool {
|
||||
pub fn connected_to_output(&self, target_node_id: NodeId) -> bool {
|
||||
// If the node is the output then return true
|
||||
if self.output == node_id {
|
||||
if self.outputs.iter().any(|&NodeOutput { node_id, .. }| node_id == target_node_id) {
|
||||
return true;
|
||||
}
|
||||
// Get the output
|
||||
let Some(output_node) = self.nodes.get(&self.output) else {
|
||||
// Get the outputs
|
||||
let Some(mut stack) = self.outputs.iter().map(|&output| self.nodes.get(&output.node_id)).collect::<Option<Vec<_>>>() else {
|
||||
return false;
|
||||
};
|
||||
let mut stack = vec![output_node];
|
||||
let mut already_visited = HashSet::new();
|
||||
already_visited.insert(self.output);
|
||||
already_visited.extend(self.outputs.iter().map(|output| output.node_id));
|
||||
|
||||
while let Some(node) = stack.pop() {
|
||||
for input in &node.inputs {
|
||||
if let &NodeInput::Node(ref_id) = input {
|
||||
if let &NodeInput::Node { node_id: ref_id, .. } = input {
|
||||
// Skip if already viewed
|
||||
if already_visited.contains(&ref_id) {
|
||||
continue;
|
||||
}
|
||||
// If the target node is used as input then return true
|
||||
if ref_id == node_id {
|
||||
if ref_id == target_node_id {
|
||||
return true;
|
||||
}
|
||||
// Add the referenced node to the stack
|
||||
|
@ -403,6 +533,21 @@ impl NodeNetwork {
|
|||
|
||||
false
|
||||
}
|
||||
|
||||
/// Is the node being used directly as an output?
|
||||
pub fn outputs_contain(&self, node_id: NodeId) -> bool {
|
||||
self.outputs.iter().any(|output| output.node_id == node_id)
|
||||
}
|
||||
|
||||
/// Is the node being used directly as an original output?
|
||||
pub fn original_outputs_contain(&self, node_id: NodeId) -> bool {
|
||||
self.original_outputs().iter().any(|output| output.node_id == node_id)
|
||||
}
|
||||
|
||||
/// Is the node being used directly as a previous output?
|
||||
pub fn previous_outputs_contain(&self, node_id: NodeId) -> Option<bool> {
|
||||
self.previous_outputs.as_ref().map(|outputs| outputs.iter().any(|output| output.node_id == node_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -421,7 +566,7 @@ mod test {
|
|||
fn add_network() -> NodeNetwork {
|
||||
NodeNetwork {
|
||||
inputs: vec![0, 0],
|
||||
output: 1,
|
||||
outputs: vec![NodeOutput::new(1, 0)],
|
||||
nodes: [
|
||||
(
|
||||
0,
|
||||
|
@ -436,7 +581,7 @@ mod test {
|
|||
1,
|
||||
DocumentNode {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::Node(0)],
|
||||
inputs: vec![NodeInput::node(0, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])),
|
||||
},
|
||||
|
@ -454,7 +599,7 @@ mod test {
|
|||
network.map_ids(|id| id + 1);
|
||||
let maped_add = NodeNetwork {
|
||||
inputs: vec![1, 1],
|
||||
output: 2,
|
||||
outputs: vec![NodeOutput::new(2, 0)],
|
||||
nodes: [
|
||||
(
|
||||
1,
|
||||
|
@ -469,7 +614,7 @@ mod test {
|
|||
2,
|
||||
DocumentNode {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::Node(1)],
|
||||
inputs: vec![NodeInput::node(1, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])),
|
||||
},
|
||||
|
@ -486,7 +631,7 @@ mod test {
|
|||
fn flatten_add() {
|
||||
let mut network = NodeNetwork {
|
||||
inputs: vec![1],
|
||||
output: 1,
|
||||
outputs: vec![NodeOutput::new(1, 0)],
|
||||
nodes: [(
|
||||
1,
|
||||
DocumentNode {
|
||||
|
@ -518,7 +663,7 @@ mod test {
|
|||
fn resolve_proto_node_add() {
|
||||
let document_node = DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Node(0)],
|
||||
inputs: vec![NodeInput::Network, NodeInput::node(0, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])),
|
||||
};
|
||||
|
@ -568,23 +713,23 @@ mod test {
|
|||
.collect(),
|
||||
};
|
||||
let network = flat_network();
|
||||
let resolved_network = network.into_proto_network();
|
||||
let resolved_network = network.into_proto_networks().collect::<Vec<_>>();
|
||||
|
||||
println!("{:#?}", resolved_network);
|
||||
println!("{:#?}", resolved_network[0]);
|
||||
println!("{:#?}", construction_network);
|
||||
assert_eq!(resolved_network, construction_network);
|
||||
assert_eq!(resolved_network[0], construction_network);
|
||||
}
|
||||
|
||||
fn flat_network() -> NodeNetwork {
|
||||
NodeNetwork {
|
||||
inputs: vec![10],
|
||||
output: 1,
|
||||
outputs: vec![NodeOutput::new(1, 0)],
|
||||
nodes: [
|
||||
(
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![NodeInput::Node(11)],
|
||||
inputs: vec![NodeInput::node(11, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
},
|
||||
|
@ -593,7 +738,7 @@ mod test {
|
|||
10,
|
||||
DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Node(14)],
|
||||
inputs: vec![NodeInput::Network, NodeInput::node(14, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])),
|
||||
},
|
||||
|
@ -614,7 +759,7 @@ mod test {
|
|||
11,
|
||||
DocumentNode {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::Node(10)],
|
||||
inputs: vec![NodeInput::node(10, 0)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])),
|
||||
},
|
||||
|
@ -625,4 +770,121 @@ mod test {
|
|||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn two_node_identity() -> NodeNetwork {
|
||||
NodeNetwork {
|
||||
inputs: vec![1, 2],
|
||||
outputs: vec![NodeOutput::new(1, 0), NodeOutput::new(2, 0)],
|
||||
nodes: [
|
||||
(
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "Identity 1".into(),
|
||||
inputs: vec![NodeInput::Network],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
},
|
||||
),
|
||||
(
|
||||
2,
|
||||
DocumentNode {
|
||||
name: "Identity 2".into(),
|
||||
inputs: vec![NodeInput::Network],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn output_duplicate(network_outputs: Vec<NodeOutput>, result_node_input: NodeInput) -> NodeNetwork {
|
||||
let mut network = NodeNetwork {
|
||||
inputs: Vec::new(),
|
||||
outputs: network_outputs,
|
||||
nodes: [
|
||||
(
|
||||
10,
|
||||
DocumentNode {
|
||||
name: "Nested network".into(),
|
||||
inputs: vec![NodeInput::value(TaggedValue::F32(1.), false), NodeInput::value(TaggedValue::F32(2.), false)],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Network(two_node_identity()),
|
||||
},
|
||||
),
|
||||
(
|
||||
11,
|
||||
DocumentNode {
|
||||
name: "Result".into(),
|
||||
inputs: vec![result_node_input],
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut new_ids = 101..;
|
||||
network.duplicate_outputs(&mut || new_ids.next().unwrap());
|
||||
network.remove_dead_nodes();
|
||||
network
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_duplicate() {
|
||||
let result = output_duplicate(vec![NodeOutput::new(10, 1)], NodeInput::node(10, 0));
|
||||
assert_eq!(result.outputs.len(), 1, "The number of outputs should remain as 1");
|
||||
assert_eq!(result.outputs[0], NodeOutput::new(101, 0), "The outer network output should be from a duplicated inner network");
|
||||
assert_eq!(result.nodes.keys().copied().collect::<Vec<_>>(), vec![101], "Should just call nested network");
|
||||
let nested_network_node = result.nodes.get(&101).unwrap();
|
||||
assert_eq!(nested_network_node.name, "Nested network".to_string(), "Name should not change");
|
||||
assert_eq!(nested_network_node.inputs, vec![NodeInput::value(TaggedValue::F32(2.), false)], "Input should be 2");
|
||||
let inner_network = nested_network_node.implementation.get_network().expect("Implementation should be network");
|
||||
assert_eq!(inner_network.inputs, vec![2], "The input should be sent to the second node");
|
||||
assert_eq!(inner_network.outputs, vec![NodeOutput::new(2, 0)], "The output should be node id 2");
|
||||
assert_eq!(inner_network.nodes.get(&2).unwrap().name, "Identity 2", "The node should be identity 2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn out_of_order_duplicate() {
|
||||
let result = output_duplicate(vec![NodeOutput::new(10, 1), NodeOutput::new(10, 0)], NodeInput::node(10, 0));
|
||||
assert_eq!(result.outputs[0], NodeOutput::new(101, 0), "The first network output should be from a duplicated nested network");
|
||||
assert_eq!(result.outputs[1], NodeOutput::new(10, 0), "The second network output should be from the original nested network");
|
||||
assert!(
|
||||
result.nodes.contains_key(&10) && result.nodes.contains_key(&101) && result.nodes.len() == 2,
|
||||
"Network should contain two duplicated nodes"
|
||||
);
|
||||
for (node_id, input_value, inner_id) in [(10, 1., 1), (101, 2., 2)] {
|
||||
let nested_network_node = result.nodes.get(&node_id).unwrap();
|
||||
assert_eq!(nested_network_node.name, "Nested network".to_string(), "Name should not change");
|
||||
assert_eq!(nested_network_node.inputs, vec![NodeInput::value(TaggedValue::F32(input_value), false)], "Input should be stable");
|
||||
let inner_network = nested_network_node.implementation.get_network().expect("Implementation should be network");
|
||||
assert_eq!(inner_network.inputs, vec![inner_id], "The input should be sent to the second node");
|
||||
assert_eq!(inner_network.outputs, vec![NodeOutput::new(inner_id, 0)], "The output should be node id");
|
||||
assert_eq!(inner_network.nodes.get(&inner_id).unwrap().name, format!("Identity {inner_id}"), "The node should be identity");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn using_other_node_duplicate() {
|
||||
let result = output_duplicate(vec![NodeOutput::new(11, 0)], NodeInput::node(10, 1));
|
||||
assert_eq!(result.outputs, vec![NodeOutput::new(11, 0)], "The network output should be the result node");
|
||||
assert!(
|
||||
result.nodes.contains_key(&11) && result.nodes.contains_key(&101) && result.nodes.len() == 2,
|
||||
"Network should contain a duplicated node and a result node"
|
||||
);
|
||||
let result_node = result.nodes.get(&11).unwrap();
|
||||
assert_eq!(result_node.inputs, vec![NodeInput::node(101, 0)], "Result node should refer to duplicate node as input");
|
||||
let nested_network_node = result.nodes.get(&101).unwrap();
|
||||
assert_eq!(nested_network_node.name, "Nested network".to_string(), "Name should not change");
|
||||
assert_eq!(nested_network_node.inputs, vec![NodeInput::value(TaggedValue::F32(2.), false)], "Input should be 2");
|
||||
let inner_network = nested_network_node.implementation.get_network().expect("Implementation should be network");
|
||||
assert_eq!(inner_network.inputs, vec![2], "The input should be sent to the second node");
|
||||
assert_eq!(inner_network.outputs, vec![NodeOutput::new(2, 0)], "The output should be node id 2");
|
||||
assert_eq!(inner_network.nodes.get(&2).unwrap().name, "Identity 2", "The node should be identity 2");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pub use dyn_any::StaticType;
|
||||
use dyn_any::{DynAny, Upcast};
|
||||
use dyn_clone::DynClone;
|
||||
pub use glam::DVec2;
|
||||
pub use glam::{DAffine2, DVec2};
|
||||
use graphene_core::raster::LuminanceCalculation;
|
||||
use graphene_core::Node;
|
||||
use std::hash::Hash;
|
||||
|
@ -22,6 +22,7 @@ pub enum TaggedValue {
|
|||
Bool(bool),
|
||||
DVec2(DVec2),
|
||||
OptionalDVec2(Option<DVec2>),
|
||||
DAffine2(DAffine2),
|
||||
Image(graphene_core::raster::Image),
|
||||
RcImage(Option<Arc<graphene_core::raster::Image>>),
|
||||
Color(graphene_core::raster::color::Color),
|
||||
|
@ -68,44 +69,48 @@ impl Hash for TaggedValue {
|
|||
8.hash(state);
|
||||
Self::DVec2(*v).hash(state)
|
||||
}
|
||||
Self::Image(i) => {
|
||||
Self::DAffine2(m) => {
|
||||
9.hash(state);
|
||||
i.hash(state)
|
||||
m.to_cols_array().iter().for_each(|x| x.to_bits().hash(state))
|
||||
}
|
||||
Self::RcImage(i) => {
|
||||
Self::Image(i) => {
|
||||
10.hash(state);
|
||||
i.hash(state)
|
||||
}
|
||||
Self::Color(c) => {
|
||||
Self::RcImage(i) => {
|
||||
11.hash(state);
|
||||
i.hash(state)
|
||||
}
|
||||
Self::Color(c) => {
|
||||
12.hash(state);
|
||||
c.hash(state)
|
||||
}
|
||||
Self::Subpath(s) => {
|
||||
12.hash(state);
|
||||
s.hash(state)
|
||||
}
|
||||
Self::RcSubpath(s) => {
|
||||
13.hash(state);
|
||||
s.hash(state)
|
||||
}
|
||||
Self::LuminanceCalculation(l) => {
|
||||
Self::RcSubpath(s) => {
|
||||
14.hash(state);
|
||||
s.hash(state)
|
||||
}
|
||||
Self::LuminanceCalculation(l) => {
|
||||
15.hash(state);
|
||||
l.hash(state)
|
||||
}
|
||||
Self::ImaginateSamplingMethod(m) => {
|
||||
15.hash(state);
|
||||
16.hash(state);
|
||||
m.hash(state)
|
||||
}
|
||||
Self::ImaginateMaskStartingFill(f) => {
|
||||
16.hash(state);
|
||||
17.hash(state);
|
||||
f.hash(state)
|
||||
}
|
||||
Self::ImaginateStatus(s) => {
|
||||
17.hash(state);
|
||||
18.hash(state);
|
||||
s.hash(state)
|
||||
}
|
||||
Self::LayerPath(p) => {
|
||||
18.hash(state);
|
||||
19.hash(state);
|
||||
p.hash(state)
|
||||
}
|
||||
}
|
||||
|
@ -124,6 +129,7 @@ impl<'a> TaggedValue {
|
|||
TaggedValue::Bool(x) => Box::new(x),
|
||||
TaggedValue::DVec2(x) => Box::new(x),
|
||||
TaggedValue::OptionalDVec2(x) => Box::new(x),
|
||||
TaggedValue::DAffine2(x) => Box::new(x),
|
||||
TaggedValue::Image(x) => Box::new(x),
|
||||
TaggedValue::RcImage(x) => Box::new(x),
|
||||
TaggedValue::Color(x) => Box::new(x),
|
||||
|
|
|
@ -8,21 +8,29 @@ use crate::proto::ProtoNetwork;
|
|||
pub struct Compiler {}
|
||||
|
||||
impl Compiler {
|
||||
pub fn compile(&self, mut network: NodeNetwork, resolve_inputs: bool) -> ProtoNetwork {
|
||||
pub fn compile(&self, mut network: NodeNetwork, resolve_inputs: bool) -> impl Iterator<Item = ProtoNetwork> {
|
||||
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
||||
println!("flattening");
|
||||
for id in node_ids {
|
||||
network.flatten(id);
|
||||
}
|
||||
let mut proto_network = network.into_proto_network();
|
||||
if resolve_inputs {
|
||||
println!("resolving inputs");
|
||||
proto_network.resolve_inputs();
|
||||
}
|
||||
println!("reordering ids");
|
||||
proto_network.reorder_ids();
|
||||
proto_network.generate_stable_node_ids();
|
||||
proto_network
|
||||
let proto_networks = network.into_proto_networks();
|
||||
proto_networks.map(move |mut proto_network| {
|
||||
if resolve_inputs {
|
||||
println!("resolving inputs");
|
||||
proto_network.resolve_inputs();
|
||||
}
|
||||
proto_network.reorder_ids();
|
||||
proto_network.generate_stable_node_ids();
|
||||
proto_network
|
||||
})
|
||||
}
|
||||
pub fn compile_single(&self, network: NodeNetwork, resolve_inputs: bool) -> Result<ProtoNetwork, String> {
|
||||
assert_eq!(network.outputs.len(), 1, "Graph with multiple outputs not yet handled");
|
||||
let Some(proto_network) = self.compile(network, resolve_inputs).next() else {
|
||||
return Err("Failed to convert graph into proto graph".to_string());
|
||||
};
|
||||
Ok(proto_network)
|
||||
}
|
||||
}
|
||||
pub type Any<'a> = Box<dyn DynAny<'a> + 'a>;
|
||||
|
|
|
@ -94,7 +94,7 @@ pub struct ProtoNetwork {
|
|||
pub nodes: Vec<(NodeId, ProtoNode)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConstructionArgs {
|
||||
Value(value::TaggedValue),
|
||||
Nodes(Vec<NodeId>),
|
||||
|
@ -133,7 +133,7 @@ impl ConstructionArgs {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct ProtoNode {
|
||||
pub construction_args: ConstructionArgs,
|
||||
pub input: ProtoNodeInput,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue