123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- using System.Collections.ObjectModel;
- using System.Reflection;
- using Avalonia.Input;
- using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands;
- using PixiEditor.AvaloniaUI.Models.DocumentModels;
- using PixiEditor.AvaloniaUI.Models.Handlers;
- using PixiEditor.AvaloniaUI.ViewModels.Nodes;
- using PixiEditor.ChangeableDocument.Actions;
- using PixiEditor.ChangeableDocument.Actions.Generated;
- using PixiEditor.ChangeableDocument.Changeables.Graph;
- using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
- using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
- using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
- using PixiEditor.Numerics;
- namespace PixiEditor.AvaloniaUI.ViewModels.Document;
- internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler
- {
- public DocumentViewModel DocumentViewModel { get; }
- public ObservableCollection<INodeHandler> AllNodes { get; } = new();
- public ObservableCollection<NodeConnectionViewModel> Connections { get; } = new();
- public ObservableCollection<NodeFrameViewModelBase> Frames { get; } = new();
- public StructureTree StructureTree { get; } = new();
- public INodeHandler? OutputNode { get; private set; }
- private DocumentInternalParts Internals { get; }
- public NodeGraphViewModel(DocumentViewModel documentViewModel, DocumentInternalParts internals)
- {
- DocumentViewModel = documentViewModel;
- Internals = internals;
- }
- public void AddNode(INodeHandler node)
- {
- if (OutputNode == null)
- {
- OutputNode = node; // TODO: this is not really correct yet, a way to check what node type is added is needed
- }
- AllNodes.Add(node);
- StructureTree.Update(this);
- }
- public void RemoveNode(Guid nodeId)
- {
- var node = AllNodes.FirstOrDefault(x => x.Id == nodeId);
- if (node != null)
- {
- AllNodes.Remove(node);
- }
- StructureTree.Update(this);
- }
- public void AddFrame(Guid frameId, IEnumerable<Guid> nodes)
- {
- var frame = new NodeFrameViewModel(frameId, AllNodes.Where(x => nodes.Contains(x.Id)));
- Frames.Add(frame);
- }
- public void AddZone(Guid frameId, string internalName, Guid startId, Guid endId)
- {
- var start = AllNodes.First(x => x.Id == startId);
- var end = AllNodes.First(x => x.Id == endId);
- var zone = new NodeZoneViewModel(frameId, internalName, start, end);
- Frames.Add(zone);
- }
- public void RemoveFrame(Guid guid)
- {
- var frame = Frames.FirstOrDefault(x => x.Id == guid);
- if (frame == null) return;
- Frames.Remove(frame);
- }
- public void SetConnection(NodeConnectionViewModel connection)
- {
- var existingInputConnection = Connections.FirstOrDefault(x => x.InputProperty == connection.InputProperty);
- if (existingInputConnection != null)
- {
- Connections.Remove(existingInputConnection);
- existingInputConnection.InputProperty.ConnectedOutput = null;
- existingInputConnection.OutputProperty.ConnectedInputs.Remove(existingInputConnection.InputProperty);
- }
- connection.InputProperty.ConnectedOutput = connection.OutputProperty;
- connection.OutputProperty.ConnectedInputs.Add(connection.InputProperty);
- Connections.Add(connection);
- StructureTree.Update(this);
- }
- public void RemoveConnection(Guid nodeId, string property)
- {
- var connection = Connections.FirstOrDefault(x =>
- x.InputProperty.Node.Id == nodeId && x.InputProperty.PropertyName == property);
- if (connection != null)
- {
- connection.InputProperty.ConnectedOutput = null;
- connection.OutputProperty.ConnectedInputs.Remove(connection.InputProperty);
- Connections.Remove(connection);
- }
- StructureTree.Update(this);
- }
- public void RemoveConnections(Guid nodeId)
- {
- var connections = Connections
- .Where(x => x.InputProperty.Node.Id == nodeId || x.OutputProperty.Node.Id == nodeId).ToList();
- foreach (var connection in connections)
- {
- connection.InputProperty.ConnectedOutput = null;
- connection.OutputProperty.ConnectedInputs.Remove(connection.InputProperty);
- Connections.Remove(connection);
- }
- StructureTree.Update(this);
- }
- public bool TryTraverse(Func<INodeHandler, bool> func)
- {
- if (OutputNode == null) return false;
- var queue = CalculateExecutionQueue(OutputNode);
- while (queue.Count > 0)
- {
- var node = queue.Dequeue();
- func(node);
- }
- return true;
- }
- private Queue<INodeHandler> CalculateExecutionQueue(INodeHandler outputNode)
- {
- // backwards breadth-first search
- var visited = new HashSet<INodeHandler>();
- var queueNodes = new Queue<INodeHandler>();
- List<INodeHandler> finalQueue = new();
- queueNodes.Enqueue(outputNode);
- while (queueNodes.Count > 0)
- {
- var node = queueNodes.Dequeue();
- if (!visited.Add(node))
- {
- continue;
- }
- finalQueue.Add(node);
- foreach (var input in node.Inputs)
- {
- if (input.ConnectedOutput == null)
- {
- continue;
- }
- queueNodes.Enqueue(input.ConnectedOutput.Node);
- }
- }
- finalQueue.Reverse();
- return new Queue<INodeHandler>(finalQueue);
- }
- public void SetNodePosition(INodeHandler node, VecD newPos)
- {
- Internals.ActionAccumulator.AddActions(new NodePosition_Action(node.Id, newPos));
- }
- public void UpdatePropertyValue(INodeHandler node, string property, object? value)
- {
- Internals.ActionAccumulator.AddFinishedActions(new UpdatePropertyValue_Action(node.Id, property, value));
- }
- public void EndChangeNodePosition()
- {
- Internals.ActionAccumulator.AddFinishedActions(new EndNodePosition_Action());
- }
- public void CreateNode(Type nodeType)
- {
- IAction change;
- PairNodeAttribute? pairAttribute = nodeType.GetCustomAttribute<PairNodeAttribute>(true);
- if (pairAttribute != null)
- {
- change = new CreateNodePair_Action(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), nodeType);
- }
- else
- {
- change = new CreateNode_Action(nodeType, Guid.NewGuid());
- }
- Internals.ActionAccumulator.AddFinishedActions(change);
- }
- public void RemoveNodes(Guid[] selectedNodes)
- {
- IAction[] actions = new IAction[selectedNodes.Length];
-
- for (int i = 0; i < selectedNodes.Length; i++)
- {
- actions[i] = new DeleteNode_Action(selectedNodes[i]);
- }
-
- Internals.ActionAccumulator.AddFinishedActions(actions);
- }
- // TODO: Remove this
- public void CreateNodeFrameAroundEverything()
- {
- CreateNodeFrame(AllNodes);
- }
- public void CreateNodeFrame(IEnumerable<INodeHandler> nodes)
- {
- Internals.ActionAccumulator.AddFinishedActions(new CreateNodeFrame_Action(Guid.NewGuid(),
- nodes.Select(x => x.Id)));
- }
- public void ConnectProperties(INodePropertyHandler? start, INodePropertyHandler? end)
- {
- if (start == null && end == null) return;
- INodeHandler inputNode = null, outputNode = null;
- string inputProperty = null, outputProperty = null;
- var input = start?.IsInput == true ? start : end;
- var output = start?.IsInput == false ? start : end;
- if (input == null && output != null)
- {
- input = output.ConnectedInputs.FirstOrDefault();
- output = null;
- }
- if (input != null)
- {
- inputNode = input.Node;
- inputProperty = input.PropertyName;
- }
- if (output != null)
- {
- outputNode = output.Node;
- outputProperty = output.PropertyName;
- }
- if (input == null) return;
- IAction action = input != null && output != null
- ? new ConnectProperties_Action(inputNode.Id, outputNode.Id, inputProperty, outputProperty)
- : new DisconnectProperty_Action(inputNode.Id, inputProperty);
- Internals.ActionAccumulator.AddFinishedActions(action);
- }
- }
|