namespace StellaOps.ElkSharp; internal static partial class ElkEdgeRouterIterative { private static double ScoreProtectedCollectorGatewaySourceExitCandidate( IReadOnlyList path, ElkPositionedNode sourceNode, ElkPoint exitReference) { var score = 0d; if (path.Count < 2) { return double.PositiveInfinity; } var boundary = path[0]; var adjacent = path[1]; var centerX = sourceNode.X + (sourceNode.Width / 2d); var centerY = sourceNode.Y + (sourceNode.Height / 2d); var desiredDx = exitReference.X - centerX; var desiredDy = exitReference.Y - centerY; var boundaryDx = boundary.X - centerX; var boundaryDy = boundary.Y - centerY; var firstDx = adjacent.X - boundary.X; var firstDy = adjacent.Y - boundary.Y; const double tolerance = 0.5d; var dominantHorizontal = Math.Abs(desiredDx) >= Math.Abs(desiredDy) * 1.15d && Math.Sign(desiredDx) != 0; var dominantVertical = Math.Abs(desiredDy) >= Math.Abs(desiredDx) * 1.15d && Math.Sign(desiredDy) != 0; if (dominantHorizontal) { if (Math.Sign(firstDx) != Math.Sign(desiredDx) || Math.Abs(firstDx) <= tolerance) { score += 100_000d; } if (Math.Abs(boundaryDy) > sourceNode.Height * 0.28d) { score += 25_000d; } score += Math.Abs(boundaryDy) * 6d; } else if (dominantVertical) { if (Math.Sign(firstDy) != Math.Sign(desiredDy) || Math.Abs(firstDy) <= tolerance) { score += 100_000d; } if (Math.Abs(boundaryDx) > sourceNode.Width * 0.28d) { score += 25_000d; } score += Math.Abs(boundaryDx) * 6d; } if (ElkShapeBoundaries.IsNearGatewayVertex(sourceNode, boundary, 8d)) { score += 4_000d; } var length = 0d; for (var i = 1; i < path.Count; i++) { length += ElkEdgeRoutingGeometry.ComputeSegmentLength(path[i - 1], path[i]); } return score + length + (Math.Max(0, path.Count - 2) * 6d); } private static List NormalizeProtectedCollectorTail( IReadOnlyList path, double graphMinY, double graphMaxY) { if (path.Count < 5) { return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList(); } const double corridorTolerance = 8d; const double coordinateTolerance = 0.5d; var firstCorridorIndex = -1; var lastCorridorIndex = -1; for (var i = 0; i < path.Count; i++) { if (path[i].Y < graphMinY - corridorTolerance || path[i].Y > graphMaxY + corridorTolerance) { if (firstCorridorIndex < 0) { firstCorridorIndex = i; } lastCorridorIndex = i; } } if (firstCorridorIndex < 0 || lastCorridorIndex <= firstCorridorIndex || lastCorridorIndex >= path.Count - 1 || path[firstCorridorIndex].Y > graphMinY - corridorTolerance) { return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList(); } var desiredDx = path[^1].X - path[0].X; if (Math.Abs(desiredDx) <= coordinateTolerance) { return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList(); } var preCorridorHorizontalIndex = -1; for (var i = 1; i < lastCorridorIndex; i++) { if (Math.Abs(path[i].Y - path[i + 1].Y) > coordinateTolerance || Math.Abs(path[i].X - path[i + 1].X) <= coordinateTolerance) { continue; } var horizontalDelta = path[i + 1].X - path[i].X; if (Math.Sign(horizontalDelta) == 0 || Math.Sign(horizontalDelta) == Math.Sign(desiredDx)) { continue; } preCorridorHorizontalIndex = i; break; } if (preCorridorHorizontalIndex >= 0) { var rebuiltPrefix = path .Take(preCorridorHorizontalIndex + 1) .Select(point => new ElkPoint { X = point.X, Y = point.Y }) .ToList(); var rewrittenCorridorY = path[lastCorridorIndex].Y; var rewrittenReentryPoint = path[lastCorridorIndex + 1]; if (Math.Abs(rebuiltPrefix[^1].Y - rewrittenCorridorY) > coordinateTolerance) { rebuiltPrefix.Add(new ElkPoint { X = rebuiltPrefix[^1].X, Y = rewrittenCorridorY, }); } if (Math.Abs(rebuiltPrefix[^1].X - rewrittenReentryPoint.X) > coordinateTolerance) { rebuiltPrefix.Add(new ElkPoint { X = rewrittenReentryPoint.X, Y = rewrittenCorridorY, }); } for (var i = lastCorridorIndex + 1; i < path.Count; i++) { rebuiltPrefix.Add(new ElkPoint { X = path[i].X, Y = path[i].Y }); } return NormalizeCollectorPoints(rebuiltPrefix); } var firstHorizontalIndex = -1; for (var i = firstCorridorIndex; i < lastCorridorIndex; i++) { if (Math.Abs(path[i].Y - path[i + 1].Y) <= coordinateTolerance && Math.Abs(path[i].X - path[i + 1].X) > coordinateTolerance) { firstHorizontalIndex = i; break; } } if (firstHorizontalIndex < 0) { return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList(); } var firstHorizontalDelta = path[firstHorizontalIndex + 1].X - path[firstHorizontalIndex].X; if (Math.Sign(firstHorizontalDelta) == 0 || Math.Sign(firstHorizontalDelta) == Math.Sign(desiredDx)) { return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList(); } var rebuilt = path .Take(firstCorridorIndex + 1) .Select(point => new ElkPoint { X = point.X, Y = point.Y }) .ToList(); var targetCorridorY = path[lastCorridorIndex].Y; var reentryPoint = path[lastCorridorIndex + 1]; if (Math.Abs(rebuilt[^1].Y - targetCorridorY) > coordinateTolerance) { rebuilt.Add(new ElkPoint { X = rebuilt[^1].X, Y = targetCorridorY, }); } if (Math.Abs(rebuilt[^1].X - reentryPoint.X) > coordinateTolerance) { rebuilt.Add(new ElkPoint { X = reentryPoint.X, Y = targetCorridorY, }); } for (var i = lastCorridorIndex + 1; i < path.Count; i++) { rebuilt.Add(new ElkPoint { X = path[i].X, Y = path[i].Y }); } return NormalizeCollectorPoints(rebuilt); } private static ElkPoint? BuildOrthogonalCollectorCorner(ElkPoint from, ElkPoint to) { const double tolerance = 0.5d; if (Math.Abs(from.X - to.X) <= tolerance || Math.Abs(from.Y - to.Y) <= tolerance) { return null; } return Math.Abs(to.Y - from.Y) >= Math.Abs(to.X - from.X) ? new ElkPoint { X = from.X, Y = to.Y } : new ElkPoint { X = to.X, Y = from.Y }; } private static List NormalizeCollectorPoints(IReadOnlyList points) { const double coordinateTolerance = 0.5d; var deduped = new List(); foreach (var point in points) { if (deduped.Count == 0 || !ElkEdgeRoutingGeometry.PointsEqual(deduped[^1], point)) { deduped.Add(point); } } if (deduped.Count <= 2) { return deduped; } var simplified = new List { deduped[0] }; for (var i = 1; i < deduped.Count - 1; i++) { var previous = simplified[^1]; var current = deduped[i]; var next = deduped[i + 1]; var sameX = Math.Abs(previous.X - current.X) <= coordinateTolerance && Math.Abs(current.X - next.X) <= coordinateTolerance; var sameY = Math.Abs(previous.Y - current.Y) <= coordinateTolerance && Math.Abs(current.Y - next.Y) <= coordinateTolerance; if (!sameX && !sameY) { simplified.Add(current); } } simplified.Add(deduped[^1]); return simplified; } }