/// <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); }
/// <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); }
// 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; }
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); } } }
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); } }
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); }
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); }
/// <summary>Called when the node needs to be drawn</summary> protected internal abstract void OnGUI(NodeGUIData nodeGuiData, IEditorGUINode iNode);
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);
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); } } } }
/// <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); }
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; } }
public void Set(IEditorGUINode node) { this.node = node; }
public void Reset() { node = null; }
/// <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)); }
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); }
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); }