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:
0HyperCube 2022-12-28 20:00:53 +00:00 committed by Keavon Chambers
parent c552edd525
commit b408bef14b
4 changed files with 88 additions and 40 deletions

View file

@ -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<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 {
@ -413,16 +431,12 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
};
// Collect the selected nodes
let selected_nodes = self
.selected_nodes
.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<_>>();
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<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
inputs: document_node_type.inputs.iter().map(|input| input.default.clone()).collect(),
// TODO: Allow inserting nodes that contain other nodes.
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);
@ -527,24 +541,19 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
}
NodeGraphMessage::DuplicateSelectedNodes => {
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::<Vec<_>>();
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<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
for node_id in &self.selected_nodes {
if let Some(node) = network.nodes.get_mut(node_id) {
node.metadata.position.0 += displacement_x;
node.metadata.position.1 += displacement_y;
node.metadata.position += IVec2::new(displacement_x, displacement_y)
}
}
Self::send_graph(network, responses);
@ -621,7 +629,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
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,
Err(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();
for node in data {
let id = crate::application::generate_uuid();
network.nodes.insert(id, node);
let new_ids: HashMap<_, _> = 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<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
let outwards_links = network.collect_outwards_links();
let required_shift = |left: NodeId, right: NodeId, network: &NodeNetwork| {
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
} else {
(8 - (right.metadata.position.0 - left.metadata.position.0)).max(0)
(8 - (right.metadata.position.x - left.metadata.position.x)).max(0)
}
} else {
0
@ -710,7 +735,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
};
let shift_node = |node_id: NodeId, shift: i32, network: &mut NodeNetwork| {
if let Some(node) = network.nodes.get_mut(&node_id) {
node.metadata.position.0 += shift
node.metadata.position.x += shift
}
};
// Shift the actual node

View file

@ -173,7 +173,7 @@ impl Fsm for ImaginateToolFsmState {
name: "Input".into(),
inputs: vec![NodeInput::Network],
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(),
inputs: vec![NodeInput::Node(2)],
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,
// TODO: Allow inserting nodes that contain other nodes.
implementation: DocumentNodeImplementation::Network(imaginate_inner_network),
metadata: graph_craft::document::DocumentNodeMetadata { position: (20, 4) },
metadata: graph_craft::document::DocumentNodeMetadata { position: (20, 4).into() },
},
),
]

View file

@ -148,7 +148,7 @@ impl Fsm for NodeGraphToolFsmState {
name: "Input".into(),
inputs: vec![NodeInput::Network],
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(),
inputs: vec![NodeInput::Node(0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
metadata: DocumentNodeMetadata { position: (20, 4) },
metadata: DocumentNodeMetadata { position: (20, 4).into() },
},
),
]

View file

@ -6,6 +6,7 @@ use std::sync::Mutex;
pub mod value;
use dyn_any::{DynAny, StaticType};
use glam::IVec2;
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaCha20Rng,
@ -33,7 +34,7 @@ fn merge_ids(a: u64, b: u64) -> 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<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)]