mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 03:48:56 +00:00
Visual Scripting - Fixed storage issues where connections would be lost
Visual Scripting - Added node duplication
This commit is contained in:
1 parent
c325a29cc0
commit
041ed8e0a0
11 files changed
+256
-60
No files matched your search
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using FluentAvalonia.UI.Media;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.UI.Shared.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="SKColor" /> into <see cref="Color2" />.
|
||||
/// </summary>
|
||||
public class SKColorToColor2Converter : IValueConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is SKColor skColor)
|
||||
return new Color2(skColor.Red, skColor.Green, skColor.Blue, skColor.Alpha);
|
||||
return new Color2();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is Color2 color2)
|
||||
return new SKColor(color2.R, color2.G, color2.B, color2.A);
|
||||
return SKColor.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core;
|
||||
using Artemis.Core.Services;
|
||||
using FluentAvalonia.Core;
|
||||
|
||||
namespace Artemis.UI.Shared.Services.NodeEditor.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a node editor command that can be used to duplicate a node.
|
||||
/// </summary>
|
||||
public class DuplicateNode : INodeEditorCommand, IDisposable
|
||||
{
|
||||
private readonly INode _node;
|
||||
private readonly bool _copyIncomingConnections;
|
||||
private readonly INodeService _nodeService;
|
||||
private readonly INodeScript _nodeScript;
|
||||
private INode? _copy;
|
||||
private NodeConnectionStore? _connections;
|
||||
|
||||
private bool _executed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DeleteNode" /> class.
|
||||
/// </summary>
|
||||
/// <param name="nodeScript">The node script the node belongs to.</param>
|
||||
/// <param name="node">The node to delete.</param>
|
||||
/// <param name="copyIncomingConnections">A boolean indicating whether incoming connections should be copied.</param>
|
||||
/// <param name="nodeService">The service to use to duplicate the node.</param>
|
||||
public DuplicateNode(INodeScript nodeScript, INode node, bool copyIncomingConnections, INodeService nodeService)
|
||||
{
|
||||
_nodeScript = nodeScript;
|
||||
_node = node;
|
||||
_copyIncomingConnections = copyIncomingConnections;
|
||||
_nodeService = nodeService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_executed && _copy is IDisposable disposableNode)
|
||||
disposableNode.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DisplayName => $"Duplicate '{_node.Name}' node";
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Execute()
|
||||
{
|
||||
if (_copy == null)
|
||||
{
|
||||
_copy = CreateCopy();
|
||||
_nodeScript.AddNode(_copy);
|
||||
}
|
||||
else if (_connections != null)
|
||||
{
|
||||
_nodeScript.AddNode(_copy);
|
||||
_connections.Restore();
|
||||
}
|
||||
|
||||
_executed = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Undo()
|
||||
{
|
||||
if (_copy != null && _connections != null)
|
||||
{
|
||||
_connections.Store();
|
||||
_nodeScript.RemoveNode(_copy);
|
||||
}
|
||||
|
||||
_executed = false;
|
||||
}
|
||||
|
||||
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.CreatePin();
|
||||
}
|
||||
|
||||
// Copy the storage
|
||||
if (_node is Node sourceNode && node is Node targetNode)
|
||||
targetNode.DeserializeStorage(sourceNode.SerializeStorage());
|
||||
|
||||
// Connect input pins
|
||||
if (_copyIncomingConnections)
|
||||
{
|
||||
List<IPin> sourceInputPins = _node.Pins.Concat(_node.PinCollections.SelectMany(c => c)).Where(p => p.Direction == PinDirection.Input).ToList();
|
||||
List<IPin> 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;
|
||||
}
|
||||
}
|
||||
@@ -53,18 +53,20 @@ public class NodeConnectionStore
|
||||
{
|
||||
foreach (IPin nodePin in Node.Pins)
|
||||
{
|
||||
if (_pinConnections.TryGetValue(nodePin, out List<IPin>? connections))
|
||||
foreach (IPin connection in connections)
|
||||
nodePin.ConnectTo(connection);
|
||||
if (!_pinConnections.TryGetValue(nodePin, out List<IPin>? connections))
|
||||
continue;
|
||||
foreach (IPin connection in connections)
|
||||
nodePin.ConnectTo(connection);
|
||||
}
|
||||
|
||||
foreach (IPinCollection nodePinCollection in Node.PinCollections)
|
||||
{
|
||||
foreach (IPin nodePin in nodePinCollection)
|
||||
{
|
||||
if (_pinConnections.TryGetValue(nodePin, out List<IPin>? connections))
|
||||
foreach (IPin connection in connections)
|
||||
nodePin.ConnectTo(connection);
|
||||
if (!_pinConnections.TryGetValue(nodePin, out List<IPin>? connections))
|
||||
continue;
|
||||
foreach (IPin connection in connections)
|
||||
nodePin.ConnectTo(connection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user