using System; using System.Collections.Generic; using System.Linq; using Artemis.Core; using Artemis.Core.Services; namespace Artemis.UI.Shared.Services.NodeEditor.Commands; /// /// Represents a node editor command that can be used to duplicate a node. /// public class DuplicateNode : INodeEditorCommand, IDisposable { private readonly bool _copyIncomingConnections; private readonly INode _node; private readonly INodeScript _nodeScript; private readonly INodeService _nodeService; private NodeConnectionStore? _connections; private INode? _copy; private bool _executed; /// /// Creates a new instance of the class. /// /// The node script the node belongs to. /// The node to delete. /// A boolean indicating whether incoming connections should be copied. /// The service to use to duplicate the node. public DuplicateNode(INodeScript nodeScript, INode node, bool copyIncomingConnections, INodeService nodeService) { _nodeScript = nodeScript; _node = node; _copyIncomingConnections = copyIncomingConnections; _nodeService = nodeService; } private INode CreateCopy() { NodeData? nodeData = _nodeService.AvailableNodes.FirstOrDefault(d => d.Type == _node.GetType()); if (nodeData == null) throw new ArtemisSharedUIException($"Can't create a copy of node of type {_node.GetType()} because there is no matching node data."); // Create the node INode node = nodeData.CreateNode(_nodeScript, null); // Move it slightly node.X = _node.X + 20; node.Y = _node.Y + 20; // Add pins to collections for (int i = 0; i < _node.PinCollections.Count; i++) { IPinCollection sourceCollection = _node.PinCollections.ElementAt(i); IPinCollection? targetCollection = node.PinCollections.ElementAtOrDefault(i); if (targetCollection == null) continue; while (targetCollection.Count() < sourceCollection.Count()) targetCollection.Add(targetCollection.CreatePin()); } // Copy the storage if (_node is Node sourceNode && node is Node targetNode) targetNode.DeserializeStorage(sourceNode.SerializeStorage()); // Connect input pins if (_copyIncomingConnections) { List sourceInputPins = _node.Pins.Concat(_node.PinCollections.SelectMany(c => c)).Where(p => p.Direction == PinDirection.Input).ToList(); List targetInputPins = node.Pins.Concat(node.PinCollections.SelectMany(c => c)).Where(p => p.Direction == PinDirection.Input).ToList(); for (int i = 0; i < sourceInputPins.Count; i++) { IPin source = sourceInputPins.ElementAt(i); IPin? target = targetInputPins.ElementAtOrDefault(i); if (target == null) continue; foreach (IPin pin in source.ConnectedTo) target.ConnectTo(pin); } } _connections = new NodeConnectionStore(node); return node; } /// public void Dispose() { if (!_executed && _copy is IDisposable disposableNode) disposableNode.Dispose(); } /// public string DisplayName => $"Duplicate '{_node.Name}' node"; /// public void Execute() { if (_copy == null) { _copy = CreateCopy(); _nodeScript.AddNode(_copy); } else if (_connections != null) { _nodeScript.AddNode(_copy); _connections.Restore(); } _executed = true; } /// public void Undo() { if (_copy != null && _connections != null) { _connections.Store(); _nodeScript.RemoveNode(_copy); } _executed = false; } }