예제 #1
0
        /// <summary>
        /// Returns a clone of the given node (clones also lists, but leaves other references as references).
        /// A new ID will be automatically generated.
        /// </summary>
        public T CloneNode <T>(IEditorGUINode node) where T : IEditorGUINode, new()
        {
            T clone = new T();
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

            FieldInfo[] srcFields  = node.GetType().GetFields(flags);
            FieldInfo[] destFields = clone.GetType().GetFields(flags);
            foreach (FieldInfo srcField in srcFields)
            {
                FieldInfo destField = destFields.FirstOrDefault(field => field.Name == srcField.Name);
                if (destField == null || destField.IsLiteral || srcField.FieldType != destField.FieldType)
                {
                    continue;
                }
                object srcValue = srcField.GetValue(node);
                if (srcValue != null)
                {
                    Type valueType = srcValue.GetType();
                    if (valueType.IsArray)
                    {
                        // Clone Array
                        Type  arrayType   = Type.GetType(valueType.FullName.Replace("[]", string.Empty));
                        Array srcArray    = srcValue as Array;
                        Array clonedArray = Array.CreateInstance(arrayType, srcArray.Length);
                        for (int i = 0; i < srcArray.Length; ++i)
                        {
                            clonedArray.SetValue(srcArray.GetValue(i), i);
                        }
                        destField.SetValue(clone, clonedArray);
                    }
                    else if (valueType.IsGenericType)
                    {
                        // Clone List
                        Type listType = Type.GetType(valueType.FullName.Replace("[]", string.Empty));
                        if (listType == null)
                        {
                            Debug.LogWarning(string.Format("Couldn't clone correctly the {0} field, a shallow copy will be used", srcField.Name));
                            destField.SetValue(clone, srcField.GetValue(node));
                        }
                        else
                        {
                            IList srcList    = srcValue as IList;
                            IList clonedList = Activator.CreateInstance(listType) as IList;
                            for (int i = 0; i < srcList.Count; ++i)
                            {
                                clonedList.Add(srcList[i]);
                            }
                            destField.SetValue(clone, clonedList);
                        }
                    }
                    else
                    {
                        destField.SetValue(clone, srcField.GetValue(node));
                    }
                }
            }
            clone.id = Guid.NewGuid().ToString();
            return(clone);
        }
예제 #2
0
        /// <summary>
        /// Always connects from BottomOrRight side to TopOrLeft side.
        /// If ALT is pressed shows the delete connection button.
        /// Called during Repaint or MouseDown/Up.
        /// Returns TRUE if the connection was deleted using the delete connection button.
        /// </summary>
        public static bool Connect(
            NodeProcess process, int connectionIndex, int fromTotConnections, NodeConnectionOptions fromOptions,
            IEditorGUINode fromNode, IEditorGUINode toNode
            )
        {
            _Styles.Init();
            NodeGUIData fromGUIData = process.nodeToGUIData[fromNode];
            NodeGUIData toGUIData   = process.nodeToGUIData[toNode];

            bool        useSubFromAreas = fromOptions.connectionMode != ConnectionMode.Dual && fromGUIData.connectorAreas != null;
            Rect        fromArea        = useSubFromAreas ? fromGUIData.connectorAreas[connectionIndex] : fromGUIData.fullArea;
            AnchorsData anchorsData     = GetAnchors(process, connectionIndex, fromNode, fromArea, toNode, toGUIData.fullArea, fromOptions, useSubFromAreas);
            Color       color           = GetConnectionColor(connectionIndex, fromTotConnections, fromGUIData, fromOptions);

            // Line (shadow + line)
            Handles.DrawBezier(
                anchorsData.fromLineP, anchorsData.toLineP, anchorsData.fromTangent, anchorsData.toTangent, _lineShadowColor, null, _LineSize + 2
                );
            Handles.DrawBezier(anchorsData.fromLineP, anchorsData.toLineP, anchorsData.fromTangent, anchorsData.toTangent, color, null, _LineSize);
            // Line start square
            Rect fromSquareR = anchorsData.fromIsSide
                ? new Rect(anchorsData.fromMarkP.x, anchorsData.fromMarkP.y - FromSquareHeight * 0.5f, FromSquareWidth, FromSquareHeight)
                : new Rect(anchorsData.fromMarkP.x - FromSquareHeight * 0.5f, anchorsData.fromMarkP.y, FromSquareHeight, FromSquareWidth);

            using (new DeGUI.ColorScope(null, null, color)) GUI.DrawTexture(fromSquareR, DeStylePalette.whiteSquare);
            // Arrow
            Rect arrowR = new Rect(
                anchorsData.toArrowP.x - DeStylePalette.ico_nodeArrow.width, anchorsData.toArrowP.y - DeStylePalette.ico_nodeArrow.height * 0.5f,
                DeStylePalette.ico_nodeArrow.width, DeStylePalette.ico_nodeArrow.height
                );
            Matrix4x4 currGUIMatrix = GUI.matrix;

            if (anchorsData.arrowRequiresRotation)
            {
                GUIUtility.RotateAroundPivot(anchorsData.arrowRotationAngle, anchorsData.toArrowP * process.guiScale + process.guiScalePositionDiff);
            }
            using (new DeGUI.ColorScope(null, null, color)) GUI.DrawTexture(arrowR, DeStylePalette.ico_nodeArrow);
            GUI.matrix = currGUIMatrix;
            // Delete connection button (placed at center of line)
            if (DeGUIKey.Exclusive.alt)
            {
                Vector2 midP    = anchorsData.fromTangent + (anchorsData.toTangent - anchorsData.fromTangent) * 0.5f;
                Vector2 midPAlt = anchorsData.fromLineP + (anchorsData.toLineP - anchorsData.fromLineP) * 0.5f;
                midP += (midPAlt - midP) * 0.25f;
                Rect btR = new Rect(midP.x - 5, midP.y - 5, 10, 10);
                using (new DeGUI.ColorScope(null, null, color)) GUI.DrawTexture(btR.Expand(2), DeStylePalette.circle);
                using (new DeGUI.ColorScope(null, null, DeGUI.colors.global.red)) {
                    if (GUI.Button(btR, "", _Styles.btDelete))
                    {
                        return(true);
                    }
                }
                GUI.DrawTexture(btR.Contract(2), DeStylePalette.ico_delete);
            }
            return(false);
        }
예제 #3
0
 // When added has a clone ready (used to verify onCloneNodeCallback).
 // When multiple copies are pasted, new clones are created
 public void Add(IEditorGUINode node, IEditorGUINode clone, NodeConnectionOptions connectionOptions, Func <IEditorGUINode, IEditorGUINode, bool> onCloneNodeCallback)
 {
     _originalNodes.Add(node);
     _originalIdToCloneId.Add(node.id, clone.id);
     _originalNodeToConnectionOptions.Add(node, connectionOptions);
     _originalNodeToGuiData.Add(node, _process.nodeToGUIData[node]);
     _cloneToOriginalNode.Add(clone, node);
     _onCloneNodeCallback = onCloneNodeCallback;
     currClones.Add(clone);
     EditorGUIUtility.systemCopyBuffer = _copyBufferId;
 }
 public void Select(IEditorGUINode node, bool keepExistingSelections)
 {
     if (!keepExistingSelections)
     {
         selectedNodes.Clear();
     }
     if (!selectedNodes.Contains(node))
     {
         selectedNodes.Add(node);
     }
     focusedNode = selectedNodes.Count == 1 ? node : null;
 }
예제 #5
0
        void RefreshAnchorsData()
        {
//            Debug.Log("<color=#00ff00>REFRESH</color> " + Event.current.type);
            _nodeToAnchorsData.Clear();
            for (int i = 0; i < _process.nodes.Count; ++i)
            {
                IEditorGUINode        fromNode    = _process.nodes[i];
                NodeGUIData           fromGUIData = _process.nodeToGUIData[fromNode];
                NodeConnectionOptions fromOptions = _process.nodeToConnectionOptions[fromNode];
                List <string>         connections = fromNode.connectedNodesIds;
                int           totConnections      = fromNode.connectedNodesIds.Count;
                AnchorsData[] anchors             = null;
                for (int c = totConnections - 1; c > -1; --c)
                {
                    string connId = connections[c];
                    if (string.IsNullOrEmpty(connId))
                    {
                        continue;                               // No connection
                    }
                    if (anchors == null)
                    {
                        anchors = new AnchorsData[totConnections];
                    }
                    IEditorGUINode toNode    = _process.idToNode[connId];
                    NodeGUIData    toGUIData = _process.nodeToGUIData[toNode];
                    // Verify if area between nodes is visible
                    if (!fromGUIData.isVisible && !toGUIData.isVisible)
                    {
                        Rect area = fromGUIData.fullArea.Add(toGUIData.fullArea);
                        if (!_process.AreaIsVisible(area))
                        {
                            continue;                                // No visible connection
                        }
                    }
                    bool useSubFromAreas = fromOptions.connectionMode != ConnectionMode.Dual &&
                                           fromGUIData.connectorAreas != null &&
                                           (fromOptions.connectionMode != ConnectionMode.NormalPlus || c < totConnections - 1);
                    Rect        fromArea    = useSubFromAreas ? fromGUIData.connectorAreas[c] : fromGUIData.fullArea;
                    AnchorsData anchorsData = GetAnchorsAllSides(
                        _process, c, fromNode, new RectCache(fromArea), toNode,
                        new RectCache(toGUIData.fullArea), fromOptions, useSubFromAreas
                        );
                    anchorsData.isSet = true;
                    anchors[c]        = anchorsData;
                }
                if (anchors != null)
                {
                    _nodeToAnchorsData.Add(fromNode, anchors);
                }
            }
        }
예제 #6
0
 public void BeginDrag(
     IEditorGUINode mainNode, List <IEditorGUINode> draggedNodes,
     List <IEditorGUINode> allNodes, Dictionary <IEditorGUINode, NodeGUIData> nodeToGuiData
     )
 {
     _mainNode              = mainNode;
     _allNodesRef           = allNodes;
     _nodeToGuiDataRef      = nodeToGuiData;
     _draggedNodesRef       = draggedNodes;
     _lastDragWasSnappedOnX = false;
     _lastDragWasSnappedOnY = false;
     nodeToFullDragPosition.Clear();
     foreach (IEditorGUINode node in _draggedNodesRef)
     {
         nodeToFullDragPosition.Add(node, node.guiPosition);
     }
 }
예제 #7
0
        static bool FromAnchorCanBeVertical(
            NodeProcess process, IEditorGUINode fromNode, RectCache fromArea, IEditorGUINode toNode, RectCache toArea
            )
        {
            bool isBottom = fromArea.center.y <= toArea.y;

            if (
                isBottom &&
                toArea.y - fromArea.yMax <= NodeProcess.SnapOffset
//                && (toArea.center.x > fromArea.xMax + 8 || toArea.center.x < fromArea.x - 8)
                && (toArea.center.x > fromArea.xMax + 8)
//                && Mathf.Abs(fromArea.center.x - toArea.center.x) > 20
                && toArea.center.x - fromArea.center.x > 20
                )
            {
                return(false);
            }
            int len = process.nodes.Count;

            for (int i = 0; i < len; ++i)
            {
                IEditorGUINode node = process.nodes[i];
                if (node == fromNode || node == toNode)
                {
                    continue;
                }
                Rect r = process.nodeToGUIData[node].fullArea;
                if (isBottom)
                {
                    if (r.y > toArea.center.y || r.yMax < fromArea.yMax || r.x > fromArea.center.x || r.xMax < fromArea.center.x)
                    {
                        continue;
                    }
                }
                else
                {
                    if (r.yMax < toArea.center.y || r.y > fromArea.y || r.xMax < fromArea.center.x || r.x > fromArea.center.x)
                    {
                        continue;
                    }
                }
                return(false);
            }
            return(true);
        }
예제 #8
0
 static bool ToAnchorCanBeTop(NodeProcess process, IEditorGUINode fromNode, Rect fromArea, IEditorGUINode toNode, Rect toArea)
 {
     if (toArea.x < fromArea.x)
     {
         return(true);
     }
     foreach (IEditorGUINode node in process.nodes)
     {
         if (node == fromNode || node == toNode)
         {
             continue;
         }
         Rect r = process.nodeToGUIData[node].fullArea;
         if (r.y > toArea.y || r.yMax < fromArea.center.y || r.x > toArea.center.x || r.xMax < toArea.x)
         {
             continue;
         }
         return(false);
     }
     return(true);
 }
 /// <summary>Returns TRUE if the given node is currently being dragged</summary>
 public bool IsDragging(IEditorGUINode node)
 {
     return(state == State.DraggingNodes && targetNode == node);
 }
예제 #10
0
 /// <summary>Called when the node needs to be drawn</summary>
 protected internal abstract void OnGUI(NodeGUIData nodeGuiData, IEditorGUINode iNode);
예제 #11
0
        internal NodeProcess process; // Set by NodeProcess when instantiating this

        #region Internal Methods

        /// <summary>Used to fill <see cref="NodeGUIData"/></summary>
        protected internal abstract NodeGUIData GetAreas(Vector2 position, IEditorGUINode iNode);
예제 #12
0
        public void EvaluateSnapping(
            IEditorGUINode forNode, Rect forArea, List <IEditorGUINode> allNodes, List <IEditorGUINode> excludedNodes,
            Dictionary <IEditorGUINode, NodeGUIData> nodeToGuiData, Rect processRelativeArea
            )
        {
            hasSnapX = hasSnapY = showHorizontalGuide = showVerticalGuide = false;
            _topSnappingPs.Clear();
            _bottomSnappingPs.Clear();
            _leftSnappingPs.Clear();
            _rightSnappingPs.Clear();
            bool hasNearSnappingX    = false;
            bool hasNearSnappingY    = false;
            bool hasBorderSnapping   = false;
            bool hasTopSnappingPs    = false;
            bool hasBottomSnappingPs = false;
            bool hasLeftSnappingPs   = false;
            bool hasRightSnappingPs  = false;

            if (Event.current.alt || nodeToGuiData[forNode].disableSnapping)
            {
                return;                                                              // ALT pressed or snapping disabled - no snapping
            }
            // Find snapping points and store them
            int len = allNodes.Count;

            // Near snapping
            for (int i = 0; i < len; ++i)
            {
                IEditorGUINode node = allNodes[i];
                if (node == forNode || excludedNodes.Contains(node))
                {
                    continue;
                }
                NodeGUIData nodeGUIData = nodeToGuiData[node];
                if (nodeGUIData.disableSnapping)
                {
                    continue;
                }
                Rect toArea = nodeGUIData.fullArea;
                if (!hasNearSnappingX && forArea.yMax > toArea.y && forArea.y < toArea.yMax)
                {
                    // Within nearSnappingX range
                    // Check rightToLeft then leftToRight
                    if (forArea.xMax < toArea.x && toArea.x - forArea.xMax <= NodeProcess.SnapOffset)
                    {
                        hasSnapX = hasNearSnappingX = true;
                        snapX    = toArea.x - forArea.width - NodeProcess.SnapOffset;
                        if (hasNearSnappingY)
                        {
                            break;
                        }
                    }
                    else if (forArea.x > toArea.xMax && forArea.x - toArea.xMax <= NodeProcess.SnapOffset)
                    {
                        hasSnapX = hasNearSnappingX = true;
                        snapX    = toArea.xMax + NodeProcess.SnapOffset;
                        if (hasNearSnappingY)
                        {
                            break;
                        }
                    }
                }
                if (!hasNearSnappingY && forArea.xMax >= toArea.x && forArea.x < toArea.xMax)
                {
                    // Within nearSnappingY range
                    // Check bottomToTop then topToBottom
                    if (forArea.yMax < toArea.y && toArea.y - forArea.yMax <= NodeProcess.SnapOffset)
                    {
                        hasSnapY = hasNearSnappingY = true;
                        snapY    = toArea.y - forArea.height - NodeProcess.SnapOffset;
                        if (hasNearSnappingX)
                        {
                            break;
                        }
                    }
                    else if (forArea.y > toArea.yMax && forArea.y - toArea.yMax <= NodeProcess.SnapOffset)
                    {
                        hasSnapY = hasNearSnappingY = true;
                        snapY    = toArea.yMax + NodeProcess.SnapOffset;
                        if (hasNearSnappingX)
                        {
                            break;
                        }
                    }
                }
            }
            if (!hasNearSnappingX || !hasNearSnappingY)
            {
                // Border snapping
                for (int i = 0; i < len; ++i)
                {
                    IEditorGUINode node = allNodes[i];
                    if (node == forNode || excludedNodes.Contains(node))
                    {
                        continue;
                    }
                    NodeGUIData nodeGUIData = nodeToGuiData[node];
                    if (nodeGUIData.disableSnapping)
                    {
                        continue;
                    }
                    Rect toArea = nodeGUIData.fullArea;
                    if (!processRelativeArea.Overlaps(toArea))
                    {
                        continue;
                    }
                    if (!hasNearSnappingX)
                    {
                        if (ValuesAreWithinBorderSnappingRange(forArea.x, toArea.x))
                        {
                            _leftSnappingPs.Add(toArea.x);
                            hasSnapX = showVerticalGuide = hasBorderSnapping = hasLeftSnappingPs = true;
                        }
                        else if (ValuesAreWithinBorderSnappingRange(forArea.x, toArea.xMax))
                        {
                            _rightSnappingPs.Add(toArea.xMax);
                            hasSnapX = showVerticalGuide = hasBorderSnapping = hasRightSnappingPs = true;
                        }
                    }
                    if (!hasNearSnappingY)
                    {
                        if (ValuesAreWithinBorderSnappingRange(forArea.y, toArea.y))
                        {
                            _topSnappingPs.Add(toArea.y);
                            hasSnapY = showHorizontalGuide = hasBorderSnapping = hasTopSnappingPs = true;
                        }
                        else if (ValuesAreWithinBorderSnappingRange(forArea.y, toArea.yMax))
                        {
                            _bottomSnappingPs.Add(toArea.yMax);
                            hasSnapY = showHorizontalGuide = hasBorderSnapping = hasBottomSnappingPs = true;
                        }
                    }
                }
                // Find closes snapping point
                if (hasBorderSnapping)
                {
                    if (hasLeftSnappingPs)
                    {
                        snapX = FindNearestValueTo(forArea.x, _leftSnappingPs);
                    }
                    else if (hasRightSnappingPs)
                    {
                        snapX = FindNearestValueTo(forArea.x, _rightSnappingPs);
                    }
                    if (hasTopSnappingPs)
                    {
                        snapY = FindNearestValueTo(forArea.y, _topSnappingPs);
                    }
                    else if (hasBottomSnappingPs)
                    {
                        snapY = FindNearestValueTo(forArea.y, _bottomSnappingPs);
                    }
                }
            }
        }
예제 #13
0
        /// <summary>
        /// Returns a list of pasteable nodes, with their GUID recreated and their connections adapted
        /// </summary>
        /// <returns></returns>
        public List <T> GetNodesToPaste <T>() where T : IEditorGUINode, new()
        {
            if (_originalNodes.Count == 0)
            {
                return(null);
            }
            List <T> result = new List <T>();

            if (_requiresRecloningOnNextPaste)
            {
                // Create new clones
                currClones.Clear();
                _originalIdToCloneId.Clear();
                _cloneToOriginalNode.Clear();
                foreach (IEditorGUINode original in _originalNodes)
                {
                    T clone = CloneNode <T>(original);
                    if (_onCloneNodeCallback != null && !_onCloneNodeCallback(original, clone))
                    {
                        continue;
                    }
                    _cloneToOriginalNode.Add(clone, original);
                    _originalIdToCloneId[original.id] = clone.id;
                    currClones.Add(clone);
                    result.Add(clone);
                }
            }
            else
            {
                foreach (IEditorGUINode clone in currClones)
                {
                    result.Add((T)clone);
                }
            }
            // Replace all interconnected ids with new ones, and eliminate others
            foreach (IEditorGUINode original in _originalNodes)
            {
                NodeConnectionOptions connectionOptions = _originalNodeToConnectionOptions[original];
                IEditorGUINode        clone             = GetCloneByOriginalId(original.id);
                for (int c = clone.connectedNodesIds.Count - 1; c > -1; --c)
                {
                    string connectionId = clone.connectedNodesIds[c];
                    if (IsConnectionToCopiedNode(connectionId))
                    {
                        // Replace connection ID with clone's ID
                        clone.connectedNodesIds[c] = _originalIdToCloneId[connectionId];
                    }
                    else
                    {
                        // Clear or delete connection
                        switch (connectionOptions.connectionMode)
                        {
                        case ConnectionMode.Flexible:
                            clone.connectedNodesIds.RemoveAt(c);
                            break;

                        default:
                            clone.connectedNodesIds[c] = null;
                            break;
                        }
                    }
                }
            }
            _requiresRecloningOnNextPaste = true;
            return(result);
        }
예제 #14
0
        void AlignSelectedNodes(GroupAlignment alignment)
        {
            int len = _process.selection.selectedNodes.Count;

            if (len <= 1)
            {
                return;
            }

            GUI.changed = true;
            Vector2 shift       = _process.areaShift;
            Vector2 alignVector = Vector2.zero;

            switch (alignment)
            {
            case GroupAlignment.Left:
                for (int i = 0; i < len; ++i)
                {
                    Rect r = _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea;
                    if (i == 0)
                    {
                        alignVector = new Vector2(r.x, 0);
                    }
                    else if (r.x < alignVector.x)
                    {
                        alignVector.x = r.x;
                    }
                }
                for (int i = 0; i < len; ++i)
                {
                    IEditorGUINode node = _process.selection.selectedNodes[i];
                    node.guiPosition = new Vector2(alignVector.x - shift.x, node.guiPosition.y);
                }
                break;

            case GroupAlignment.Right:
                for (int i = 0; i < len; ++i)
                {
                    Rect r = _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea;
                    if (i == 0)
                    {
                        alignVector = new Vector2(r.xMax, 0);
                    }
                    else if (r.xMax > alignVector.x)
                    {
                        alignVector.x = r.xMax;
                    }
                }
                for (int i = 0; i < len; ++i)
                {
                    IEditorGUINode node = _process.selection.selectedNodes[i];
                    node.guiPosition = new Vector2(
                        alignVector.x - _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea.width - shift.x,
                        node.guiPosition.y
                        );
                }
                break;

            case GroupAlignment.VCenter:
                for (int i = 0; i < len; ++i)
                {
                    Rect r = _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea;
                    if (i == 0)
                    {
                        alignVector = new Vector2(r.center.x, 0);
                    }
                    else if (r.center.x > alignVector.x)
                    {
                        alignVector.x += (r.center.x - alignVector.x) * 0.5f;
                    }
                    else if (r.center.x < alignVector.x)
                    {
                        alignVector.x -= (alignVector.x - r.center.x) * 0.5f;
                    }
                }
                for (int i = 0; i < len; ++i)
                {
                    IEditorGUINode node = _process.selection.selectedNodes[i];
                    node.guiPosition = new Vector2(
                        alignVector.x - _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea.width * 0.5f - shift.x,
                        node.guiPosition.y
                        );
                }
                break;

            case GroupAlignment.Top:
                for (int i = 0; i < len; ++i)
                {
                    Rect r = _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea;
                    if (i == 0)
                    {
                        alignVector = new Vector2(0, r.y);
                    }
                    else if (r.y < alignVector.y)
                    {
                        alignVector.y = r.y;
                    }
                }
                for (int i = 0; i < len; ++i)
                {
                    IEditorGUINode node = _process.selection.selectedNodes[i];
                    node.guiPosition = new Vector2(node.guiPosition.x, alignVector.y - shift.y);
                }
                break;

            case GroupAlignment.Bottom:
                for (int i = 0; i < len; ++i)
                {
                    Rect r = _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea;
                    if (i == 0)
                    {
                        alignVector = new Vector2(0, r.yMax);
                    }
                    else if (r.yMax > alignVector.y)
                    {
                        alignVector.y = r.yMax;
                    }
                }
                for (int i = 0; i < len; ++i)
                {
                    IEditorGUINode node = _process.selection.selectedNodes[i];
                    node.guiPosition = new Vector2(
                        node.guiPosition.x,
                        alignVector.y - _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea.height - shift.y
                        );
                }
                break;

            case GroupAlignment.HCenter:
                for (int i = 0; i < len; ++i)
                {
                    Rect r = _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea;
                    if (i == 0)
                    {
                        alignVector = new Vector2(0, r.center.y);
                    }
                    else if (r.center.y > alignVector.y)
                    {
                        alignVector.y += (r.center.y - alignVector.y) * 0.5f;
                    }
                    else if (r.center.y < alignVector.y)
                    {
                        alignVector.y -= (alignVector.y - r.center.y) * 0.5f;
                    }
                }
                for (int i = 0; i < len; ++i)
                {
                    IEditorGUINode node = _process.selection.selectedNodes[i];
                    node.guiPosition = new Vector2(
                        node.guiPosition.x,
                        alignVector.y - _process.nodeToGUIData[_process.selection.selectedNodes[i]].fullArea.height * 0.5f - shift.y
                        );
                }
                break;
            }
        }
예제 #15
0
 public void Set(IEditorGUINode node)
 {
     this.node = node;
 }
예제 #16
0
 public void Reset()
 {
     node = null;
 }
예제 #17
0
        /// <summary>
        /// Always connects from BottomOrRight side to TopOrLeft side.
        /// If ALT is pressed shows the delete connection button.
        /// Called during Repaint or MouseDown/Up.
        /// Returns TRUE if the connection was deleted using the delete connection button.
        /// </summary>
        public bool Connect(
            int connectionIndex, int fromTotConnections, NodeConnectionOptions fromOptions,
            IEditorGUINode fromNode
            )
        {
            _Styles.Init();
            if (_anchorsDataRefreshRequired)
            {
                _anchorsDataRefreshRequired = false;
                RefreshAnchorsData();
            }
            NodeGUIData fromGUIData = _process.nodeToGUIData[fromNode];

            if (!_nodeToAnchorsData.ContainsKey(fromNode))
            {
                return(false);
            }
            AnchorsData anchorsData = _nodeToAnchorsData[fromNode][connectionIndex];

            if (!anchorsData.isSet)
            {
                return(false);
            }
            //
            Color color = GetConnectionColor(connectionIndex, fromTotConnections, fromGUIData, fromOptions);

            // Line (shadow + line)
            if (_process.options.connectorsShadow)
            {
                Handles.DrawBezier(
                    anchorsData.fromLineP, anchorsData.toLineP, anchorsData.fromTangent, anchorsData.toTangent, _LineShadowColor, null, _process.options.connectorsThickness + 2
                    );
            }
            Handles.DrawBezier(anchorsData.fromLineP, anchorsData.toLineP, anchorsData.fromTangent, anchorsData.toTangent, color, null, _process.options.connectorsThickness);
            // Line start square
            Rect fromSquareR;

            switch (anchorsData.fromSide)
            {
            case ConnectionSide.Top:
                fromSquareR = new Rect(anchorsData.fromMarkP.x - _FromSquareHeight * 0.5f, anchorsData.fromMarkP.y - _FromSquareWidth, _FromSquareHeight, _FromSquareWidth);
                break;

            case ConnectionSide.Bottom:
                fromSquareR = new Rect(anchorsData.fromMarkP.x - _FromSquareHeight * 0.5f, anchorsData.fromMarkP.y, _FromSquareHeight, _FromSquareWidth);
                break;

            case ConnectionSide.Left:
                fromSquareR = new Rect(anchorsData.fromMarkP.x - _FromSquareWidth, anchorsData.fromMarkP.y - _FromSquareHeight * 0.5f, _FromSquareWidth, _FromSquareHeight);
                break;

            default: // Right
                fromSquareR = new Rect(anchorsData.fromMarkP.x, anchorsData.fromMarkP.y - _FromSquareHeight * 0.5f, _FromSquareWidth, _FromSquareHeight);
                break;
            }
            using (new DeGUI.ColorScope(null, null, color)) GUI.DrawTexture(fromSquareR, DeStylePalette.whiteSquare);
            // Arrow
            Rect arrowR = new Rect(
                anchorsData.toArrowP.x - DeStylePalette.ico_nodeArrow.width, anchorsData.toArrowP.y - DeStylePalette.ico_nodeArrow.height * 0.5f,
                DeStylePalette.ico_nodeArrow.width, DeStylePalette.ico_nodeArrow.height
                );
            Matrix4x4 currGUIMatrix = GUI.matrix;

            if (anchorsData.arrowRequiresRotation)
            {
                GUIUtility.RotateAroundPivot(anchorsData.arrowRotationAngle, anchorsData.toArrowP * _process.guiScale + _process.guiScalePositionDiff);
            }
            using (new DeGUI.ColorScope(null, null, color)) GUI.DrawTexture(arrowR, DeStylePalette.ico_nodeArrow);
            GUI.matrix = currGUIMatrix;
            // Delete connection button (placed at center of line)
            if (DeGUIKey.Exclusive.alt)
            {
                Vector2 midP    = anchorsData.fromTangent + (anchorsData.toTangent - anchorsData.fromTangent) * 0.5f;
                Vector2 midPAlt = anchorsData.fromLineP + (anchorsData.toLineP - anchorsData.fromLineP) * 0.5f;
                midP += (midPAlt - midP) * 0.25f;
                Rect btR = new Rect(midP.x - 5, midP.y - 5, 10, 10);
                using (new DeGUI.ColorScope(null, null, color)) GUI.DrawTexture(btR.Expand(2), DeStylePalette.circle);
                using (new DeGUI.ColorScope(null, null, DeGUI.colors.global.red)) {
                    if (GUI.Button(btR, "", _Styles.btDelete))
                    {
                        return(true);
                    }
                }
                GUI.DrawTexture(btR.Contract(2), DeStylePalette.ico_delete);
            }
            return(false);
        }
        internal Rect selectionRect;                                                                 // Updated by process when drawing selection via mouse

        #region Public Methods

        public bool IsSelected(IEditorGUINode node)
        {
            return(selectedNodes.Contains(node));
        }
예제 #19
0
        static AnchorsData GetAnchors_2Sides(
            NodeProcess process, int connectionIndex, IEditorGUINode fromNode, Rect fromArea, IEditorGUINode toNode, Rect toArea,
            NodeConnectionOptions connectionOptions, bool sideOnly
            )
        {
            AnchorsData a            = new AnchorsData();
            float       distX        = toArea.x - fromArea.xMax;
            float       distY        = toArea.y - fromArea.yMax;
            bool        fromIsBottom = !sideOnly && fromArea.yMax < toArea.y && distY >= distX &&
                                       FromAnchorCanBeBottom(process, fromNode, fromArea, toNode, toArea);

            a.fromMarkP = fromIsBottom
                ? new Vector2(fromArea.center.x, fromArea.yMax)
                : new Vector2(fromArea.xMax, fromArea.center.y);
            if (connectionOptions.connectionMode == ConnectionMode.Dual)
            {
                if (fromIsBottom)
                {
                    a.fromMarkP.x += connectionIndex == 1 ? 4 : -4;
                }
                else
                {
                    a.fromMarkP.y += connectionIndex == 1 ? 4 : -4;
                }
            }
            // Find correct fromLineP
            a.fromLineP = a.fromMarkP;
            if (fromIsBottom)
            {
                a.fromLineP.y += _FromSquareWidth;
            }
            else
            {
                a.fromLineP.x += _FromSquareWidth;
            }
            //
            bool toIsTop = toArea.y > a.fromMarkP.y &&
                           (fromArea.xMax > toArea.x || toArea.y - a.fromMarkP.y > toArea.center.x - a.fromMarkP.x) &&
                           ToAnchorCanBeTop(process, fromNode, fromArea, toNode, toArea);

            a.toArrowP = a.toLineP = toIsTop
                ? new Vector2(toArea.center.x, toArea.y)
                : new Vector2(toArea.x, toArea.center.y);
            a.fromIsSide = !fromIsBottom;
            a.toIsSide   = !toIsTop;
            // Set tangents
            bool  isToBehindFrom = a.toArrowP.x < a.fromMarkP.x && a.toArrowP.y < fromArea.yMax;
            float dist           = Vector2.Distance(a.toArrowP, a.fromLineP);

            a.isStraight = connectionOptions.connectorMode == ConnectorMode.Straight ||
                           !isToBehindFrom && connectionOptions.connectorMode == ConnectorMode.Smart && dist <= _MaxDistanceForSmartStraight;
            if (a.isStraight)
            {
                a.fromTangent = a.fromLineP;
                a.toTangent   = a.toArrowP;
            }
            else
            {
                if (toIsTop)
                {
                    a.toLineP.y -= DeStylePalette.ico_nodeArrow.width;
                }
                else
                {
                    a.toLineP.x -= DeStylePalette.ico_nodeArrow.width;
                }
                float axisDistance    = a.fromIsSide ? Mathf.Abs(a.toArrowP.x - a.fromLineP.x) : Mathf.Abs(a.toArrowP.y - a.fromLineP.y);
                float tangentDistance = isToBehindFrom ? _TangentDistanceIfInverse : Mathf.Min(_TangentDistance, axisDistance * 0.2f + dist * 0.2f);
                a.fromTangent = a.fromLineP + (fromIsBottom ? Vector2.up * tangentDistance : Vector2.right * tangentDistance);
                a.toTangent   = a.toLineP + (toIsTop? Vector2.up * -tangentDistance : Vector2.right * -tangentDistance);
            }
            // Set arrow
            if (a.isStraight)
            {
                a.arrowRequiresRotation = true;
                a.arrowRotationAngle    = -AngleBetween(Vector2.right, a.toArrowP - a.fromLineP);
            }
            else if (toIsTop)
            {
                a.arrowRequiresRotation = true;
                a.arrowRotationAngle    = 90;
            }
            return(a);
        }
예제 #20
0
        static AnchorsData GetAnchorsAllSides(
            NodeProcess process, int connectionIndex, IEditorGUINode fromNode, RectCache fromArea, IEditorGUINode toNode, RectCache toArea,
            NodeConnectionOptions connectionOptions, bool sideOnly
            )
        {
            AnchorsData a = new AnchorsData();
            // From/to side
            bool toSideSetByFrom = false;

            if (sideOnly)
            {
                a.fromSide = ConnectionSide.Right;
            }
            else
            {
                if (toArea.x >= fromArea.x)
                {
                    // R/T/B
                    if (toArea.x < fromArea.xMax)
                    {
                        if (FromAnchorCanBeVertical(process, fromNode, fromArea, toNode, toArea))
                        {
                            a.fromSide = toArea.center.y < fromArea.center.y ? ConnectionSide.Top : ConnectionSide.Bottom;
                        }
                        else
                        {
                            bool nearVertSnapOffset = toArea.y > fromArea.yMax && fromArea.yMax - toArea.y <= NodeProcess.SnapOffset;
                            bool rightToTop         = nearVertSnapOffset && fromArea.xMax < toArea.center.x;
                            bool leftToTop          = !rightToTop && fromArea.x > toArea.center.x;
                            if (rightToTop)
                            {
                                a.fromSide = ConnectionSide.Right;
                                a.toSide   = ConnectionSide.Top;
                            }
                            else if (leftToTop)
                            {
                                a.fromSide = ConnectionSide.Left;
                                a.toSide   = ConnectionSide.Top;
                            }
                            else
                            {
                                a.fromSide = ConnectionSide.Left;
                                a.toSide   = ConnectionSide.Left;
                            }
                            toSideSetByFrom = true;
                        }
                    }
                    else
                    {
                        if (toArea.yMax > fromArea.y && toArea.y < fromArea.yMax)
                        {
                            a.fromSide = ConnectionSide.Right;
                        }
                        else
                        {
                            float dX = toArea.x - fromArea.xMax;
                            float dY = toArea.center.y < fromArea.y ? toArea.center.y - fromArea.y : toArea.center.y - fromArea.yMax;
                            if (dX > Mathf.Abs(dY) || !FromAnchorCanBeVertical(process, fromNode, fromArea, toNode, toArea))
                            {
                                a.fromSide = ConnectionSide.Right;
                            }
                            else
                            {
                                a.fromSide = dY < 0 ? ConnectionSide.Top : ConnectionSide.Bottom;
                            }
                        }
                    }
                }
                else
                {
                    // L/T/B
                    if (toArea.xMax > fromArea.x)
                    {
                        if (FromAnchorCanBeVertical(process, fromNode, fromArea, toNode, toArea))
                        {
                            a.fromSide = toArea.center.y < fromArea.center.y ? ConnectionSide.Top : ConnectionSide.Bottom;
                        }
                        else
                        {
                            a.fromSide      = ConnectionSide.Right;
                            a.toSide        = ConnectionSide.Right;
                            toSideSetByFrom = true;
                        }
                    }
                    else
                    {
                        if (toArea.yMax > fromArea.y && toArea.y < fromArea.yMax)
                        {
                            a.fromSide = ConnectionSide.Left;
                        }
                        else
                        {
                            float dX = fromArea.x - toArea.xMax;
                            float dY = toArea.yMax < fromArea.y ? toArea.yMax - fromArea.y : toArea.y - fromArea.yMax;
                            if (dX > Mathf.Abs(dY) || !FromAnchorCanBeVertical(process, fromNode, fromArea, toNode, toArea))
                            {
                                a.fromSide = ConnectionSide.Left;
                            }
                            else
                            {
                                a.fromSide = dY < 0 ? ConnectionSide.Top : ConnectionSide.Bottom;
                            }
                        }
                    }
                }
            }
            // To side
            if (!toSideSetByFrom)
            {
                a.toSide = ConnectionSide.Left;
                switch (a.fromSide)
                {
                case ConnectionSide.Top:
                    a.toSide = ToAnchorCanBeVertical(process, fromNode, fromArea, a.fromSide, toNode, toArea)
                        ? ConnectionSide.Bottom
                        : toArea.center.x < fromArea.center.x ? ConnectionSide.Right : ConnectionSide.Left;
                    break;

                case ConnectionSide.Bottom:
                    a.toSide = ToAnchorCanBeVertical(process, fromNode, fromArea, a.fromSide, toNode, toArea)
                        ? ConnectionSide.Top
                        : toArea.center.x < fromArea.center.x ? ConnectionSide.Right : ConnectionSide.Left;
                    break;

                case ConnectionSide.Left:
                    a.toSide = ConnectionSide.Right;
                    break;

                default: // Right
                    if (sideOnly && toArea.x < fromArea.xMax)
                    {
                        // Right side was forced, see if we can connect to sweeter sides
                        a.toSide = toArea.center.x >= fromArea.xMax
                            ? toArea.yMax < fromArea.y ? ConnectionSide.Bottom : ConnectionSide.Top
                            : ConnectionSide.Right;
                    }
                    else
                    {
                        a.toSide = ConnectionSide.Left;
                    }
                    break;
                }
            }
            //
            a.fromIsSide = a.fromSide == ConnectionSide.Left || a.fromSide == ConnectionSide.Right;
            a.toIsSide   = a.toSide == ConnectionSide.Left || a.toSide == ConnectionSide.Right;
            //
            int fromDisplacement = sideOnly ? 0 : 8;

            switch (a.fromSide)
            {
            case ConnectionSide.Top:
                a.fromLineP    = a.fromMarkP = new Vector2(fromArea.center.x + fromDisplacement, fromArea.y);
                a.fromLineP.y -= _FromSquareWidth;
                if (connectionOptions.connectionMode == ConnectionMode.Dual)
                {
                    a.fromLineP.x = a.fromMarkP.x += connectionIndex == 1 ? 4 : -4;
                }
                break;

            case ConnectionSide.Bottom:
                a.fromLineP    = a.fromMarkP = new Vector2(fromArea.center.x + fromDisplacement, fromArea.yMax);
                a.fromLineP.y += _FromSquareWidth;
                if (connectionOptions.connectionMode == ConnectionMode.Dual)
                {
                    a.fromLineP.x = a.fromMarkP.x += connectionIndex == 1 ? 4 : -4;
                }
                break;

            case ConnectionSide.Left:
                a.fromLineP    = a.fromMarkP = new Vector2(fromArea.x, fromArea.center.y + fromDisplacement);
                a.fromLineP.x -= _FromSquareWidth;
                if (connectionOptions.connectionMode == ConnectionMode.Dual)
                {
                    a.fromLineP.y = a.fromMarkP.y += connectionIndex == 1 ? 4 : -4;
                }
                break;

            case ConnectionSide.Right:
                a.fromLineP    = a.fromMarkP = new Vector2(fromArea.xMax, fromArea.center.y + fromDisplacement);
                a.fromLineP.x += _FromSquareWidth;
                if (connectionOptions.connectionMode == ConnectionMode.Dual)
                {
                    a.fromLineP.y = a.fromMarkP.y += connectionIndex == 1 ? 4 : -4;
                }
                break;
            }
            const float maxDistForDirect = 20;

            switch (a.toSide)
            {
            case ConnectionSide.Top:
                a.toArrowP = a.toLineP = new Vector2(toArea.center.x, toArea.y);
                if (Vector2.Distance(a.fromLineP, a.toLineP) < maxDistForDirect)
                {
                    a.toArrowP.x = a.toLineP.x += fromDisplacement;
                }
                break;

            case ConnectionSide.Bottom:
                a.toArrowP = a.toLineP = new Vector2(toArea.center.x, toArea.yMax);
                if (Vector2.Distance(a.fromLineP, a.toLineP) < maxDistForDirect)
                {
                    a.toArrowP.x = a.toLineP.x += fromDisplacement;
                }
                break;

            case ConnectionSide.Left:
                a.toArrowP = a.toLineP = new Vector2(toArea.x, toArea.center.y);
                if (Vector2.Distance(a.fromLineP, a.toLineP) < maxDistForDirect)
                {
                    a.toArrowP.y = a.toLineP.y += fromDisplacement;
                }
                break;

            case ConnectionSide.Right:
                a.toArrowP = a.toLineP = new Vector2(toArea.xMax, toArea.center.y);
                if (Vector2.Distance(a.fromLineP, a.toLineP) < maxDistForDirect)
                {
                    a.toArrowP.y = a.toLineP.y += fromDisplacement;
                }
                break;
            }
            // Set tangents + arrows
//            bool toIsBehindFrom = a.fromSide == ConnectionSide.Right && a.toArrowP.x < a.fromMarkP.x && a.toArrowP.y < fromArea.yMax;
            bool  toIsBehindFrom = a.fromSide == ConnectionSide.Right && a.toArrowP.x < fromArea.center.x;
            float d = Vector2.Distance(a.toArrowP, a.fromLineP);

            a.isStraight = connectionOptions.connectorMode == ConnectorMode.Straight ||
                           !toIsBehindFrom &&
                           connectionOptions.connectorMode == ConnectorMode.Smart && d <= _MaxDistanceForSmartStraight ||
                           Mathf.Approximately(a.toLineP.x, a.fromLineP.x) || Mathf.Approximately(a.toLineP.y, a.fromLineP.y);
            if (a.isStraight)
            {
                a.fromTangent           = a.fromLineP;
                a.toTangent             = a.toArrowP;
                a.arrowRequiresRotation = true;
                a.arrowRotationAngle    = -AngleBetween(Vector2.right, a.toArrowP - a.fromLineP);
            }
            else
            {
                float   axisDistance = a.fromIsSide ? Mathf.Abs(a.toArrowP.x - a.fromLineP.x) : Mathf.Abs(a.toArrowP.y - a.fromLineP.y);
                float   tangentDistance = toIsBehindFrom ? _TangentDistanceIfInverse : Mathf.Min(_TangentDistance, axisDistance * 0.2f + d * 0.2f);
                Vector2 fromTangentOffset, toTangentOffset;
                switch (a.fromSide)
                {
                case ConnectionSide.Top:
                    fromTangentOffset = Vector2.up * -tangentDistance;
                    break;

                case ConnectionSide.Bottom:
                    fromTangentOffset = Vector2.up * tangentDistance;
                    break;

                case ConnectionSide.Left:
                    fromTangentOffset = Vector2.right * -tangentDistance;
                    break;

                default: // Right
                    fromTangentOffset = Vector2.right * tangentDistance;
                    break;
                }
                switch (a.toSide)
                {
                case ConnectionSide.Top:
                    a.toLineP.y            -= DeStylePalette.ico_nodeArrow.width;
                    toTangentOffset         = Vector2.up * -tangentDistance;
                    a.arrowRequiresRotation = true;
                    a.arrowRotationAngle    = 90;
                    break;

                case ConnectionSide.Bottom:
                    a.toLineP.y            += DeStylePalette.ico_nodeArrow.width;
                    toTangentOffset         = Vector2.up * tangentDistance;
                    a.arrowRequiresRotation = true;
                    a.arrowRotationAngle    = -90;
                    break;

                case ConnectionSide.Left:
                    a.toLineP.x    -= DeStylePalette.ico_nodeArrow.width;
                    toTangentOffset = Vector2.right * -tangentDistance;
                    break;

                default: // Right
                    a.toLineP.x            += DeStylePalette.ico_nodeArrow.width;
                    toTangentOffset         = Vector2.right * tangentDistance;
                    a.arrowRequiresRotation = true;
                    a.arrowRotationAngle    = 180;
                    break;
                }
                a.fromTangent = a.fromLineP + fromTangentOffset;
                a.toTangent   = a.toLineP + toTangentOffset;
            }
            return(a);
        }
 public void Deselect(IEditorGUINode node)
 {
     selectedNodes.Remove(node);
 }