mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-07 14:58:03 +00:00
Use canvas as target for raster rendering (#1256)
* Implement ApplicationIo * Simplify output duplication logic * Fix let node initialization for ExtractImageFrame * Async macros * Use manual node registry impl * Fix canvas insertion into the dom
This commit is contained in:
parent
57415b948b
commit
259dcdc628
27 changed files with 810 additions and 259 deletions
|
@ -45,7 +45,7 @@ pub struct DocumentNode {
|
|||
|
||||
impl DocumentNode {
|
||||
pub fn populate_first_network_input(&mut self, node_id: NodeId, output_index: usize, offset: usize, lambda: bool) {
|
||||
let input = self
|
||||
let (index, _) = self
|
||||
.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
|
@ -53,7 +53,6 @@ impl DocumentNode {
|
|||
.nth(offset)
|
||||
.unwrap_or_else(|| panic!("no network input found for {self:#?} and offset: {offset}"));
|
||||
|
||||
let index = input.0;
|
||||
self.inputs[index] = NodeInput::Node { node_id, output_index, lambda };
|
||||
}
|
||||
|
||||
|
@ -67,7 +66,7 @@ impl DocumentNode {
|
|||
(ProtoNodeInput::None, ConstructionArgs::Value(tagged_value))
|
||||
}
|
||||
NodeInput::Node { node_id, output_index, lambda } => {
|
||||
assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode.");
|
||||
assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode. {:#?}", self.name);
|
||||
(ProtoNodeInput::Node(node_id, lambda), ConstructionArgs::Nodes(vec![]))
|
||||
}
|
||||
NodeInput::Network(ty) => (ProtoNodeInput::Network(ty), ConstructionArgs::Nodes(vec![])),
|
||||
|
@ -564,9 +563,8 @@ impl NodeNetwork {
|
|||
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
|
||||
let nodes = std::mem::take(&mut self.nodes);
|
||||
self.nodes = nodes
|
||||
.into_iter()
|
||||
.map(|(id, mut node)| {
|
||||
node.inputs.iter_mut().for_each(|input| input.map_ids(f));
|
||||
|
@ -596,70 +594,28 @@ impl NodeNetwork {
|
|||
network.generate_node_paths(new_path.as_slice());
|
||||
}
|
||||
if node.path.is_some() {
|
||||
log::warn!("Overwriting node path");
|
||||
log::warn!("Attempting to overwrite node path");
|
||||
} else {
|
||||
node.path = Some(new_path);
|
||||
}
|
||||
node.path = Some(new_path);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
// Recursively 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
|
||||
fn replace_node_inputs(&mut self, old_input: NodeInput, new_input: NodeInput) {
|
||||
for node in self.nodes.values_mut() {
|
||||
if let DocumentNodeImplementation::Network(network) = &mut node.implementation {
|
||||
if network.outputs.is_empty() {
|
||||
continue;
|
||||
let node_string = format!("{:?}", node);
|
||||
node.inputs.iter_mut().for_each(|input| {
|
||||
if *input == old_input {
|
||||
*input = new_input.clone();
|
||||
}
|
||||
network.outputs = vec![network.outputs[0]];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_network_outputs(&mut self, old_output: NodeOutput, new_output: NodeOutput) {
|
||||
for output in self.outputs.iter_mut() {
|
||||
if *output == old_output {
|
||||
*output = new_output.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -715,8 +671,14 @@ impl NodeNetwork {
|
|||
self.nodes.insert(id, node);
|
||||
return;
|
||||
}
|
||||
|
||||
// replace value inputs with value nodes
|
||||
for input in &mut node.inputs {
|
||||
// Skip inputs that are already value nodes
|
||||
if node.implementation == DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()) {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut dummy_input = NodeInput::ShortCircut(concrete!(()));
|
||||
std::mem::swap(&mut dummy_input, input);
|
||||
if let NodeInput::Value { tagged_value, exposed } = dummy_input {
|
||||
|
@ -749,54 +711,61 @@ impl NodeNetwork {
|
|||
}
|
||||
}
|
||||
|
||||
match node.implementation {
|
||||
DocumentNodeImplementation::Network(mut inner_network) => {
|
||||
// Connect all network inputs to either the parent network nodes, or newly created value nodes.
|
||||
inner_network.map_ids(|inner_id| map_ids(id, inner_id));
|
||||
let new_nodes = inner_network.nodes.keys().cloned().collect::<Vec<_>>();
|
||||
// Copy nodes from the inner network into the parent network
|
||||
self.nodes.extend(inner_network.nodes);
|
||||
self.disabled.extend(inner_network.disabled);
|
||||
if let DocumentNodeImplementation::Network(mut inner_network) = node.implementation {
|
||||
// Connect all network inputs to either the parent network nodes, or newly created value nodes.
|
||||
inner_network.map_ids(|inner_id| map_ids(id, inner_id));
|
||||
let new_nodes = inner_network.nodes.keys().cloned().collect::<Vec<_>>();
|
||||
// Copy nodes from the inner network into the parent network
|
||||
self.nodes.extend(inner_network.nodes);
|
||||
self.disabled.extend(inner_network.disabled);
|
||||
|
||||
let mut network_offsets = HashMap::new();
|
||||
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_id, output_index, lambda } => {
|
||||
let network_input = self.nodes.get_mut(network_input).unwrap();
|
||||
network_input.populate_first_network_input(node_id, output_index, *offset, lambda);
|
||||
}
|
||||
NodeInput::Network(_) => {
|
||||
*network_offsets.get_mut(network_input).unwrap() += 1;
|
||||
if let Some(index) = self.inputs.iter().position(|i| *i == id) {
|
||||
self.inputs[index] = *network_input;
|
||||
}
|
||||
}
|
||||
NodeInput::ShortCircut(_) => (),
|
||||
NodeInput::Value { .. } => unreachable!("Value inputs should have been replaced with value nodes"),
|
||||
NodeInput::Inline(_) => (),
|
||||
let mut network_offsets = HashMap::new();
|
||||
assert_eq!(
|
||||
node.inputs.len(),
|
||||
inner_network.inputs.len(),
|
||||
"The number of inputs to the node and the inner network must be the same {}",
|
||||
node.name
|
||||
);
|
||||
// Match the document node input and the inputs of the inner network
|
||||
for (document_input, network_input) in node.inputs.into_iter().zip(inner_network.inputs.iter()) {
|
||||
// Keep track of how many network inputs we have already connected for each node
|
||||
let offset = network_offsets.entry(network_input).or_insert(0);
|
||||
match document_input {
|
||||
// If the input to self is a node, connect the corresponding output of the inner network to it
|
||||
NodeInput::Node { node_id, output_index, lambda } => {
|
||||
let network_input = self.nodes.get_mut(network_input).unwrap();
|
||||
network_input.populate_first_network_input(node_id, output_index, *offset, lambda);
|
||||
}
|
||||
}
|
||||
node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
|
||||
node.inputs = inner_network
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|&NodeOutput { node_id, node_output_index }| NodeInput::Node {
|
||||
node_id,
|
||||
output_index: node_output_index,
|
||||
lambda: false,
|
||||
})
|
||||
.collect();
|
||||
|
||||
for node_id in new_nodes {
|
||||
self.flatten_with_fns(node_id, map_ids, gen_id);
|
||||
NodeInput::Network(_) => {
|
||||
*network_offsets.get_mut(network_input).unwrap() += 1;
|
||||
if let Some(index) = self.inputs.iter().position(|i| *i == id) {
|
||||
self.inputs[index] = *network_input;
|
||||
}
|
||||
}
|
||||
NodeInput::ShortCircut(_) => (),
|
||||
NodeInput::Value { .. } => unreachable!("Value inputs should have been replaced with value nodes"),
|
||||
NodeInput::Inline(_) => (),
|
||||
}
|
||||
}
|
||||
DocumentNodeImplementation::Unresolved(_) => (),
|
||||
DocumentNodeImplementation::Extract => (),
|
||||
|
||||
// Connect all nodes that were previously connected to this node to the nodes of the inner network
|
||||
for (i, output) in inner_network.outputs.into_iter().enumerate() {
|
||||
let node_input = |node_id, output_index, lambda| NodeInput::Node { node_id, output_index, lambda };
|
||||
|
||||
self.replace_node_inputs(node_input(id, i, false), node_input(output.node_id, output.node_output_index, false));
|
||||
self.replace_node_inputs(node_input(id, i, true), node_input(output.node_id, output.node_output_index, true));
|
||||
|
||||
self.replace_network_outputs(NodeOutput::new(id, i), output);
|
||||
}
|
||||
|
||||
for node_id in new_nodes {
|
||||
self.flatten_with_fns(node_id, map_ids, gen_id);
|
||||
}
|
||||
} else {
|
||||
// If the node is not a network, it is a primitive node and can be inserted into the network as is.
|
||||
assert!(!self.nodes.contains_key(&id), "Trying to insert a node into the network caused an id conflict");
|
||||
self.nodes.insert(id, node);
|
||||
}
|
||||
assert!(!self.nodes.contains_key(&id), "Trying to insert a node into the network caused an id conflict");
|
||||
self.nodes.insert(id, node);
|
||||
}
|
||||
|
||||
fn remove_id_node(&mut self, id: NodeId) -> Result<(), String> {
|
||||
|
@ -1090,17 +1059,8 @@ mod test {
|
|||
fn resolve_flatten_add_as_proto_network() {
|
||||
let construction_network = ProtoNetwork {
|
||||
inputs: vec![10],
|
||||
output: 1,
|
||||
output: 11,
|
||||
nodes: [
|
||||
(
|
||||
1,
|
||||
ProtoNode {
|
||||
identifier: "graphene_core::ops::IdNode".into(),
|
||||
input: ProtoNodeInput::Node(11, false),
|
||||
construction_args: ConstructionArgs::Nodes(vec![]),
|
||||
document_node_path: vec![1],
|
||||
},
|
||||
),
|
||||
(
|
||||
10,
|
||||
ProtoNode {
|
||||
|
@ -1135,18 +1095,8 @@ mod test {
|
|||
fn flat_network() -> NodeNetwork {
|
||||
NodeNetwork {
|
||||
inputs: vec![10],
|
||||
outputs: vec![NodeOutput::new(1, 0)],
|
||||
outputs: vec![NodeOutput::new(11, 0)],
|
||||
nodes: [
|
||||
(
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![NodeInput::node(11, 0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
|
||||
path: Some(vec![1]),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
10,
|
||||
DocumentNode {
|
||||
|
@ -1223,7 +1173,7 @@ mod test {
|
|||
outputs: network_outputs,
|
||||
nodes: [
|
||||
(
|
||||
10,
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "Nested network".into(),
|
||||
inputs: vec![NodeInput::value(TaggedValue::F32(1.), false), NodeInput::value(TaggedValue::F32(2.), false)],
|
||||
|
@ -1232,7 +1182,7 @@ mod test {
|
|||
},
|
||||
),
|
||||
(
|
||||
11,
|
||||
2,
|
||||
DocumentNode {
|
||||
name: "Result".into(),
|
||||
inputs: vec![result_node_input],
|
||||
|
@ -1246,26 +1196,25 @@ mod test {
|
|||
..Default::default()
|
||||
};
|
||||
let mut new_ids = 101..;
|
||||
network.duplicate_outputs(&mut || new_ids.next().unwrap());
|
||||
network.flatten_with_fns(1, |self_id, inner_id| self_id * 10 + inner_id, || 10000);
|
||||
network.flatten_with_fns(2, |self_id, inner_id| self_id * 10 + inner_id, || 10001);
|
||||
network.remove_dead_nodes();
|
||||
network
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_duplicate() {
|
||||
let result = output_duplicate(vec![NodeOutput::new(10, 1)], NodeInput::node(10, 0));
|
||||
let result = output_duplicate(vec![NodeOutput::new(1, 0)], NodeInput::node(1, 0));
|
||||
println!("{:#?}", result);
|
||||
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");
|
||||
assert_eq!(result.outputs[0], NodeOutput::new(11, 0), "The outer network output should be from a duplicated inner network");
|
||||
let mut ids = result.nodes.keys().copied().collect::<Vec<_>>();
|
||||
ids.sort();
|
||||
assert_eq!(ids, vec![11, 10010], "Should only contain identity and values");
|
||||
}
|
||||
|
||||
// TODO: Write more tests
|
||||
/*
|
||||
#[test]
|
||||
fn out_of_order_duplicate() {
|
||||
let result = output_duplicate(vec![NodeOutput::new(10, 1), NodeOutput::new(10, 0)], NodeInput::node(10, 0));
|
||||
|
@ -1303,4 +1252,5 @@ mod test {
|
|||
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");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue