/// <summary> /// Create a new node from reflection data and insert into the Graph. /// </summary> internal void AddNodeFromSearch(Node node, Vector2 screenPosition, PortView connectedPort = null, bool registerUndo = true) { // Calculate where to place this node on the graph var windowRoot = EditorWindow.rootVisualElement; var windowMousePosition = EditorWindow.rootVisualElement.ChangeCoordinatesTo( windowRoot.parent, screenPosition - EditorWindow.position.position ); var graphMousePosition = contentViewContainer.WorldToLocal(windowMousePosition); // Track undo and add to the graph if (registerUndo) { Undo.RegisterCompleteObjectUndo(Graph, $"Add Node {node.Name}"); } node.Position = graphMousePosition; Graph.AddNode(node); serializedGraph.Update(); EditorUtility.SetDirty(Graph); // Add a node to the visual graph var editorType = NodeReflection.GetNodeEditorType(node.GetType()); var element = Activator.CreateInstance(editorType) as NodeView; element.Initialize(node, this, edgeConnectorListener); AddElement(element); // If there was a provided existing port to connect to, find the best // candidate port on the new node and connect. if (connectedPort != null) { var edge = new GraphViewEdge(); if (connectedPort.direction == Direction.Input) { edge.input = connectedPort; edge.output = element.GetCompatibleOutputPort(connectedPort); } else { edge.output = connectedPort; edge.input = element.GetCompatibleInputPort(connectedPort); } AddEdge(edge, false); } Dirty(element); }
public NodeReflectionData(Type type, NodeAttribute nodeAttr) { Type = type; Name = nodeAttr.Name ?? ObjectNames.NicifyVariableName(type.Name); Path = nodeAttr.Path?.Split('/'); Help = nodeAttr.Help; Deletable = nodeAttr.Deletable; Moveable = nodeAttr.Moveable; EditorType = NodeReflection.GetNodeEditorType(type); contextMethods = new Dictionary <ContextMenu, MethodInfo>(); var attrs = type.GetCustomAttributes(true); foreach (var attr in attrs) { if (attr is TagsAttribute tagAttr) { // Load any tags associated with the node Tags.AddRange(tagAttr.Tags); } else if (attr is OutputAttribute output) { // Load any Outputs defined at the class level Ports.Add(new PortReflectionData() { Name = output.Name, Type = output.Type, Direction = PortDirection.Output, Capacity = output.Multiple ? PortCapacity.Multiple : PortCapacity.Single, HasControlElement = false }); } } // Load additional data from class fields AddFieldsFromClass(type); SetScriptNodeType(); SetScriptNodeViewType(); LoadContextMethods(); }
/// <summary> /// Append views for a set of nodes /// </summary> private void AddNodeViews(IEnumerable <Node> nodes, bool selectOnceAdded = false, bool centerOnMouse = false) { // Add views of each node from the graph var nodeMap = new Dictionary <Node, NodeView>(); foreach (var node in nodes) { if (!Graph.Nodes.Contains(node)) { Debug.LogError("Cannot add NodeView: Node is not indexed on the graph"); } else { var editorType = NodeReflection.GetNodeEditorType(node.GetType()); var element = Activator.CreateInstance(editorType) as NodeView; element.Initialize(node, this, edgeConnectorListener); AddElement(element); nodeMap.Add(node, element); Dirty(element); if (selectOnceAdded) { AddToSelection(element); } } } if (centerOnMouse) { var bounds = GetBounds(nodeMap.Values); var worldPosition = contentViewContainer.WorldToLocal(lastMousePosition); var delta = worldPosition - bounds.center; foreach (var node in nodeMap) { node.Value.SetPosition(new Rect(node.Key.Position + delta, Vector2.one)); } } // Sync edges on the graph with our graph's connections // TODO: Deal with trash connections from bad imports // and try to just refactor this whole thing tbh foreach (var node in nodeMap) { foreach (var port in node.Key.Ports.Values) { if (port.Direction == PortDirection.Output) { continue; } foreach (var conn in port.ConnectedPorts) { var connectedNode = conn?.Node; if (connectedNode == null) { Debug.LogError( $"Could not connect `{node.Value.title}:{port.Name}`: " + $"Connected node no longer exists." ); continue; } // Only add if the linked node is in the collection // TODO: This shouldn't be a problem if (!nodeMap.ContainsKey(connectedNode)) { Debug.LogError( $"Could not connect `{node.Value.title}:{port.Name}` -> `{connectedNode.Name}:{conn.Name}`. " + $"Target node does not exist in the NodeView map" ); continue; } var inPort = node.Value.GetInputPort(port.Name); var outPort = nodeMap[connectedNode].GetOutputPort(conn.Name); if (inPort == null) { Debug.LogError( $"Could not connect `{node.Value.title}:{port.Name}` -> `{connectedNode.Name}:{conn.Name}`. " + $"Input port `{port.Name}` no longer exists." ); } else if (outPort == null) { Debug.LogError( $"Could not connect `{connectedNode.Name}:{conn.Name}` to `{node.Value.name}:{port.Name}`. " + $"Output port `{conn.Name}` no longer exists." ); } else { var edge = inPort.ConnectTo(outPort); AddElement(edge); } } } } }
/// <summary> /// Append views for a set of nodes /// </summary> void AddNodeViews(List <AbstractNode> nodes, bool selectOnceAdded = false, bool centerOnMouse = false) { var serializedNodesArr = m_SerializedGraph.FindProperty("nodes"); // Add views of each node from the graph Dictionary <AbstractNode, NodeView> nodeMap = new Dictionary <AbstractNode, NodeView>(); // TODO: Could just be a list with index checking. for (int i = 0; i < nodes.Count; i++) { var node = nodes[i]; var graphIdx = m_Graph.nodes.IndexOf(node); if (graphIdx < 0) { Debug.LogError("Cannot add NodeView: Node is not indexed on the graph"); } else { var serializedNode = serializedNodesArr.GetArrayElementAtIndex(graphIdx); var editorType = NodeReflection.GetNodeEditorType(node.GetType()); var element = Activator.CreateInstance(editorType) as NodeView; element.Initialize(node, serializedNode, m_EdgeListener); AddElement(element); nodeMap.Add(node, element); Dirty(element); if (selectOnceAdded) { AddToSelection(element); } } } if (centerOnMouse) { Rect bounds = GetBounds(nodeMap.Values); Vector2 worldPosition = contentViewContainer.WorldToLocal(m_LastMousePosition); Vector2 delta = worldPosition - bounds.center; foreach (var node in nodeMap) { node.Value.SetPosition(new Rect(node.Key.graphPosition + delta, Vector2.one)); } } // Sync edges on the graph with our graph's connections // TODO: Deal with trash connections from bad imports // and try to just refactor this whole thing tbh foreach (var node in nodeMap) { foreach (var port in node.Key.ports) { if (!port.isInput) { continue; } var connections = port.ConnectedPorts; foreach (var conn in connections) { if (conn.node == null) { Debug.LogError( $"Could not connect `{node.Value.title}:{port.name}`: " + $"Connected node no longer exists." ); continue; } // Only add if the linked node is in the collection if (nodeMap.ContainsKey(conn.node)) { var inPort = node.Value.GetInputPort(port.name); var outPort = nodeMap[conn.node].GetOutputPort(conn.name); if (inPort == null) { Debug.LogError( $"Could not connect `{node.Value.title}:{port.name}` -> `{conn.node.name}:{conn.name}`. " + $"Input port `{port.name}` no longer exists." ); } else if (outPort == null) { Debug.LogError( $"Could not connect `{conn.node.name}:{conn.name}` to `{node.Value.name}:{port.name}`. " + $"Output port `{conn.name}` no longer exists." ); } else { var edge = inPort.ConnectTo(outPort); AddElement(edge); } } } } } }
/// <summary> /// Create a new node from reflection data and insert into the Graph. /// </summary> internal void AddNodeFromReflectionData( NodeReflectionData data, Vector2 screenPosition, PortView connectedPort = null ) { // Calculate where to place this node on the graph var windowRoot = m_EditorWindow.rootVisualElement; var windowMousePosition = m_EditorWindow.rootVisualElement.ChangeCoordinatesTo( windowRoot.parent, screenPosition - m_EditorWindow.position.position ); var graphMousePosition = contentViewContainer.WorldToLocal(windowMousePosition); // Create a new node instance and set initial data (ports, etc) Undo.RegisterCompleteObjectUndo(m_Graph, $"Add Node {data.name}"); Debug.Log($"+node {data.name}"); var node = data.CreateInstance(); node.graphPosition = graphMousePosition; m_Graph.AddNode(node); m_SerializedGraph.Update(); EditorUtility.SetDirty(m_Graph); var serializedNodesArr = m_SerializedGraph.FindProperty("nodes"); var nodeIdx = m_Graph.nodes.IndexOf(node); var serializedNode = serializedNodesArr.GetArrayElementAtIndex(nodeIdx); // Add a node to the visual graph var editorType = NodeReflection.GetNodeEditorType(data.type); var element = Activator.CreateInstance(editorType) as NodeView; element.Initialize(node, serializedNode, m_EdgeListener); AddElement(element); // If there was a provided existing port to connect to, find the best // candidate port on the new node and connect. if (connectedPort != null) { var edge = new Edge(); if (connectedPort.direction == Direction.Input) { edge.input = connectedPort; edge.output = element.GetCompatibleOutputPort(connectedPort); } else { edge.output = connectedPort; edge.input = element.GetCompatibleInputPort(connectedPort); } AddEdge(edge, false); } Dirty(element); }