NodeGraph.cs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. using System.Collections;
  2. using System.Diagnostics;
  3. using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
  4. using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
  5. using PixiEditor.ChangeableDocument.Rendering;
  6. using PixiEditor.DrawingApi.Core.Surface.ImageData;
  7. namespace PixiEditor.ChangeableDocument.Changeables.Graph;
  8. public class NodeGraph : IReadOnlyNodeGraph, IDisposable
  9. {
  10. private readonly List<Node> _nodes = new();
  11. public IReadOnlyCollection<Node> Nodes => _nodes;
  12. public OutputNode? OutputNode => Nodes.OfType<OutputNode>().FirstOrDefault();
  13. IReadOnlyCollection<IReadOnlyNode> IReadOnlyNodeGraph.AllNodes => Nodes;
  14. IReadOnlyNode IReadOnlyNodeGraph.OutputNode => OutputNode;
  15. public void AddNode(Node node)
  16. {
  17. if (Nodes.Contains(node))
  18. {
  19. return;
  20. }
  21. _nodes.Add(node);
  22. }
  23. public void RemoveNode(Node node)
  24. {
  25. if (!Nodes.Contains(node))
  26. {
  27. return;
  28. }
  29. _nodes.Remove(node);
  30. }
  31. private Queue<IReadOnlyNode> CalculateExecutionQueue(OutputNode outputNode, bool validate = true)
  32. {
  33. // backwards breadth-first search
  34. var visited = new HashSet<IReadOnlyNode>();
  35. var queueNodes = new Queue<IReadOnlyNode>();
  36. List<IReadOnlyNode> finalQueue = new();
  37. queueNodes.Enqueue(outputNode);
  38. while (queueNodes.Count > 0)
  39. {
  40. var node = queueNodes.Dequeue();
  41. if (!visited.Add(node) || (validate && !node.AreInputsLegal()))
  42. {
  43. continue;
  44. }
  45. finalQueue.Add(node);
  46. foreach (var input in node.InputProperties)
  47. {
  48. if (input.Connection == null)
  49. {
  50. continue;
  51. }
  52. queueNodes.Enqueue(input.Connection.Node);
  53. }
  54. }
  55. finalQueue.Reverse();
  56. return new Queue<IReadOnlyNode>(finalQueue);
  57. }
  58. void IReadOnlyNodeGraph.AddNode(IReadOnlyNode node) => AddNode((Node)node);
  59. void IReadOnlyNodeGraph.RemoveNode(IReadOnlyNode node) => RemoveNode((Node)node);
  60. public void Dispose()
  61. {
  62. foreach (var node in Nodes)
  63. {
  64. node.Dispose();
  65. }
  66. }
  67. public bool TryTraverse(Action<IReadOnlyNode> action)
  68. {
  69. if(OutputNode == null) return false;
  70. var queue = CalculateExecutionQueue(OutputNode, false);
  71. while (queue.Count > 0)
  72. {
  73. var node = queue.Dequeue();
  74. action(node);
  75. }
  76. return true;
  77. }
  78. public Surface? Execute(RenderingContext context)
  79. {
  80. if (OutputNode == null) return null;
  81. var queue = CalculateExecutionQueue(OutputNode);
  82. while (queue.Count > 0)
  83. {
  84. var node = queue.Dequeue();
  85. if (node is Node typedNode)
  86. {
  87. typedNode.ExecuteInternal(context);
  88. }
  89. else
  90. {
  91. node.Execute(context);
  92. }
  93. }
  94. return OutputNode.Input.Value;
  95. }
  96. }