//...
        static void FinalizeConnection(Port sourcePort, FlowNode targetNode)
        {
            if (sourcePort == null || targetNode == null)
            {
                return;
            }

            Port source = null;
            Port target = null;

            if (sourcePort is ValueOutput || sourcePort is FlowOutput)
            {
                source = sourcePort;
                target = targetNode.GetFirstInputOfType(sourcePort.type);
            }
            else
            {
                source = targetNode.GetFirstOutputOfType(sourcePort.type);
                target = sourcePort;
            }

            BinderConnection.Create(source, target);
        }
        ///----------------------------------------------------------------------------------------------
        ///----------------------------------------------------------------------------------------------

        ///Convert nodes to macro (with a bit of hocus pocus)
        public static void ConvertNodesToMacro(List <Node> originalNodes)
        {
            if (originalNodes == null || originalNodes.Count == 0)
            {
                return;
            }

            if (!UnityEditor.EditorUtility.DisplayDialog("Convert to Macro", "This will create a new Macro out of the nodes.\nPlease note that since Macros are assets, Scene Object references will not be saved.\nThe Macro can NOT be unpacked later on.\nContinue?", "Yes", "No!"))
            {
                return;
            }

            //create asset
            var newMacro = EditorUtils.CreateAsset <Macro>();

            if (newMacro == null)
            {
                return;
            }

            //undo
            var graph = (FlowScriptBase)originalNodes[0].graph;

            graph.RecordUndo("Convert To Macro");

            //clone nodes
            var cloned = Graph.CloneNodes(originalNodes, newMacro, -newMacro.translation);

            //clear initial "example" ports
            newMacro.inputDefinitions.Clear();
            newMacro.outputDefinitions.Clear();

            //cache used ports
            var inputMergeMapSource = new Dictionary <Port, Port>();
            var inputMergeMapTarget = new Dictionary <Port, Port>();

            var outputMergeMapTarget = new Dictionary <Port, Port>();
            var outputMergeMapSource = new Dictionary <Port, Port>();


            //relink copied nodes to inside macro entry/exit
            for (var i = 0; i < originalNodes.Count; i++)
            {
                var originalNode = originalNodes[i];
                //create macro entry node port definitions and link those to input ports of cloned nodes inside
                foreach (var originalInputConnection in originalNode.inConnections.OfType <BinderConnection>())
                {
                    //only do stuff if link source node is not part of the clones
                    if (originalNodes.Contains(originalInputConnection.sourceNode))
                    {
                        continue;
                    }
                    Port defSourcePort = null;
                    //merge same input ports and same target ports
                    if (!inputMergeMapSource.TryGetValue(originalInputConnection.sourcePort, out defSourcePort))
                    {
                        if (!inputMergeMapTarget.TryGetValue(originalInputConnection.targetPort, out defSourcePort))
                        {
                            //remark: we use sourcePort.type instead of target port type, so that connections remain assignable
                            var def = new DynamicPortDefinition(originalInputConnection.targetPort.name, originalInputConnection.sourcePort.type);
                            defSourcePort = newMacro.AddInputDefinition(def);
                            inputMergeMapTarget[originalInputConnection.targetPort] = defSourcePort;
                        }
                        inputMergeMapSource[originalInputConnection.sourcePort] = defSourcePort;
                    }

                    if (defSourcePort.CanAcceptConnections())                      //check this for case of merged FlowPorts
                    {
                        var targetPort = (cloned[i] as FlowNode).GetInputPort(originalInputConnection.targetPortID);
                        BinderConnection.Create(defSourcePort, targetPort);
                    }
                }

                //create macro exit node port definitions and link those to output ports of cloned nodes inside
                foreach (var originalOutputConnection in originalNode.outConnections.OfType <BinderConnection>())
                {
                    //only do stuff if link target node is not part of the clones
                    if (originalNodes.Contains(originalOutputConnection.targetNode))
                    {
                        continue;
                    }
                    Port defTargetPort = null;
                    //merge same input ports and same target ports
                    if (!outputMergeMapTarget.TryGetValue(originalOutputConnection.targetPort, out defTargetPort))
                    {
                        if (!outputMergeMapSource.TryGetValue(originalOutputConnection.sourcePort, out defTargetPort))
                        {
                            var def = new DynamicPortDefinition(originalOutputConnection.sourcePort.name, originalOutputConnection.sourcePort.type);
                            defTargetPort = newMacro.AddOutputDefinition(def);
                            outputMergeMapSource[originalOutputConnection.sourcePort] = defTargetPort;
                        }
                        outputMergeMapTarget[originalOutputConnection.targetPort] = defTargetPort;
                    }

                    if (defTargetPort.CanAcceptConnections())                      //check this for case of merged ValuePorts
                    {
                        var sourcePort = (cloned[i] as FlowNode).GetOutputPort(originalOutputConnection.sourcePortID);
                        BinderConnection.Create(sourcePort, defTargetPort);
                    }
                }
            }

            //Delete originals
            var originalBounds = RectUtils.GetBoundRect(originalNodes.Select(n => n.rect).ToArray());

            foreach (var node in originalNodes.ToArray())
            {
                graph.RemoveNode(node, false);
            }

            //Create MacroWrapper. Relink macro wrapper to outside nodes
            var wrapperPos = originalBounds.center;

            wrapperPos.x = (int)wrapperPos.x;
            wrapperPos.y = (int)wrapperPos.y;
            var wrapper = graph.AddMacroNode(newMacro, wrapperPos, null, null);

            wrapper.GatherPorts();
            foreach (var pair in inputMergeMapSource)
            {
                var source = pair.Key;
                var target = wrapper.GetInputPort(pair.Value.ID);
                BinderConnection.Create(source, target);
            }
            foreach (var pair in outputMergeMapTarget)
            {
                var source = wrapper.GetOutputPort(pair.Value.ID);
                var target = pair.Key;
                BinderConnection.Create(source, target);
            }

            //organize a bit
            var clonedBounds = RectUtils.GetBoundRect(cloned.Select(n => n.rect).ToArray());

            newMacro.entry.position = new Vector2((int)(clonedBounds.xMin - 300), (int)clonedBounds.yMin);
            newMacro.exit.position  = new Vector2((int)(clonedBounds.xMax + 300), (int)clonedBounds.yMin);
            newMacro.translation    = -newMacro.entry.position + new Vector2(300, 300);
            //

            //validate and save
            newMacro.Validate();
            UnityEditor.AssetDatabase.SaveAssets();
        }
示例#3
0
        //Seal it...
        sealed protected override void DrawNodeConnections(Rect drawCanvas, bool fullDrawPass, Vector2 canvasMousePos, float zoomFactor)
        {
            var e = Event.current;

            //Port container graphics
            if (fullDrawPass || drawCanvas.Overlaps(rect))
            {
                GUI.Box(new Rect(rect.x - 8, rect.y + 2, 10, rect.height), string.Empty, CanvasStyles.nodePortContainer);
                GUI.Box(new Rect(rect.xMax - 2, rect.y + 2, 10, rect.height), string.Empty, CanvasStyles.nodePortContainer);
            }
            ///

            if (fullDrawPass || drawCanvas.Overlaps(rect))
            {
                var portRect = new Rect(0, 0, 10, 10);

                //INPUT Ports
                if (orderedInputs != null)
                {
                    Port instancePort = null;
                    for (var i = 0; i < orderedInputs.Length; i++)
                    {
                        var port       = orderedInputs[i];
                        var canConnect = true;
                        if (clickedPort != null)
                        {
                            if ((port == clickedPort) ||
                                (clickedPort.parent == port.parent) ||
                                (clickedPort.IsInputPort()) ||
                                (port.isConnected && port is ValueInput) ||
                                (!TypeConverter.HasConvertion(clickedPort.type, port.type)))
                            {
                                canConnect = false;
                            }
                        }

                        port.pos        = new Vector2(rect.x - 5, rect.y + port.posOffsetY);
                        portRect.width  = port.isConnected? 12:10;
                        portRect.height = portRect.width;
                        portRect.center = port.pos;

                        //first gameobject or component port is considered to be the 'instance' port by convention
                        if (port == firstValuePort)
                        {
                            if (port.IsUnitySceneObject())
                            {
                                instancePort = port;
                            }
                        }

                        //Port graphic
                        if (clickedPort != null && !canConnect && clickedPort != port)
                        {
                            GUI.color = new Color(1, 1, 1, 0.3f);
                        }
                        GUI.Box(portRect, string.Empty, port.isConnected? CanvasStyles.nodePortConnected : CanvasStyles.nodePortEmpty);
                        GUI.color = Color.white;

                        //Tooltip
                        if (portRect.Contains(e.mousePosition))
                        {
                            var labelString = (canConnect || port.isConnected || port == clickedPort)? port.type.FriendlyName() : "Can't Connect Here";
                            var size        = CanvasStyles.box.CalcSize(new GUIContent(labelString));
                            var rect        = new Rect(0, 0, size.x + 10, size.y + 5);
                            rect.x = portRect.x - size.x - 10;
                            rect.y = portRect.y - size.y / 2;
                            GUI.Box(rect, labelString, CanvasStyles.box);

                            //Or value
                        }
                        else
                        {
                            if (port is ValueInput && !port.isConnected)
                            {
                                //Only these types are shown their value
                                if (port.type.IsValueType || port.type == typeof(Type) || port.type == typeof(string) || port.IsUnityObject())
                                {
                                    var    value       = (port as ValueInput).serializedValue;
                                    string labelString = null;
                                    if (!(port as ValueInput).isDefaultValue)
                                    {
                                        if (value is UnityEngine.Object && value as UnityEngine.Object != null)
                                        {
                                            labelString = string.Format("<b><color=#66ff33>{0}</color></b>", (value as UnityEngine.Object).name);
                                        }
                                        else
                                        {
                                            labelString = value.ToStringAdvanced();
                                        }
                                    }
                                    else if (port == instancePort)
                                    {
                                        var exists = true;
                                        if (graphAgent != null && typeof(Component).IsAssignableFrom(port.type))
                                        {
                                            exists = graphAgent.GetComponent(port.type) != null;
                                        }
                                        var color = exists? "66ff33" : "ff3300";
                                        labelString = string.Format("<color=#{0}><b>♟ <i>Self</i></b></color>", color);
                                    }
                                    else
                                    {
                                        GUI.color   = new Color(1, 1, 1, 0.15f);
                                        labelString = value.ToStringAdvanced();
                                    }
                                    var size = CanvasStyles.label.CalcSize(new GUIContent(labelString));
                                    var rect = new Rect(0, 0, size.x, size.y);
                                    rect.x = portRect.x - size.x - 5;
                                    rect.y = portRect.y - size.y * 0.3f;                                     //*0.3? something's wrong here. FIX
                                    GUI.Label(rect, labelString, CanvasStyles.label);
                                    GUI.color = Color.white;
                                }
                            }
                        }

                        if (GraphEditorUtility.allowClick)
                        {
                            //Right click removes connections
                            if (port.isConnected && e.type == EventType.ContextClick && portRect.Contains(e.mousePosition))
                            {
                                foreach (var c in GetInPortConnections(port))
                                {
                                    graph.RemoveConnection(c);
                                }
                                e.Use();
                                return;
                            }

                            //Click initialize new drag & drop connection
                            if (e.type == EventType.MouseDown && e.button == 0 && portRect.Contains(e.mousePosition))
                            {
                                if (port.CanAcceptConnections())
                                {
                                    dragDropMisses = 0;
                                    clickedPort    = port;
                                    e.Use();
                                }
                            }

                            //Drop on creates connection
                            if (e.type == EventType.MouseUp && e.button == 0 && clickedPort != null)
                            {
                                if (portRect.Contains(e.mousePosition) && port.CanAcceptConnections())
                                {
                                    BinderConnection.Create(clickedPort, port);
                                    clickedPort = null;
                                    e.Use();
                                }
                            }
                        }
                    }
                }

                //OUTPUT Ports
                if (orderedOutputs != null)
                {
                    for (var i = 0; i < orderedOutputs.Length; i++)
                    {
                        var port       = orderedOutputs[i];
                        var canConnect = true;
                        if (clickedPort != null)
                        {
                            if ((port == clickedPort) ||
                                (clickedPort.parent == port.parent) ||
                                (clickedPort.IsOutputPort()) ||
                                (port.isConnected && port is FlowOutput) ||
                                (!TypeConverter.HasConvertion(port.type, clickedPort.type)))
                            {
                                canConnect = false;
                            }
                        }

                        port.pos        = new Vector2(rect.xMax + 5, rect.y + port.posOffsetY);
                        portRect.width  = port.isConnected? 12:10;
                        portRect.height = portRect.width;
                        portRect.center = port.pos;

                        //Port graphic
                        if (clickedPort != null && !canConnect && clickedPort != port)
                        {
                            GUI.color = new Color(1, 1, 1, 0.3f);
                        }
                        GUI.Box(portRect, string.Empty, port.isConnected? CanvasStyles.nodePortConnected : CanvasStyles.nodePortEmpty);
                        GUI.color = Color.white;

                        //Tooltip
                        if (portRect.Contains(e.mousePosition))
                        {
                            var labelString = (canConnect || port.isConnected || port == clickedPort)? port.type.FriendlyName() : "Can't Connect Here";
                            var size        = CanvasStyles.box.CalcSize(new GUIContent(labelString));
                            var rect        = new Rect(0, 0, size.x + 10, size.y + 5);
                            rect.x = portRect.x + 15;
                            rect.y = portRect.y - portRect.height / 2;
                            GUI.Box(rect, labelString, CanvasStyles.box);
                        }

                        if (GraphEditorUtility.allowClick)
                        {
                            //Right click removes connections
                            if (e.type == EventType.ContextClick && portRect.Contains(e.mousePosition))
                            {
                                foreach (var c in GetOutPortConnections(port))
                                {
                                    graph.RemoveConnection(c);
                                }
                                e.Use();
                                return;
                            }

                            //Click initialize new drag & drop connection
                            if (e.type == EventType.MouseDown && e.button == 0 && portRect.Contains(e.mousePosition))
                            {
                                if (port.CanAcceptConnections())
                                {
                                    dragDropMisses = 0;
                                    clickedPort    = port;
                                    e.Use();
                                }
                            }

                            //Drop on creates connection
                            if (e.type == EventType.MouseUp && e.button == 0 && clickedPort != null)
                            {
                                if (portRect.Contains(e.mousePosition) && port.CanAcceptConnections())
                                {
                                    BinderConnection.Create(port, clickedPort);
                                    clickedPort = null;
                                    e.Use();
                                }
                            }
                        }
                    }
                }
            }

            ///ACCEPT CONNECTION
            if (clickedPort != null && e.type == EventType.MouseUp)
            {
                ///ON NODE
                if (rect.Contains(e.mousePosition))
                {
                    var cachePort = clickedPort;
                    var menu      = new GenericMenu();
                    if (cachePort is ValueOutput || cachePort is FlowOutput)
                    {
                        if (orderedInputs != null)
                        {
                            foreach (var _port in orderedInputs.Where(p => p.CanAcceptConnections() && TypeConverter.HasConvertion(cachePort.type, p.type)))
                            {
                                var port = _port;
                                menu.AddItem(new GUIContent(string.Format("To: '{0}'", port.name)), false, () => { BinderConnection.Create(cachePort, port); });
                            }
                        }
                    }
                    else
                    {
                        if (orderedOutputs != null)
                        {
                            foreach (var _port in orderedOutputs.Where(p => p.CanAcceptConnections() && TypeConverter.HasConvertion(p.type, cachePort.type)))
                            {
                                var port = _port;
                                menu.AddItem(new GUIContent(string.Format("From: '{0}'", port.name)), false, () => { BinderConnection.Create(port, cachePort); });
                            }
                        }
                    }

                    //append menu items
                    menu = OnDragAndDropPortContextMenu(menu, cachePort);

                    //if there is only 1 option, just do it
                    if (menu.GetItemCount() == 1)
                    {
                        EditorUtils.GetMenuItems(menu)[0].func();
                    }
                    else
                    {
                        GraphEditorUtility.PostGUI += () => { menu.ShowAsContext(); };
                    }

                    clickedPort = null;
                    e.Use();
                    ///

                    ///ON CANVAS
                }
                else
                {
                    dragDropMisses++;
                    if (dragDropMisses == graph.allNodes.Count && clickedPort != null)
                    {
                        var cachePort = clickedPort;
                        clickedPort = null;
                        DoContextPortConnectionMenu(cachePort, e.mousePosition, zoomFactor);
                        e.Use();
                    }
                }
            }

            //Temp connection line when linking
            if (clickedPort != null && clickedPort.parent == this)
            {
                var from  = clickedPort.pos;
                var to    = e.mousePosition;
                var xDiff = (from.x - to.x) * 0.8f;
                xDiff = to.x > from.x? xDiff : -xDiff;
                var tangA = clickedPort is FlowInput || clickedPort is ValueInput? new Vector2(xDiff, 0) : new Vector2(-xDiff, 0);
                var tangB = tangA * -1;
                Handles.DrawBezier(from, to, from + tangA, to + tangB, new Color(0.5f, 0.5f, 0.8f, 0.8f), null, 3);
            }

            //Actualy draw the existing connections
            for (var i = 0; i < outConnections.Count; i++)
            {
                var binder = outConnections[i] as BinderConnection;
                if (binder != null)                  //for in case it's MissingConnection
                {
                    var sourcePort = binder.sourcePort;
                    var targetPort = binder.targetPort;
                    if (sourcePort != null && targetPort != null)
                    {
                        if (fullDrawPass || drawCanvas.Overlaps(RectUtils.GetBoundRect(sourcePort.pos, targetPort.pos)))
                        {
                            binder.DrawConnectionGUI(sourcePort.pos, targetPort.pos);
                        }
                    }
                }
            }
        }