2025-05-17 12:34:37 +01:00

616 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AssetGraph.Core.Attributes;
using AssetGraph.Core.GraphElements;
using AssetGraph.Nodes;
using ImageProcessingGraph.Editor;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
namespace AssetGraph.Core
{
public class AssetGraphViewWindow : GraphView
{
internal AssetGraphData asset;
private SerializedObject serializedObject;
private AssetGraphEditorWindow window;
public AssetGraphEditorWindow Window => window;
public List<AssetGraphNodeEditor> graphNodes;
public Dictionary<string, AssetGraphNodeEditor> nodeDictionary;
public Dictionary<Edge, GraphConnection> connectionDictionary;
public Dictionary<string, AssetGraphStickyNote> stickyNoteDictionary;
internal AssetGraphSearchProvider searchProvider;
public AssetGraphEdgeConnector edgeConnectorListener;
private bool isDropdownEnabled = false;
public Vector2 cachedMousePos;
private Button debugModeButton;
private Button outputRunOrder;
private Button makeStickyNode;
public AssetGraphViewWindow(SerializedObject obeject, AssetGraphEditorWindow window)
{
this.serializedObject = obeject;
this.asset = obeject.targetObject as AssetGraphData;
this.graphNodes = new List<AssetGraphNodeEditor>();
nodeDictionary = new Dictionary<string, AssetGraphNodeEditor>();
stickyNoteDictionary = new Dictionary<string, AssetGraphStickyNote>();
connectionDictionary = new Dictionary<Edge, GraphConnection>();
searchProvider = ScriptableObject.CreateInstance<AssetGraphSearchProvider>();
searchProvider.graph = this;
edgeConnectorListener = new AssetGraphEdgeConnector(this);
this.nodeCreationRequest = ShowSearchWindow;
this.window = window;
StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Unity Image Processing/GraphView.uss");
if (styleSheet == null)
{
styleSheet = EditorGUIUtility.Load("Packages/com.chromium.imageprocessingrah/GraphView.uss") as StyleSheet;
}
styleSheets.Add(styleSheet);
GridBackground background = new GridBackground();
background.name = "Grid";
this.Add(background);
background.SendToBack();
CreateButtons();
this.AddManipulator(new ContentDragger());
this.AddManipulator(new SelectionDragger());
this.AddManipulator(new RectangleSelector());
this.AddManipulator(new ClickSelector());
this.AddManipulator(new ContentZoomer() );
DrawNodes();
DrawConnections();
DrawStickyNotes();
graphViewChanged += OnGraphViewChanged;
Undo.undoRedoPerformed += UndoRedoPerformed;
RegisterCallback<MouseMoveEvent>(OnMouseMove);
}
private void OnMouseMove(MouseMoveEvent evt)
{
cachedMousePos = evt.localMousePosition;
}
private void UndoRedoPerformed()
{
DrawNodes();
DrawConnections();
DrawStickyNotes();
}
private void CreateButtons()
{
#region Run Button
Button runButton = new Button
{
text = "Run",
style =
{
width = 100,
height = 40,
position = Position.Absolute,
top = 10,
left = 10
}
};
runButton.clicked += () =>
{
asset.RunGraph();
};
Add(runButton);
#endregion
#if false
debugModeButton = new Button
{
text = "Debug Mode",
style =
{
width = 120,
height = 40,
position = Position.Absolute,
top = 55,
right = 10
}
};
debugModeButton.clicked += () =>
{
};
#endif
outputRunOrder = new Button
{
text = "Recalculate Run Order",
style =
{
width = 120,
height = 40,
position = Position.Absolute,
top = 55,
right = 10,
fontSize = 9
}
};
outputRunOrder.clicked += () =>
{
asset.runOrder = asset.GetExecutionOrder(asset.Nodes, asset.Connections);
};
makeStickyNode = new Button
{
text = "Make Sticky Node",
style =
{
width = 120,
height = 40,
position = Position.Absolute,
top = 55+45,
right = 10,
fontSize = 9
}
};
makeStickyNode.clicked += () =>
{
var noteData = new AssetGraphStickyNoteData("");
asset.stickyNotes.Add(noteData);
this.DrawStickyNote(noteData);
};
Button dropdownButton = new Button
{
text = "Options",
style =
{
width = 120,
height = 40,
position = Position.Absolute,
top = 10,
right = 10
}
};
dropdownButton.clicked+= () =>
{
if (!isDropdownEnabled)
{
#if false
Add(debugModeButton);
#endif
Add(outputRunOrder);
Add(makeStickyNode);
}
else
{
#if false
Remove(debugModeButton);
#endif
Remove(outputRunOrder);
Remove(makeStickyNode);
}
isDropdownEnabled = !isDropdownEnabled;
};
Add(dropdownButton);
}
private AssetGraphNodeEditor GetNode(string NodeID)
{
AssetGraphNodeEditor node = null;
nodeDictionary.TryGetValue(NodeID, out node);
return node;
}
public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
{
List<Port> compatiblePorts = new List<Port>();
foreach (var node in graphNodes)
{
if (node == startPort.node)
continue;
var ports = node.inputContainer.Children()
.Concat(node.outputContainer.Children())
.OfType<Port>();
foreach (var port in ports)
{
if (startPort.direction == port.direction)
continue;
Type typeA = GetPortEffectiveType(startPort);
Type typeB = GetPortEffectiveType(port);
// if (typeA == typeof(object))
// {
// var BaseGenPort = startPort.node as ImageProcessingGraphNodeVisual;
// var GenPort = BaseGenPort.GraphNode as GenericConnection;
//
// if (GenPort.InternalType != null)
// {
// if(!GenPort.InternalType.IsAssignableFrom(typeB))
// continue;
// }
// }
//
// if (typeB == typeof(object))
// {
// var BaseGenPort = port.node as ImageProcessingGraphNodeVisual;
// var GenPort = BaseGenPort.GraphNode as GenericConnection;
//
// if (GenPort.InternalType != null)
// {
// if(!GenPort.InternalType.IsAssignableFrom(typeA))
// continue;
// }
// }
if (!typeA.IsAssignableFrom(typeB) && !typeB.IsAssignableFrom(typeA))
continue;
if (startPort.direction == Direction.Output && CreatesCycle(startPort, port))
continue;
if (startPort.direction == Direction.Input && CreatesCycle(port, startPort))
continue;
compatiblePorts.Add(port);
}
}
return compatiblePorts;
}
private Type GetPortEffectiveType(Port port)
{
if (port is AssetGraphPort iptPort)
{
if (iptPort.node is AssetGraphNodeEditor vis &&
vis.GraphNode is GenericConnection conn &&
conn.InternalType != null)
{
return conn.InternalType;
}
return port.portType ?? typeof(object);
}
return port.portType ?? typeof(object);
}
private GraphViewChange OnGraphViewChanged(GraphViewChange graphviewchange)
{
if (graphviewchange.movedElements != null)
{
Undo.RecordObject(serializedObject.targetObject, "Moved Graph Elements");
foreach (var VARIABLE in graphviewchange.movedElements.OfType<AssetGraphNodeEditor>())
{
VARIABLE.SavePosition();
}
foreach (var VARIABLE in graphviewchange.movedElements.OfType<AssetGraphStickyNote>())
{
VARIABLE.SavePosition();
}
}
if (graphviewchange.elementsToRemove != null)
{
List<AssetGraphNodeEditor> nodesToRemove = graphviewchange.elementsToRemove.OfType<AssetGraphNodeEditor>().ToList();
List<Edge> edges = graphviewchange.elementsToRemove.OfType<Edge>().ToList();
if (nodesToRemove.Count > 0)
{
Undo.RecordObject(serializedObject.targetObject, "Remove Node");
foreach (var VARIABLE in nodesToRemove)
{
RemoveNode(VARIABLE);
}
}
if (edges.Count > 0)
{
Undo.RecordObject(serializedObject.targetObject, "Remove Edge");
foreach (var VARIABLE in edges)
{
RemoveEdge(VARIABLE);
}
}
foreach (var VARIABLE in graphviewchange.elementsToRemove.OfType<Edge>())
{
RemoveEdge(VARIABLE);
}
}
if (graphviewchange.edgesToCreate != null)
{
foreach (Edge edge in graphviewchange.edgesToCreate)
{
Undo.RecordObject(serializedObject.targetObject, "Created Connection");
CreateEdge(edge);
}
}
return graphviewchange;
}
#region Edges
public void CreateEdge(Edge edge)
{
AssetGraphNodeEditor outputNode = (AssetGraphNodeEditor)edge.output.node;
AssetGraphNodeEditor inputNode = (AssetGraphNodeEditor)edge.input.node;
int outputIndex = outputNode.OutputPorts.IndexOf(edge.output);
int inputIndex = inputNode.InputPorts.IndexOf(edge.input);
string inputType = inputNode.GraphNode.GetType().Name;
string outputType = outputNode.GraphNode.GetType().Name;
GraphConnection connection = new GraphConnection(inputNode.GraphNode.ID,inputIndex, inputType,outputNode.GraphNode.ID, outputIndex, outputType, edge);
edge.output.Connect(edge);
edge.input.Connect(edge);
AssetGraphPort portIn = (AssetGraphPort)edge.output;
AssetGraphPort portOut = (AssetGraphPort)edge.output;
if (portIn.fieldInfo != null)
inputNode.ToggleExposedVariable(edge.input, false);
asset.Connections.Add(connection);
connectionDictionary.Add(edge, connection);
}
private void RemoveEdge(Edge edge)
{
if (connectionDictionary.TryGetValue(edge, out GraphConnection connection))
{
asset.Connections.Remove(connection);
connectionDictionary.Remove(edge);
edge.output.Disconnect(edge);
edge.input.Disconnect(edge);
AssetGraphNodeEditor inputNode = (AssetGraphNodeEditor)edge.input.node;
AssetGraphPort portIn = (AssetGraphPort)edge.input;
if(portIn.fieldInfo != null)
inputNode.ToggleExposedVariable(edge.input, true);
}
}
private void DrawConnections()
{
foreach (KeyValuePair<Edge, GraphConnection> node in connectionDictionary)
{
RemoveElement(node.Key);
}
connectionDictionary.Clear();
if (asset.Connections != null)
{
foreach (GraphConnection conn in asset.Connections)
{
AssetGraphNodeEditor inputNode = GetNode(conn.inputPort.nodeID);
AssetGraphNodeEditor outputNode = GetNode(conn.outputPort.nodeID);
if (inputNode != null && outputNode != null)
{
AssetGraphPort inPort = inputNode.InputPorts[conn.inputPort.portID] as AssetGraphPort;
AssetGraphPort outPort = outputNode.OutputPorts[conn.outputPort.portID] as AssetGraphPort;
Edge edge = inPort.ConnectTo(outPort);
AddElement(edge);
connectionDictionary.Add(edge, conn);
conn.SetInternalEdge(edge);
((AssetGraphNodeEditor)inPort.node).ToggleExposedVariable(inPort, false);
}
}
}
}
private bool DoesConversionNodeExist()
{
return false;
}
#region Cycle Logic
private bool CreatesCycle(Port fromPort, Port toPort)
{
var visited = new HashSet<Node>();
return HasPathTo(toPort.node, fromPort.node, visited);
}
private bool HasPathTo(Node current, Node target, HashSet<Node> visited)
{
if (current == target)
return true;
if (visited.Contains(current))
return false;
visited.Add(current);
foreach (var outputPort in current.outputContainer.Children().OfType<Port>())
{
foreach (var edge in outputPort.connections)
{
var nextNode = edge.input.node;
if (HasPathTo(nextNode, target, visited))
return true;
}
}
return false;
}
#endregion
#endregion
#region Nodes
private void DrawNodes()
{
foreach (AssetGraphNodeEditor node in graphNodes)
{
RemoveElement(node);
}
graphNodes.Clear();
foreach (KeyValuePair<string, AssetGraphNodeEditor> node in nodeDictionary)
{
RemoveElement(node.Value);
}
nodeDictionary.Clear();
foreach (var variable in asset.Nodes)
{
AddNodeToGraph(variable);
}
}
private void AddNodeToGraph(AssetGraphNode node)
{
node.typeName = node.GetType().AssemblyQualifiedName;
var infoAttr = node.GetType().GetCustomAttribute<NodeInfoAttribute>();
AssetGraphNodeEditor editorNode = null;
if (typeof(AssetGraphNodeEditor).IsAssignableFrom(infoAttr.EditorType))
{
editorNode = (AssetGraphNodeEditor)Activator.CreateInstance(infoAttr.EditorType, node, this);
}
else
{
editorNode = new AssetGraphNodeEditor(node, this);
}
editorNode.SetPosition(node.Position);
graphNodes.Add(editorNode);
nodeDictionary.Add(node.ID, editorNode);
AddElement(editorNode);
}
public void Add(AssetGraphNode node)
{
Undo.RecordObject(serializedObject.targetObject, "Added Node");
asset.Nodes.Add(node);
serializedObject.Update();
AddNodeToGraph(node);
}
private void RemoveNode(AssetGraphNodeEditor variable)
{
List<GraphConnection> connectionsToRemove = new List<GraphConnection>();
foreach (var connection in asset.Connections)
{
if (connection.inputPort.nodeID == variable.GraphNode.ID || connection.outputPort.nodeID == variable.GraphNode.ID)
connectionsToRemove.Add(connection);
}
foreach (var connection in connectionsToRemove)
{
asset.Connections.Remove(connection);
}
asset.Nodes.Remove(variable.GraphNode);
nodeDictionary.Remove(variable.GraphNode.ID);
graphNodes.Remove(variable);
serializedObject.Update();
}
#endregion
#region Search Window
private void ShowSearchWindow(NodeCreationContext obj)
{
searchProvider.target = (VisualElement)focusController.focusedElement;
SearchWindow.Open(new SearchWindowContext(obj.screenMousePosition), searchProvider);
}
#endregion
#region Sticky Note
public void DrawStickyNotes()
{
foreach (var stick in asset.stickyNotes)
{
this.stickyNoteDictionary.Add(stick.GUID, DrawStickyNote(stick));
}
}
public AssetGraphStickyNote DrawStickyNote(AssetGraphStickyNoteData stickyNote)
{
AssetGraphStickyNote stickyNoteVisual = new AssetGraphStickyNote(stickyNote, this);
stickyNoteVisual.SetPosition(stickyNote.Position);
stickyNoteVisual.style.width = stickyNote.Size.x;
stickyNoteVisual.style.height = stickyNote.Size.y;
this.Add(stickyNoteVisual);
return stickyNoteVisual;
}
#endregion
}
}