2025-05-17 11:04:41 +01:00

186 lines
6.1 KiB
C#

using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using AssetGraph.Core.GraphElements;
using AssetGraph.Nodes;
using ImageProcessingGraph.Editor;
using UnityEditor.Experimental.GraphView;
using Debug = UnityEngine.Debug;
// ReSharper disable once CheckNamespace
namespace AssetGraph.Core
{
[CreateAssetMenu(menuName = "Image Processing Graph/New Graph")]
public class AssetGraphData : ScriptableObject
{
[SerializeField] private List<AssetGraphNode> nodes;
[SerializeField] private List<GraphConnection> connections;
[SerializeField] public List<AssetGraphNode> runOrder;
[SerializeField] public List<AssetGraphStickyNoteData> stickyNotes;
public List<AssetGraphNode> Nodes => nodes;
public List<GraphConnection> Connections => connections;
public delegate void OnRunEvent();
public event OnRunEvent OnRun;
public OnRunEvent OnRunEnd;
public AssetGraphData()
{
nodes = new List<AssetGraphNode>();
connections = new List<GraphConnection>();
stickyNotes = new List<AssetGraphStickyNoteData>();
}
[OnOpenAsset(1)]
public static bool OpenGraphAsset(int instanceID, int line)
{
var asset = EditorUtility.InstanceIDToObject(instanceID) as AssetGraphData;
if (asset != null)
{
AssetGraphEditorWindow.Open(asset);
return false;
}
return true;
}
public void RunGraph()
{
var bleh = GetAllVariableNodesWithTypes();
OnRun?.Invoke();
// Create and start the stopwatch to measure time
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
runOrder = GetExecutionOrder(this.nodes, this.connections);
foreach (var VARIABLE in runOrder)
{
if (!VARIABLE.RunNode())
{
break;
}
}
foreach (var VARIABLE in runOrder)
{
VARIABLE.RunCleanUp();
}
stopwatch.Stop();
// Log the elapsed time
UnityEngine.Debug.Log($"Graph execution took {stopwatch.ElapsedMilliseconds} milliseconds.");
AssetDatabase.Refresh();
}
public List<(AssetGraphNode node, System.Type type)> GetAllVariableNodesWithTypes()
{
var result = new List<(AssetGraphNode node, System.Type type)>();
foreach (var node in nodes)
{
var nodeType = node.GetType();
var baseType = nodeType.BaseType;
if (baseType != null && baseType.IsGenericType)
{
if (baseType.GetGenericTypeDefinition() == typeof(VariableNode<>))
{
var genericArgument = baseType.GetGenericArguments()[0];
result.Add((node, genericArgument));
}
}
}
return result;
}
public List<AssetGraphNode> GetExecutionOrder(List<AssetGraphNode> nodes, List<GraphConnection> connections, bool debug = false)
{
// === Initialization ===
var nodeMap = new Dictionary<string, AssetGraphNode>(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<AssetGraphNode>(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;
}
}
}