UnityImageProcessing_Package/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs
2025-04-26 19:40:12 +01:00

343 lines
12 KiB
C#

using System.Collections.Generic;
using System.Linq;
using ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows;
using ImageProcessingGraph.Editor.Windows;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
namespace ImageProcessingGraph.Editor
{
public class ImageProcessingGraphViewWindow : GraphView
{
internal ImageProcessingGraphAsset asset;
private SerializedObject serializedObject;
private ImageProcessingGraphEditorWindow window;
public ImageProcessingGraphEditorWindow Window => window;
public List<ImageProcessingGraphNodeVisual> graphNodes;
public Dictionary<string, ImageProcessingGraphNodeVisual> nodeDictionary;
public Dictionary<Edge, GraphConnection> connectionDictionary;
internal ImageProcessingGraphSearchProvider searchProvider;
internal ImageProcessingGraphEdgeConnectorListener edgeConnectorListener;
public ImageProcessingGraphViewWindow(SerializedObject obeject, ImageProcessingGraphEditorWindow window)
{
this.serializedObject = obeject;
this.asset = obeject.targetObject as ImageProcessingGraphAsset;
this.graphNodes = new List<ImageProcessingGraphNodeVisual>();
nodeDictionary = new Dictionary<string, ImageProcessingGraphNodeVisual>();
connectionDictionary = new Dictionary<Edge, GraphConnection>();
searchProvider = ScriptableObject.CreateInstance<ImageProcessingGraphSearchProvider>();
searchProvider.graph = this;
edgeConnectorListener = new ImageProcessingGraphEdgeConnectorListener(this);
this.nodeCreationRequest = ShowSearchWindow;
this.window = window;
StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Unity Image Processing/GraphView.uss");
styleSheets.Add(styleSheet);
GridBackground background = new GridBackground();
background.name = "Grid";
Add(background);
background.SendToBack();
this.AddManipulator(new ContentDragger());
this.AddManipulator(new SelectionDragger());
this.AddManipulator(new RectangleSelector());
this.AddManipulator(new ClickSelector());
this.AddManipulator(new ContentZoomer() );
DrawNodes();
DrawConnections();
graphViewChanged += OnGraphViewChanged;
Undo.undoRedoEvent += UndoEvent;
}
private ImageProcessingGraphNodeVisual GetNode(string NodeID)
{
ImageProcessingGraphNodeVisual 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)
{
// Prevent connections to self
if (node == startPort.node)
continue;
foreach (var port in node.inputContainer.Children().Concat(node.outputContainer.Children()).OfType<Port>())
{
// Prevent connecting input to input or output to output
if (port.direction == startPort.direction)
continue;
if (port.portType != startPort.portType)
{
if (DoesConversionNodeExist())
{
}
else
continue;
}
// Prevent connection if it creates a cycle
if (startPort.direction == Direction.Output && CreatesCycle(startPort, port))
continue;
if (startPort.direction == Direction.Input && CreatesCycle(port, startPort))
continue;
compatiblePorts.Add(port);
}
}
return compatiblePorts;
}
private void UndoEvent(in UndoRedoInfo undo)
{
DrawNodes();
}
private GraphViewChange OnGraphViewChanged(GraphViewChange graphviewchange)
{
if (graphviewchange.movedElements != null)
{
Undo.RecordObject(serializedObject.targetObject, "Moved Graph Elements");
foreach (var VARIABLE in graphviewchange.movedElements.OfType<ImageProcessingGraphNodeVisual>())
{
VARIABLE.SavePosition();
}
}
if (graphviewchange.elementsToRemove != null)
{
List<ImageProcessingGraphNodeVisual> nodesToRemove = graphviewchange.elementsToRemove.OfType<ImageProcessingGraphNodeVisual>().ToList();
if (nodesToRemove.Count > 0)
{
Undo.RecordObject(serializedObject.targetObject, "Remove Node");
foreach (var VARIABLE in nodesToRemove)
{
RemoveNode(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
void CreateEdge(Edge edge)
{
ImageProcessingGraphNodeVisual outputNode = (ImageProcessingGraphNodeVisual)edge.output.node;
ImageProcessingGraphNodeVisual inputNode = (ImageProcessingGraphNodeVisual)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);
asset.Connections.Add(connection);
}
private void RemoveEdge(Edge variable)
{
if (connectionDictionary.TryGetValue(variable, out GraphConnection connection))
{
asset.Connections.Remove(connection);
connectionDictionary.Remove(variable);
}
}
private void DrawConnections()
{
if (asset.Connections != null)
{
foreach (GraphConnection conn in asset.Connections)
{
ImageProcessingGraphNodeVisual inputNode = GetNode(conn.inputPort.nodeID);
ImageProcessingGraphNodeVisual outputNode = GetNode(conn.outputPort.nodeID);
if (inputNode != null && outputNode != null)
{
Port inPort = inputNode.InputPorts[conn.inputPort.portID];
Port outPort = outputNode.OutputPorts[conn.outputPort.portID];
Edge edge = inPort.ConnectTo(outPort);
AddElement(edge);
connectionDictionary.Add(edge, conn);
}
}
}
}
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 (ImageProcessingGraphNodeVisual node in graphNodes)
{
RemoveElement(node);
}
graphNodes.Clear();
foreach (KeyValuePair<string, ImageProcessingGraphNodeVisual> node in nodeDictionary)
{
RemoveElement(node.Value);
}
nodeDictionary.Clear();
foreach (var variable in asset.Nodes)
{
AddNodeToGraph(variable);
}
}
private void AddNodeToGraph(BaseImageNode node)
{
node.typeName = node.GetType().AssemblyQualifiedName;
ImageProcessingGraphNodeVisual editorNode = new ImageProcessingGraphNodeVisual(node, this);
editorNode.SetPosition(node.Position);
graphNodes.Add(editorNode);
nodeDictionary.Add(node.ID, editorNode);
AddElement(editorNode);
}
public void Add(BaseImageNode node)
{
Undo.RecordObject(serializedObject.targetObject, "Added Node");
asset.Nodes.Add(node);
serializedObject.Update();
AddNodeToGraph(node);
}
private void RemoveNode(ImageProcessingGraphNodeVisual 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
}
}