///// <summary> ///// Fetches every node connection declaration for each node type for later use ///// </summary> //public static void FetchNodeConnectionDeclarations() //{ // nodePortDeclarations = NodeSystemSetting.Inst.NodePortDec; //} /// <summary> /// Updates all node connection ports in the given node and creates or adjusts them according to the declaration /// </summary> public static void UpdateConnectionPorts(Node node) { foreach (ConnectionPortDeclaration portDecl in GetPortDeclarationEnumerator(node, true)) { ConnectionPort port = (ConnectionPort)portDecl.portField.GetValue(node); if (port == null) { // Create new port from declaration port = portDecl.portInfo.CreateNew(node); portDecl.portField.SetValue(node, port); } else { // Check port values against port declaration portDecl.portInfo.UpdateProperties(port); } } }
/// <summary> /// Removes the connection of this port to the specified port if existant /// </summary> public void RemoveConnection(ConnectionPort port, bool silent = false) { Undo.RecordObjects(new [] { this, port }, "NodeEditor_连接移除保存"); if (port == null) { connections.RemoveAll(p => p != null); return; } if (!silent) { NodeEditorCallbacks.IssueOnRemoveConnection(this, port); } port.connections.Remove(this); connections.Remove(port); }
/// <summary> /// Returns whether every direct ancestor has been calculated /// </summary> public bool ancestorsCalculated() { for (int i = 0; i < inputPorts.Count; i++) { ConnectionPort port = inputPorts[i]; for (int t = 0; t < port.connections.Count; t++) { if (!port.connections[t].body.calculated) { return(false); } } } return(true); }
public virtual bool IsCompatibleWith(ConnectionPort port) { if (port.GetType() != ConnectionType) { return(false); } if (port.styleID != StyleID) { return(false); } if (!(Direction == Direction.None && port.direction == Direction.None) && !(Direction == Direction.In && port.direction == Direction.Out) && !(Direction == Direction.Out && port.direction == Direction.In)) { return(false); } return(true); }
public static void IssueOnRemoveConnection(ConnectionPort port1, ConnectionPort port2) { if (OnRemoveConnection != null) { OnRemoveConnection.Invoke(port1, port2); } for (int cnt = 0; cnt < receiverCount; cnt++) { if (callbackReceiver [cnt] == null) { callbackReceiver.RemoveAt(cnt--); } else { callbackReceiver [cnt].OnRemoveConnection(port1, port2); } } }
public static void IssueOnAddConnectionPort(ConnectionPort connectionPort) { if (OnAddConnectionPort != null) { OnAddConnectionPort.Invoke(connectionPort); } for (int cnt = 0; cnt < receiverCount; cnt++) { if (callbackReceiver [cnt] == null) { callbackReceiver.RemoveAt(cnt--); } else { callbackReceiver [cnt].OnAddConnectionPort(connectionPort); } } }
/// <summary> /// Returns all node IDs that can automatically connect to the specified port. /// If port is null, all node IDs are returned. /// </summary> public static List <string> getCompatibleNodes(ConnectionPort port) { if (port == null) { return(NodeTypes.nodes.Keys.ToList()); } List <string> compatibleNodes = new List <string> (); foreach (NodeTypeData nodeData in NodeTypes.nodes.Values) { // Iterate over all nodes to check compability of any of their connection ports if (ConnectionPortManager.GetPortDeclarations(nodeData.typeID).Any( (ConnectionPortDeclaration portDecl) => portDecl.portInfo.IsCompatibleWith(port))) { compatibleNodes.Add(nodeData.typeID); } } return(compatibleNodes); }
public override bool IsCompatibleWith(ConnectionPort port) { if (!(Direction == Direction.None && port.direction == Direction.None) && !(Direction == Direction.In && port.direction == Direction.Out) && !(Direction == Direction.Out && port.direction == Direction.In)) { return(false); } ValueConnectionKnob valueKnob = port as ValueConnectionKnob; if (valueKnob == null) { return(false); } Type knobType = ConnectionPortStyles.GetValueType(StyleID); return(knobType.IsAssignableFrom(valueKnob.valueType)); }
public static void ReinstateNode(Node node, List <ConnectionPort> connectedPorts) { if (node == null) { // Not critical. Happens when undo records are triggered, but it's canvas has been unloaded // Debug.LogWarning("Lost reference to Undo SO 'node'!"); return; } if (connectedPorts == null) // Should not happen if node has not been lost { Debug.LogWarning("Lost reference to Undo SO 'connectedPorts'!"); } node.canvas.nodes.Remove(node); node.canvas.nodes.Add(node); ConnectionPortManager.UpdateConnectionPorts(node); if (connectedPorts != null) { int portIndex = 0; for (int i = 0; i < connectedPorts.Count; i++) { if (connectedPorts[i] == null) { // 'Decode' null-seperated list portIndex++; continue; } if (node.connectionPorts.Count <= portIndex) { Debug.LogWarning("Misaligned port count in reinstated node!"); break; } ConnectionPort port1 = node.connectionPorts[portIndex], port2 = connectedPorts[i]; // Workaround for undo records created in playmode that have being deleted (by Unity) after exiting if (port1.body.canvas.nodes.Contains(port1.body) && port2.body.canvas.nodes.Contains(port2.body)) { port1.TryApplyConnection(port2, true); } } if (node.connectionPorts.Count > portIndex) { Debug.LogWarning("Misaligned port count in reinstated node!"); } } }
/// <summary> /// Removes the connection of this port to the specified port if existant /// </summary> public void RemoveConnection(ConnectionPort port, bool silent = false) { if (port == null) { connections.RemoveAll(p => p != null); return; } if (!silent) { NodeEditorCallbacks.IssueOnRemoveConnection(this, port); } port.connections.Remove(this); connections.Remove(port); if (!silent) { body.canvas.OnNodeChange(body); } }
/// <summary> /// Determines whether a connection can be applied between this port and the specified port /// </summary> public virtual bool CanApplyConnection(ConnectionPort port) { if (port == null || body == port.body || connections.Contains(port)) { return(false); } if (direction == Direction.None && port.direction == Direction.None) { return(true); // None-Directive connections can always connect } if (direction == Direction.In && port.direction != Direction.Out) { return(false); // Cannot connect inputs with anything other than outputs } if (direction == Direction.Out && port.direction != Direction.In) { return(false); // Cannot connect outputs with anything other than inputs } if (!body.canvas.allowRecursion) { // Assure no loop would be created bool loop; if (direction == Direction.Out) { loop = body.isChildOf(port.body); } else { loop = port.body.isChildOf(body); } if (loop) { // Loop would be created, not allowed Debug.LogWarning("Cannot apply connection: Recursion detected!"); return(false); } } return(true); }
/// <summary> /// Applies a connection between between this port and the specified port. /// 'CanApplyConnection' has to be checked before to avoid interferences! /// </summary> public void ApplyConnection(ConnectionPort port, bool silent = false) { if (port == null) { return; } if (maxConnectionCount == ConnectionCount.Single && connections.Count > 0) { // Respect maximum connection count on this port RemoveConnection(connections[0], silent); connections.Clear(); } connections.Add(port); if (port.maxConnectionCount == ConnectionCount.Single && port.connections.Count > 0) { // Respect maximum connection count on the other port port.RemoveConnection(port.connections[0], silent); port.connections.Clear(); } port.connections.Add(this); #if UNITY_EDITOR if (!silent) { // Create Undo record // Important: Copy variables used within anonymous functions within same level (this if block) for correct serialization! ConnectionPort port1 = this, port2 = port; UndoPro.UndoProManager.RecordOperation( () => NodeEditorUndoActions.CreateConnection(port1, port2), () => NodeEditorUndoActions.DeleteConnection(port1, port2), "Create Connection"); } #endif if (!silent) { // Callbacks port.body.OnAddConnection(port, this); body.OnAddConnection(this, port); NodeEditorCallbacks.IssueOnAddConnection(this, port); body.canvas.OnNodeChange(direction == Direction.In? port.body : body); } }
/// <summary> /// Recursively checks whether this node is in a loop /// </summary> internal bool isInLoop() { if (BeginRecursiveSearchLoop()) { return(this == startRecursiveSearchNode); } for (int i = 0; i < inputPorts.Count; i++) { ConnectionPort port = inputPorts[i]; for (int t = 0; t < port.connections.Count; t++) { if (port.connections[t].body.isInLoop()) { StopRecursiveSearchLoop(); return(true); } } } EndRecursiveSearchLoop(); return(false); }
/// <summary> /// Determines whether a connection can be applied between this port and the specified port /// </summary> public virtual bool CanApplyConnection(ConnectionPort port) { if (port == null || body == port.body || connections.Contains(port)) { return(false); } if (direction == Direction.None && port.direction == Direction.None) { return(true); // None-Directive connections can always connect } if (direction == Direction.In && port.direction != Direction.Out) { return(false); // Cannot connect inputs with anything other than outputs } if (direction == Direction.Out && port.direction != Direction.In) { return(false); // Cannot connect outputs with anything other than inputs } if (styleID != port.styleID) { return(false); // Cannot connect two different styles } if (direction == Direction.Out) // Let inputs handle checks for recursion { return(port.CanApplyConnection(this)); } if (port.body.isChildOf(body)) { // Recursive if (!port.body.allowsLoopRecursion(body)) { // TODO: Generic Notification Debug.LogWarning("Cannot apply connection: Recursion detected!"); return(false); } } return(true); }
/// <summary> /// Removes the connection of this port to the specified port if existant /// </summary> public void RemoveConnection(ConnectionPort port, bool silent = false) { #if UNITY_EDITOR if (silent == false && port != null) { // Undo record // Important: Copy variables used within anonymous functions within same level (this if block) for correct serialization! ConnectionPort port1 = this, port2 = port; UndoPro.UndoProManager.RecordOperation( () => NodeEditorUndoActions.DeleteConnection(port1, port2), () => NodeEditorUndoActions.CreateConnection(port1, port2), "Delete Connection"); } #endif if (port == null) { Debug.LogWarning("Cannot delete null port!"); //connections.RemoveAll (p => p != null); return; } if (!silent) { port.body.OnRemoveConnection(port, this); this.body.OnRemoveConnection(this, port); NodeEditorCallbacks.IssueOnRemoveConnection(this, port); } port.connections.Remove(this); connections.Remove(port); if (!silent) { body.canvas.OnNodeChange(body); } }
/// <summary> /// Returns an enumerator of type ConnectionPortDeclaration over all port declarations of the given node /// </summary> public static IEnumerable GetPortDeclarationEnumerator(Node node, bool triggerUpdate = false) { List <ConnectionPort> declaredConnectionPorts = new List <ConnectionPort> (); ConnectionPortDeclaration[] portDecls; if (nodePortDeclarations.TryGetValue(node.GetID, out portDecls)) { foreach (ConnectionPortDeclaration portDecl in portDecls) { // Iterate over each connection port and yield it's declaration yield return(portDecl); ConnectionPort port = (ConnectionPort)portDecl.portField.GetValue(node); if (port != null) { declaredConnectionPorts.Add(port); } } } if (triggerUpdate) { // Update lists as values might have changes when calling this function node.staticConnectionPorts = declaredConnectionPorts; UpdateRepresentativePortLists(node); } }
/// <summary> /// Creates a node of the specified ID at pos on the current canvas, optionally auto-connecting the specified output to a matching input /// </summary> public static Node Create(string nodeID, Vector2 pos, ConnectionPort connectingPort = null, bool silent = false, bool init = true) { return(Create(nodeID, pos, NodeEditor.curNodeCanvas, connectingPort, silent, init)); }
/// <summary> /// Callback when the given port on this node was assigned a new connection /// </summary> protected internal virtual void OnAddConnection(ConnectionPort port, ConnectionPort connection) { }
/// <summary> /// Creates a working copy of the specified nodeCanvas, and optionally also of it's associated editorStates. /// This breaks the link of this object to any stored assets and references. That means, that all changes to this object will have to be explicitly saved. /// </summary> public static NodeCanvas CreateWorkingCopy(NodeCanvas nodeCanvas, bool editorStates = true) { if (nodeCanvas == null) { return(null); } // Lists holding initial and cloned version of each ScriptableObject for later replacement of references List <ScriptableObject> allSOs = new List <ScriptableObject> (); List <ScriptableObject> clonedSOs = new List <ScriptableObject> (); System.Func <ScriptableObject, ScriptableObject> copySOs = (ScriptableObject so) => ReplaceSO(allSOs, clonedSOs, so); // Clone and enter the canvas object and it's referenced SOs nodeCanvas = AddClonedSO(allSOs, clonedSOs, nodeCanvas); AddClonedSOs(allSOs, clonedSOs, nodeCanvas.GetScriptableObjects()); // Iterate over the core ScriptableObjects in the canvas and clone them for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++) { Node node = nodeCanvas.nodes[nodeCnt]; // Clone Node and additional scriptableObjects Node clonedNode = AddClonedSO(allSOs, clonedSOs, node); AddClonedSOs(allSOs, clonedSOs, clonedNode.GetScriptableObjects()); // Update representative port list connectionPorts with all ports and clone them ConnectionPortManager.UpdatePortLists(clonedNode); AddClonedSOs(allSOs, clonedSOs, clonedNode.connectionPorts); } // Replace every reference to any of the initial SOs of the first list with the respective clones of the second list nodeCanvas.CopyScriptableObjects(copySOs); for (int nodeCnt = 0; nodeCnt < nodeCanvas.nodes.Count; nodeCnt++) { // Replace SOs in all Nodes Node node = nodeCanvas.nodes[nodeCnt]; // Replace node and additional ScriptableObjects with their copies Node clonedNode = nodeCanvas.nodes[nodeCnt] = ReplaceSO(allSOs, clonedSOs, node); clonedNode.CopyScriptableObjects(copySOs); // Replace ConnectionPorts foreach (ConnectionPortDeclaration portDecl in ConnectionPortManager.GetPortDeclarationEnumerator(clonedNode, true)) { // Iterate over each static port declaration and replace it with it's connections ConnectionPort port = (ConnectionPort)portDecl.portField.GetValue(clonedNode); port = ReplaceSO(allSOs, clonedSOs, port); for (int c = 0; c < port.connections.Count; c++) { port.connections[c] = ReplaceSO(allSOs, clonedSOs, port.connections[c]); } port.body = clonedNode; portDecl.portField.SetValue(clonedNode, port); } for (int i = 0; i < clonedNode.dynamicConnectionPorts.Count; i++) { // Iterate over all dynamic connection ports ConnectionPort port = clonedNode.dynamicConnectionPorts[i]; port = ReplaceSO(allSOs, clonedSOs, port); for (int c = 0; c < port.connections.Count; c++) { port.connections[c] = ReplaceSO(allSOs, clonedSOs, port.connections[c]); } port.body = clonedNode; clonedNode.dynamicConnectionPorts[i] = port; } } if (editorStates) // Clone the editorStates { nodeCanvas.editorStates = CreateWorkingCopy(nodeCanvas.editorStates, nodeCanvas); } // Fix references in editorStates to Nodes in the canvas foreach (NodeEditorState state in nodeCanvas.editorStates) { state.selectedNode = ReplaceSO(allSOs, clonedSOs, state.selectedNode); } return(nodeCanvas); }
/// <summary> /// Creates a node of the specified ID at pos on the specified canvas, optionally auto-connecting the specified output to a matching input /// silent disables any events, init specifies whether OnCreate should be called /// </summary> public static Node Create(string nodeID, Vector2 pos, NodeCanvas hostCanvas, ConnectionPort connectingPort = null, bool silent = false, bool init = true) { if (string.IsNullOrEmpty(nodeID) || hostCanvas == null) { throw new ArgumentException(); } if (!NodeCanvasManager.CheckCanvasCompability(nodeID, hostCanvas.GetType())) { throw new UnityException("Cannot create Node with ID '" + nodeID + "' as it is not compatible with the current canavs type (" + hostCanvas.GetType().ToString() + ")!"); } if (!hostCanvas.CanAddNode(nodeID)) { throw new UnityException("Cannot create Node with ID '" + nodeID + "' on the current canvas of type (" + hostCanvas.GetType().ToString() + ")!"); } // Create node from data NodeTypeData data = NodeTypes.getNodeData(nodeID); Node node = (Node)CreateInstance(data.type); if (node == null) { return(null); } // Init node state node.name = node.Title; node.autoSize = node.DefaultSize; node.position = pos; ConnectionPortManager.UpdateConnectionPorts(node); if (init) { node.OnCreate(); } if (connectingPort != null) { // Handle auto-connection and link the output to the first compatible input foreach (ConnectionPort port in node.connectionPorts) { if (port.TryApplyConnection(connectingPort, silent)) { break; } } } // Add node to host canvas hostCanvas.nodes.Add(node); if (!silent) { // Callbacks hostCanvas.OnNodeChange(connectingPort != null ? connectingPort.body : node); NodeEditorCallbacks.IssueOnAddNode(node); hostCanvas.Validate(); NodeEditor.RepaintClients(); } return(node); }
public virtual void OnAddConnectionPort(ConnectionPort knob) { }
/// <summary> /// Creates a copy of the specified node at pos on the specified canvas, optionally auto-connecting the specified output to a matching input /// silent disables any events /// </summary> public static Node CreateCopy(Node toCopy, Vector2 pos, NodeCanvas hostCanvas, ConnectionPort connectingPort = null, bool silent = false) { if (toCopy == null || hostCanvas == null) { throw new ArgumentException(); } if (!NodeCanvasManager.CheckCanvasCompability(toCopy.GetID, hostCanvas.GetType())) { throw new UnityException("Cannot create Node with ID '" + toCopy.GetID + "' as it is not compatible with the current canvas type (" + hostCanvas.GetType().ToString() + ")!"); } if (!hostCanvas.CanAddNode(toCopy.GetID)) { throw new UnityException("Cannot create Node with ID '" + toCopy.GetID + "' on the current canvas of type (" + hostCanvas.GetType().ToString() + ")!"); } Node node = ScriptableObject.Instantiate(toCopy); //Clone static connection ports foreach (ConnectionPortDeclaration portDecl in ConnectionPortManager.GetPortDeclarationEnumerator(node, true)) { ConnectionPort port = (ConnectionPort)portDecl.portField.GetValue(node); port = portDecl.portInfo.CreateNew(node); portDecl.portField.SetValue(node, port); } //Clone dynamic connection ports for (int i = 0; i < node.dynamicConnectionPorts.Count; ++i) { node.dynamicConnectionPorts[i] = ScriptableObject.Instantiate(node.dynamicConnectionPorts[i]); node.dynamicConnectionPorts[i].body = node; node.dynamicConnectionPorts[i].ClearConnections(); } ConnectionPortManager.UpdateRepresentativePortLists(node); //Clone child SOs System.Func <ScriptableObject, ScriptableObject> copySOs = (ScriptableObject so) => ScriptableObject.Instantiate(so);; node.CopyScriptableObjects(copySOs); if (node == null) { return(null); } // Init node state node.name = node.Title; node.autoSize = node.DefaultSize; node.position = pos; ConnectionPortManager.UpdateConnectionPorts(node); if (connectingPort != null) { // Handle auto-connection and link the output to the first compatible input for (int i = 0; i < node.connectionPorts.Count; i++) { if (node.connectionPorts[i].TryApplyConnection(connectingPort, silent)) { break; } } } // Add node to host canvas hostCanvas.nodes.Add(node); if (!silent) { // Callbacks hostCanvas.OnNodeChange(connectingPort != null ? connectingPort.body : node); NodeEditorCallbacks.IssueOnAddNode(node); hostCanvas.Validate(); NodeEditor.RepaintClients(); } return(node); }
/// <summary> /// Creates a copy of the specified at pos on the current canvas, optionally auto-connecting the specified output to a matching input /// </summary> public static Node CreateCopy(Node toCopy, Vector2 pos, ConnectionPort connectingPort = null, bool silent = false) { return(CreateCopy(toCopy, pos, NodeEditor.curNodeCanvas, connectingPort, silent)); }
// Connection public virtual void OnAddConnection(ConnectionPort port1, ConnectionPort port2) { }
/// <summary> /// Creates a node of the specified ID at pos on the specified canvas, optionally auto-connecting the specified output to a matching input /// silent disables any events, init specifies whether OnCreate should be called /// </summary> public static Node Create(string nodeID, Vector2 pos, NodeCanvas hostCanvas, ConnectionPort connectingPort = null, bool silent = false, bool init = true) { if (string.IsNullOrEmpty(nodeID) || hostCanvas == null) { throw new ArgumentException(); } if (!NodeCanvasManager.CheckCanvasCompability(nodeID, hostCanvas.GetType())) { throw new UnityException("Cannot create Node with ID '" + nodeID + "' as it is not compatible with the current canavs type (" + hostCanvas.GetType().ToString() + ")!"); } if (!hostCanvas.CanAddNode(nodeID)) { throw new UnityException("Cannot create Node with ID '" + nodeID + "' on the current canvas of type (" + hostCanvas.GetType().ToString() + ")!"); } // Create node from data NodeTypeData data = NodeTypes.getNodeData(nodeID); Node node = (Node)CreateInstance(data.type); if (node == null) { return(null); } // Init node state node.canvas = hostCanvas; node.name = node.Title; node.autoSize = node.DefaultSize; node.position = pos; ConnectionPortManager.UpdateConnectionPorts(node); if (init) { node.OnCreate(); } if (connectingPort != null) { // Handle auto-connection and link the output to the first compatible input for (int i = 0; i < node.connectionPorts.Count; i++) { if (node.connectionPorts[i].TryApplyConnection(connectingPort, true)) { break; } } } // Add node to host canvas hostCanvas.nodes.Add(node); if (!silent) { // Callbacks hostCanvas.OnNodeChange(connectingPort != null ? connectingPort.body : node); NodeEditorCallbacks.IssueOnAddNode(node); hostCanvas.Validate(); NodeEditor.RepaintClients(); } #if UNITY_EDITOR if (!silent) { List <ConnectionPort> connectedPorts = new List <ConnectionPort>(); foreach (ConnectionPort port in node.connectionPorts) { // 'Encode' connected ports in one list (double level cannot be serialized) foreach (ConnectionPort conn in port.connections) { connectedPorts.Add(conn); } connectedPorts.Add(null); } Node createdNode = node; UndoPro.UndoProManager.RecordOperation( () => NodeEditorUndoActions.ReinstateNode(createdNode, connectedPorts), () => NodeEditorUndoActions.RemoveNode(createdNode), "Create Node"); // Make sure the new node is in the memory dump NodeEditorUndoActions.CompleteSOMemoryDump(hostCanvas); } #endif return(node); }
public virtual void OnRemoveConnection(ConnectionPort port1, ConnectionPort port2) { }