diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index 1d259fef..6b9f487b 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -13,6 +13,8 @@ mod document_node_types; mod node_properties; pub use self::document_node_types::*; +use glam::IVec2; + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub enum FrontendGraphDataType { #[default] @@ -300,7 +302,7 @@ impl NodeGraphMessageHandler { }) .collect(), outputs: node_type.outputs.to_vec(), - position: node.metadata.position, + position: node.metadata.position.into(), output: network.output == *id, disabled: network.disabled.contains(id), }) @@ -368,6 +370,22 @@ impl NodeGraphMessageHandler { false } } + + /// Gets the default node input based on the node name and the input index + fn default_node_input(name: String, index: usize) -> Option { + 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) -> impl Iterator + '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 for NodeGraphMessageHandler { @@ -413,16 +431,12 @@ impl MessageHandler>(); + let new_ids = &self.selected_nodes.iter().copied().enumerate().map(|(new, old)| (old, new as NodeId)).collect(); + let copied_nodes: Vec<_> = Self::copy_nodes(network, new_ids).collect(); // Prefix to show that this is 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()); } @@ -465,7 +479,7 @@ impl MessageHandler { if let Some(network) = self.get_active_network_mut(document) { - let mut new_selected = Vec::new(); - for &id in &self.selected_nodes { - // 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(); + let new_ids = &self.selected_nodes.iter().map(|&id| (id, crate::application::generate_uuid())).collect(); + self.selected_nodes.clear(); - // Shift duplicated nodes - node.metadata.position.0 += 2; - node.metadata.position.1 += 2; + // Copy the selected nodes + let copied_nodes = Self::copy_nodes(network, new_ids).collect::>(); + for (new_id, mut node) in copied_nodes { + // Shift duplicated node + node.metadata.position += IVec2::splat(2); - network.nodes.insert(new_id, node); - new_selected.push(new_id); - } - } + // Add new node to the list + self.selected_nodes.push(new_id); + network.nodes.insert(new_id, node); } - self.selected_nodes = new_selected; Self::send_graph(network, responses); self.update_selected(document, responses); } @@ -592,8 +601,7 @@ impl MessageHandler>(&serialized_nodes) { + let data = match serde_json::from_str::>(&serialized_nodes) { Ok(d) => d, Err(e) => { warn!("Invalid node data {e:?}"); @@ -629,13 +637,30 @@ impl MessageHandler = data.iter().map(|&(id, _)| (id, crate::application::generate_uuid())).collect(); + 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 - self.selected_nodes.push(id); + self.selected_nodes.push(new_id); } Self::send_graph(network, responses); @@ -699,10 +724,10 @@ impl MessageHandler u64 { #[derive(Clone, Debug, PartialEq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DocumentNodeMetadata { - pub position: (i32, i32), + pub position: IVec2, } #[derive(Clone, Debug, PartialEq)] @@ -94,6 +95,28 @@ impl DocumentNode { 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

(mut self, default_input: P, new_ids: &HashMap) -> Self + where + P: Fn(String, usize) -> Option, + { + 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)]