using System.Collections; using System.Diagnostics; using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces; using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes; using PixiEditor.ChangeableDocument.Rendering; using PixiEditor.DrawingApi.Core; namespace PixiEditor.ChangeableDocument.Changeables.Graph; public class NodeGraph : IReadOnlyNodeGraph, IDisposable { private readonly List _nodes = new(); public IReadOnlyCollection Nodes => _nodes; public OutputNode? OutputNode => Nodes.OfType().FirstOrDefault(); IReadOnlyCollection IReadOnlyNodeGraph.AllNodes => Nodes; IReadOnlyNode IReadOnlyNodeGraph.OutputNode => OutputNode; public void AddNode(Node node) { if (Nodes.Contains(node)) { return; } _nodes.Add(node); } public void RemoveNode(Node node) { if (!Nodes.Contains(node)) { return; } _nodes.Remove(node); } public Queue CalculateExecutionQueue(IReadOnlyNode outputNode) { var finalQueue = new HashSet(); var queueNodes = new Queue(); queueNodes.Enqueue(outputNode); while (queueNodes.Count > 0) { var node = queueNodes.Dequeue(); if (finalQueue.Contains(node)) { continue; } bool canAdd = true; foreach (var input in node.InputProperties) { if (input.Connection == null) { continue; } if (finalQueue.Contains(input.Connection.Node)) { continue; } canAdd = false; if (finalQueue.Contains(input.Connection.Node)) { finalQueue.Remove(input.Connection.Node); finalQueue.Add(input.Connection.Node); } if (!queueNodes.Contains(input.Connection.Node)) { queueNodes.Enqueue(input.Connection.Node); } } if (canAdd) { finalQueue.Add(node); } else { queueNodes.Enqueue(node); } } return new Queue(finalQueue); } void IReadOnlyNodeGraph.AddNode(IReadOnlyNode node) => AddNode((Node)node); void IReadOnlyNodeGraph.RemoveNode(IReadOnlyNode node) => RemoveNode((Node)node); public void Dispose() { foreach (var node in Nodes) { node.Dispose(); } } public bool TryTraverse(Action action) { if(OutputNode == null) return false; var queue = CalculateExecutionQueue(OutputNode); while (queue.Count > 0) { var node = queue.Dequeue(); action(node); } return true; } public Texture? Execute(RenderingContext context) { if (OutputNode == null) return null; var queue = CalculateExecutionQueue(OutputNode); while (queue.Count > 0) { var node = queue.Dequeue(); if (node is Node typedNode) { typedNode.ExecuteInternal(context); } else { node.Execute(context); } } return OutputNode.Input.Value; } }