public override void CachePosition() { var innerWidth = innerPosition.width; var innerHeight = innerPosition.height; var edgeWidth = edgePosition.width; var edgeHeight = edgePosition.height; var labelWidth = Styles.label.CalcSize(label).x; var labelHeight = EditorGUIUtility.singleLineHeight; sourcePosition = canvas.Widget(transition.source).position; destinationPosition = canvas.Widget(transition.destination).position; Vector2 sourceClosestPoint; Vector2 destinationClosestPoint; LudiqGUIUtility.ClosestPoints(sourcePosition, destinationPosition, out sourceClosestPoint, out destinationClosestPoint); if (transition.destination != transition.source) { GraphGUI.GetConnectionEdge ( sourceClosestPoint, destinationClosestPoint, out sourceEdge, out destinationEdge ); } else { sourceEdge = Edge.Right; destinationEdge = Edge.Left; } sourceEdgeCenter = sourcePosition.GetEdgeCenter(sourceEdge); destinationEdgeCenter = destinationPosition.GetEdgeCenter(destinationEdge); siblingStateTransitions.Clear(); var siblingIndex = 0; // Assign one common axis for transition for all siblings, // regardless of their inversion. The axis is arbitrarily // chosen as the axis for the first transition. var assignedTransitionAxis = false; var transitionAxis = Vector2.zero; foreach (var graphTransition in canvas.graph.transitions) { var current = transition == graphTransition; var analog = transition.source == graphTransition.source && transition.destination == graphTransition.destination; var inverted = transition.source == graphTransition.destination && transition.destination == graphTransition.source; if (current) { siblingIndex = siblingStateTransitions.Count; } if (current || analog || inverted) { if (!assignedTransitionAxis) { var siblingStateTransitionDrawer = canvas.Widget <IStateTransitionWidget>(graphTransition); transitionAxis = siblingStateTransitionDrawer.sourceEdge.Normal(); assignedTransitionAxis = true; } siblingStateTransitions.Add(graphTransition); } } // Fix the edge case where the source and destination perfectly overlap if (transitionAxis == Vector2.zero) { transitionAxis = Vector2.right; } // Calculate the spread axis and origin for the set of siblings var spreadAxis = transitionAxis.Perpendicular1().Abs(); var spreadOrigin = (sourceEdgeCenter + destinationEdgeCenter) / 2; if (transition.source == transition.destination) { spreadAxis = Vector2.up; spreadOrigin = sourcePosition.GetEdgeCenter(Edge.Bottom) - Vector2.down * 10; } if (LudiqCore.Configuration.developerMode && LudiqGraphs.Configuration.debug) { Handles.BeginGUI(); Handles.color = Color.yellow; Handles.DrawLine(spreadOrigin + spreadAxis * -1000, spreadOrigin + spreadAxis * 1000); Handles.EndGUI(); } // Calculate the offset of the current sibling by iterating over its predecessors var spreadOffset = 0f; var previousSpreadSize = 0f; for (var i = 0; i <= siblingIndex; i++) { var siblingSize = canvas.Widget <IStateTransitionWidget>(siblingStateTransitions[i]).outerPosition.size; var siblingSizeProjection = GraphGUI.SizeProjection(siblingSize, spreadOrigin, spreadAxis); spreadOffset += previousSpreadSize / 2 + siblingSizeProjection / 2; previousSpreadSize = siblingSizeProjection; } if (transition.source != transition.destination) { // Calculate the total spread size to center the sibling set var totalSpreadSize = 0f; for (var i = 0; i < siblingStateTransitions.Count; i++) { var siblingSize = canvas.Widget <IStateTransitionWidget>(siblingStateTransitions[i]).outerPosition.size; var siblingSizeProjection = GraphGUI.SizeProjection(siblingSize, spreadOrigin, spreadAxis); totalSpreadSize += siblingSizeProjection; } spreadOffset -= totalSpreadSize / 2; } // Finally, calculate the positions middle = spreadOrigin + spreadOffset * spreadAxis; var edgeX = middle.x - edgeWidth / 2; var edgeY = middle.y - edgeHeight / 2; _position = new Rect ( edgeX, edgeY, edgeWidth, edgeHeight ).PixelPerfect(); var innerX = innerPosition.x; var innerY = innerPosition.y; _clippingPosition = _position.Encompass(sourceEdgeCenter).Encompass(destinationEdgeCenter); if (transition.source != transition.destination) { entryEdge = destinationEdge; exitEdge = sourceEdge; } else { entryEdge = sourceEdge; exitEdge = destinationEdge; } entryEdgeCenter = edgePosition.GetEdgeCenter(entryEdge); exitEdgeCenter = edgePosition.GetEdgeCenter(exitEdge); var x = innerX; iconPosition = new Rect ( x, innerY, Styles.eventIcon.fixedWidth, Styles.eventIcon.fixedHeight ).PixelPerfect(); x += iconPosition.width; var clipWidth = innerWidth - (x - innerX); clipPosition = new Rect ( x, edgeY, clipWidth, edgeHeight ).PixelPerfect(); labelInnerPosition = new Rect ( Styles.spaceAroundIcon, innerY - edgeY, labelWidth, labelHeight ).PixelPerfect(); }