Make copying/duplicating nodes not preserve the incoming connection (#917)
* Remove wires to nodes outside of copy * Fix logic error * Shift pasted nodes Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
c552edd525
commit
b408bef14b
|
|
@ -13,6 +13,8 @@ mod document_node_types;
|
||||||
mod node_properties;
|
mod node_properties;
|
||||||
pub use self::document_node_types::*;
|
pub use self::document_node_types::*;
|
||||||
|
|
||||||
|
use glam::IVec2;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum FrontendGraphDataType {
|
pub enum FrontendGraphDataType {
|
||||||
#[default]
|
#[default]
|
||||||
|
|
@ -300,7 +302,7 @@ impl NodeGraphMessageHandler {
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
outputs: node_type.outputs.to_vec(),
|
outputs: node_type.outputs.to_vec(),
|
||||||
position: node.metadata.position,
|
position: node.metadata.position.into(),
|
||||||
output: network.output == *id,
|
output: network.output == *id,
|
||||||
disabled: network.disabled.contains(id),
|
disabled: network.disabled.contains(id),
|
||||||
})
|
})
|
||||||
|
|
@ -368,6 +370,22 @@ impl NodeGraphMessageHandler {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the default node input based on the node name and the input index
|
||||||
|
fn default_node_input(name: String, index: usize) -> Option<NodeInput> {
|
||||||
|
resolve_document_node_type(&name)
|
||||||
|
.and_then(|node| node.inputs.get(index))
|
||||||
|
.map(|input: &DocumentInputType| input.default.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator of nodes to be copied and their ids, excluding output and input nodes
|
||||||
|
fn copy_nodes<'a>(network: &'a NodeNetwork, new_ids: &'a HashMap<NodeId, NodeId>) -> impl Iterator<Item = (NodeId, DocumentNode)> + 'a {
|
||||||
|
new_ids
|
||||||
|
.iter()
|
||||||
|
.filter(|&(&id, _)| id != network.output && !network.inputs.contains(&id))
|
||||||
|
.filter_map(|(&id, &new)| network.nodes.get(&id).map(|node| (new, node.clone())))
|
||||||
|
.map(move |(new, node)| (new, node.map_ids(Self::default_node_input, new_ids)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageHandler)> for NodeGraphMessageHandler {
|
impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageHandler)> for NodeGraphMessageHandler {
|
||||||
|
|
@ -413,16 +431,12 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||||
};
|
};
|
||||||
|
|
||||||
// Collect the selected nodes
|
// Collect the selected nodes
|
||||||
let selected_nodes = self
|
let new_ids = &self.selected_nodes.iter().copied().enumerate().map(|(new, old)| (old, new as NodeId)).collect();
|
||||||
.selected_nodes
|
let copied_nodes: Vec<_> = Self::copy_nodes(network, new_ids).collect();
|
||||||
.iter()
|
|
||||||
.filter(|&&id| !network.inputs.contains(&id) && network.output != id) // Don't copy input or output nodes
|
|
||||||
.filter_map(|id| network.nodes.get(id))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Prefix to show that this is nodes
|
// Prefix to show that this is nodes
|
||||||
let mut copy_text = String::from("graphite/nodes: ");
|
let mut copy_text = String::from("graphite/nodes: ");
|
||||||
copy_text += &serde_json::to_string(&selected_nodes).expect("Could not serialize paste");
|
copy_text += &serde_json::to_string(&copied_nodes).expect("Could not serialize paste");
|
||||||
|
|
||||||
responses.push_back(FrontendMessage::TriggerTextCopy { copy_text }.into());
|
responses.push_back(FrontendMessage::TriggerTextCopy { copy_text }.into());
|
||||||
}
|
}
|
||||||
|
|
@ -465,7 +479,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||||
inputs: document_node_type.inputs.iter().map(|input| input.default.clone()).collect(),
|
inputs: document_node_type.inputs.iter().map(|input| input.default.clone()).collect(),
|
||||||
// TODO: Allow inserting nodes that contain other nodes.
|
// TODO: Allow inserting nodes that contain other nodes.
|
||||||
implementation: DocumentNodeImplementation::Network(inner_network),
|
implementation: DocumentNodeImplementation::Network(inner_network),
|
||||||
metadata: graph_craft::document::DocumentNodeMetadata { position: (x, y) },
|
metadata: graph_craft::document::DocumentNodeMetadata { position: (x, y).into() },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Self::send_graph(network, responses);
|
Self::send_graph(network, responses);
|
||||||
|
|
@ -527,24 +541,19 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||||
}
|
}
|
||||||
NodeGraphMessage::DuplicateSelectedNodes => {
|
NodeGraphMessage::DuplicateSelectedNodes => {
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network_mut(document) {
|
||||||
let mut new_selected = Vec::new();
|
let new_ids = &self.selected_nodes.iter().map(|&id| (id, crate::application::generate_uuid())).collect();
|
||||||
for &id in &self.selected_nodes {
|
self.selected_nodes.clear();
|
||||||
// Don't allow copying input or output nodes.
|
|
||||||
if id != network.output && !network.inputs.contains(&id) {
|
|
||||||
if let Some(node) = network.nodes.get(&id) {
|
|
||||||
let new_id = crate::application::generate_uuid();
|
|
||||||
let mut node = node.clone();
|
|
||||||
|
|
||||||
// Shift duplicated nodes
|
// Copy the selected nodes
|
||||||
node.metadata.position.0 += 2;
|
let copied_nodes = Self::copy_nodes(network, new_ids).collect::<Vec<_>>();
|
||||||
node.metadata.position.1 += 2;
|
for (new_id, mut node) in copied_nodes {
|
||||||
|
// Shift duplicated node
|
||||||
|
node.metadata.position += IVec2::splat(2);
|
||||||
|
|
||||||
network.nodes.insert(new_id, node);
|
// Add new node to the list
|
||||||
new_selected.push(new_id);
|
self.selected_nodes.push(new_id);
|
||||||
}
|
network.nodes.insert(new_id, node);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.selected_nodes = new_selected;
|
|
||||||
Self::send_graph(network, responses);
|
Self::send_graph(network, responses);
|
||||||
self.update_selected(document, responses);
|
self.update_selected(document, responses);
|
||||||
}
|
}
|
||||||
|
|
@ -592,8 +601,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||||
|
|
||||||
for node_id in &self.selected_nodes {
|
for node_id in &self.selected_nodes {
|
||||||
if let Some(node) = network.nodes.get_mut(node_id) {
|
if let Some(node) = network.nodes.get_mut(node_id) {
|
||||||
node.metadata.position.0 += displacement_x;
|
node.metadata.position += IVec2::new(displacement_x, displacement_y)
|
||||||
node.metadata.position.1 += displacement_y;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::send_graph(network, responses);
|
Self::send_graph(network, responses);
|
||||||
|
|
@ -621,7 +629,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = match serde_json::from_str::<Vec<DocumentNode>>(&serialized_nodes) {
|
let data = match serde_json::from_str::<Vec<(NodeId, DocumentNode)>>(&serialized_nodes) {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Invalid node data {e:?}");
|
warn!("Invalid node data {e:?}");
|
||||||
|
|
@ -629,13 +637,30 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Shift nodes until it is not in the same position as another node
|
||||||
|
let mut shift = IVec2::ZERO;
|
||||||
|
while data
|
||||||
|
.iter()
|
||||||
|
.all(|(_, node)| network.nodes.values().any(|existing_node| node.metadata.position + shift == existing_node.metadata.position))
|
||||||
|
{
|
||||||
|
shift += IVec2::splat(2);
|
||||||
|
}
|
||||||
|
|
||||||
self.selected_nodes.clear();
|
self.selected_nodes.clear();
|
||||||
for node in data {
|
|
||||||
let id = crate::application::generate_uuid();
|
let new_ids: HashMap<_, _> = data.iter().map(|&(id, _)| (id, crate::application::generate_uuid())).collect();
|
||||||
network.nodes.insert(id, node);
|
for (old_id, mut node) in data {
|
||||||
|
// Shift copied node
|
||||||
|
node.metadata.position += shift;
|
||||||
|
|
||||||
|
// Get the new, non-conflicting id
|
||||||
|
let new_id = *new_ids.get(&old_id).unwrap();
|
||||||
|
|
||||||
|
// Insert node into network
|
||||||
|
network.nodes.insert(new_id, node.map_ids(Self::default_node_input, &new_ids));
|
||||||
|
|
||||||
// Select the newly pasted node
|
// Select the newly pasted node
|
||||||
self.selected_nodes.push(id);
|
self.selected_nodes.push(new_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::send_graph(network, responses);
|
Self::send_graph(network, responses);
|
||||||
|
|
@ -699,10 +724,10 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||||
let outwards_links = network.collect_outwards_links();
|
let outwards_links = network.collect_outwards_links();
|
||||||
let required_shift = |left: NodeId, right: NodeId, network: &NodeNetwork| {
|
let required_shift = |left: NodeId, right: NodeId, network: &NodeNetwork| {
|
||||||
if let (Some(left), Some(right)) = (network.nodes.get(&left), network.nodes.get(&right)) {
|
if let (Some(left), Some(right)) = (network.nodes.get(&left), network.nodes.get(&right)) {
|
||||||
if right.metadata.position.0 < left.metadata.position.0 {
|
if right.metadata.position.x < left.metadata.position.x {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
(8 - (right.metadata.position.0 - left.metadata.position.0)).max(0)
|
(8 - (right.metadata.position.x - left.metadata.position.x)).max(0)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
|
@ -710,7 +735,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||||
};
|
};
|
||||||
let shift_node = |node_id: NodeId, shift: i32, network: &mut NodeNetwork| {
|
let shift_node = |node_id: NodeId, shift: i32, network: &mut NodeNetwork| {
|
||||||
if let Some(node) = network.nodes.get_mut(&node_id) {
|
if let Some(node) = network.nodes.get_mut(&node_id) {
|
||||||
node.metadata.position.0 += shift
|
node.metadata.position.x += shift
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Shift the actual node
|
// Shift the actual node
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ impl Fsm for ImaginateToolFsmState {
|
||||||
name: "Input".into(),
|
name: "Input".into(),
|
||||||
inputs: vec![NodeInput::Network],
|
inputs: vec![NodeInput::Network],
|
||||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||||
metadata: DocumentNodeMetadata { position: (8, 4) },
|
metadata: DocumentNodeMetadata { position: (8, 4).into() },
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
@ -182,7 +182,7 @@ impl Fsm for ImaginateToolFsmState {
|
||||||
name: "Output".into(),
|
name: "Output".into(),
|
||||||
inputs: vec![NodeInput::Node(2)],
|
inputs: vec![NodeInput::Node(2)],
|
||||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||||
metadata: DocumentNodeMetadata { position: (32, 4) },
|
metadata: DocumentNodeMetadata { position: (32, 4).into() },
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
@ -192,7 +192,7 @@ impl Fsm for ImaginateToolFsmState {
|
||||||
inputs: imaginate_inputs,
|
inputs: imaginate_inputs,
|
||||||
// TODO: Allow inserting nodes that contain other nodes.
|
// TODO: Allow inserting nodes that contain other nodes.
|
||||||
implementation: DocumentNodeImplementation::Network(imaginate_inner_network),
|
implementation: DocumentNodeImplementation::Network(imaginate_inner_network),
|
||||||
metadata: graph_craft::document::DocumentNodeMetadata { position: (20, 4) },
|
metadata: graph_craft::document::DocumentNodeMetadata { position: (20, 4).into() },
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ impl Fsm for NodeGraphToolFsmState {
|
||||||
name: "Input".into(),
|
name: "Input".into(),
|
||||||
inputs: vec![NodeInput::Network],
|
inputs: vec![NodeInput::Network],
|
||||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||||
metadata: DocumentNodeMetadata { position: (8, 4) },
|
metadata: DocumentNodeMetadata { position: (8, 4).into() },
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
@ -157,7 +157,7 @@ impl Fsm for NodeGraphToolFsmState {
|
||||||
name: "Output".into(),
|
name: "Output".into(),
|
||||||
inputs: vec![NodeInput::Node(0)],
|
inputs: vec![NodeInput::Node(0)],
|
||||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
|
||||||
metadata: DocumentNodeMetadata { position: (20, 4) },
|
metadata: DocumentNodeMetadata { position: (20, 4).into() },
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use std::sync::Mutex;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
use dyn_any::{DynAny, StaticType};
|
use dyn_any::{DynAny, StaticType};
|
||||||
|
use glam::IVec2;
|
||||||
use rand_chacha::{
|
use rand_chacha::{
|
||||||
rand_core::{RngCore, SeedableRng},
|
rand_core::{RngCore, SeedableRng},
|
||||||
ChaCha20Rng,
|
ChaCha20Rng,
|
||||||
|
|
@ -33,7 +34,7 @@ fn merge_ids(a: u64, b: u64) -> u64 {
|
||||||
#[derive(Clone, Debug, PartialEq, Default)]
|
#[derive(Clone, Debug, PartialEq, Default)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub struct DocumentNodeMetadata {
|
pub struct DocumentNodeMetadata {
|
||||||
pub position: (i32, i32),
|
pub position: IVec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
@ -94,6 +95,28 @@ impl DocumentNode {
|
||||||
unreachable!("tried to resolve not flattened node on resolved node");
|
unreachable!("tried to resolve not flattened node on resolved node");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts all node id inputs to a new id based on a HashMap.
|
||||||
|
///
|
||||||
|
/// If the node is not in the hashmap then a default input is found based on the node name and input index.
|
||||||
|
pub fn map_ids<P>(mut self, default_input: P, new_ids: &HashMap<NodeId, NodeId>) -> Self
|
||||||
|
where
|
||||||
|
P: Fn(String, usize) -> Option<NodeInput>,
|
||||||
|
{
|
||||||
|
for (index, input) in self.inputs.iter_mut().enumerate() {
|
||||||
|
let &mut NodeInput::Node(id) = input else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(&new_id) = new_ids.get(&id) {
|
||||||
|
*input = NodeInput::Node(new_id);
|
||||||
|
} else if let Some(new_input) = default_input(self.name.clone(), index) {
|
||||||
|
*input = new_input;
|
||||||
|
} else {
|
||||||
|
warn!("Node does not exist in library with that many inputs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue