150 lines
5.1 KiB
C#
150 lines
5.1 KiB
C#
using ImageProcessingGraph.Editor.Windows;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEditor.Callbacks;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using UnityEditor.Experimental.GraphView;
|
|
using Debug = UnityEngine.Debug;
|
|
|
|
// ReSharper disable once CheckNamespace
|
|
namespace ImageProcessingGraph.Editor
|
|
{
|
|
[CreateAssetMenu(menuName = "Image Processing Graph/New Graph")]
|
|
public class ImageProcessingGraphAsset : ScriptableObject
|
|
{
|
|
[SerializeReference] private List<BaseImageNode> nodes;
|
|
[SerializeField] private List<GraphConnection> connections;
|
|
[SerializeField] public List<BaseImageNode> runOrder;
|
|
[SerializeField] public List<StickyNote> stickyNotes;
|
|
|
|
public List<BaseImageNode> Nodes => nodes;
|
|
public List<GraphConnection> Connections => connections;
|
|
public ImageProcessingGraphAsset()
|
|
{
|
|
nodes = new List<BaseImageNode>();
|
|
connections = new List<GraphConnection>();
|
|
}
|
|
|
|
|
|
[OnOpenAsset(1)]
|
|
public static bool OpenGraphAsset(int instanceID, int line)
|
|
{
|
|
var asset = EditorUtility.InstanceIDToObject(instanceID) as ImageProcessingGraphAsset;
|
|
if (asset != null)
|
|
{
|
|
ImageProcessingGraphEditorWindow.Open(asset);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void RunGraph()
|
|
{
|
|
// Create and start the stopwatch to measure time
|
|
Stopwatch stopwatch = new Stopwatch();
|
|
stopwatch.Start();
|
|
|
|
if (runOrder == null)
|
|
runOrder = GetExecutionOrder(this.nodes, this.connections);
|
|
|
|
foreach (var VARIABLE in runOrder)
|
|
{
|
|
VARIABLE.RunNode();
|
|
}
|
|
|
|
// Stop the stopwatch after running the nodes
|
|
stopwatch.Stop();
|
|
|
|
// Log the elapsed time
|
|
UnityEngine.Debug.Log($"Graph execution took {stopwatch.ElapsedMilliseconds} milliseconds.");
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Computes a topological execution order of nodes based on their dependencies.
|
|
/// Throws if a cycle is detected or nodes are missing from the input list.
|
|
/// </summary>
|
|
public List<BaseImageNode> GetExecutionOrder(List<BaseImageNode> nodes, List<GraphConnection> connections, bool debug = false)
|
|
{
|
|
// === Initialization ===
|
|
var nodeMap = new Dictionary<string, BaseImageNode>(nodes.Count);
|
|
var adjList = new Dictionary<string, List<string>>(nodes.Count);
|
|
var inDegree = new Dictionary<string, int>(nodes.Count);
|
|
|
|
foreach (var node in nodes)
|
|
{
|
|
string id = node.ID;
|
|
nodeMap[id] = node;
|
|
adjList[id] = new List<string>();
|
|
inDegree[id] = 0;
|
|
}
|
|
|
|
// === Build Graph ===
|
|
foreach (var conn in connections)
|
|
{
|
|
string from = conn.outputPort.nodeID;
|
|
string to = conn.inputPort.nodeID;
|
|
|
|
if (!nodeMap.ContainsKey(from) || !nodeMap.ContainsKey(to))
|
|
throw new System.Exception($"Invalid connection: '{from}' → '{to}' (One or both node IDs not found)");
|
|
|
|
adjList[from].Add(to);
|
|
inDegree[to]++;
|
|
}
|
|
|
|
if (debug)
|
|
{
|
|
Debug.Log("=== Dependency Graph ===");
|
|
foreach (var from in adjList.Keys)
|
|
{
|
|
var outputs = string.Join(", ", adjList[from].Select(t => $"{nodeMap[t].typeName} ({t})"));
|
|
Debug.Log($"[{nodeMap[from].typeName}] ({from}) → [{outputs}]");
|
|
}
|
|
Debug.Log("=========================");
|
|
}
|
|
|
|
// === Topological Sort (Kahn's Algorithm) ===
|
|
var executionOrder = new List<BaseImageNode>(nodes.Count);
|
|
var queue = new Queue<string>(nodes.Count);
|
|
|
|
foreach (var kvp in inDegree)
|
|
{
|
|
if (kvp.Value == 0)
|
|
queue.Enqueue(kvp.Key);
|
|
}
|
|
|
|
while (queue.Count > 0)
|
|
{
|
|
string currentID = queue.Dequeue();
|
|
executionOrder.Add(nodeMap[currentID]);
|
|
|
|
foreach (var neighbor in adjList[currentID])
|
|
{
|
|
inDegree[neighbor]--;
|
|
if (inDegree[neighbor] == 0)
|
|
queue.Enqueue(neighbor);
|
|
}
|
|
}
|
|
|
|
if (executionOrder.Count != nodes.Count)
|
|
{
|
|
var remaining = string.Join(", ", inDegree.Where(p => p.Value > 0).Select(p => $"{nodeMap[p.Key].typeName} ({p.Key})"));
|
|
throw new System.Exception($"Cycle detected in graph. Remaining nodes: {remaining}");
|
|
}
|
|
|
|
if (debug)
|
|
{
|
|
Debug.Log("=== Execution Order ===");
|
|
foreach (var n in executionOrder)
|
|
Debug.Log($"→ {n.typeName} ({n.ID})");
|
|
}
|
|
|
|
return executionOrder;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
} |