using System.Collections.Concurrent; using System.Diagnostics; using System.Globalization; namespace StellaOps.ElkSharp; internal static partial class ElkEdgeRouterIterative { private static IEnumerable GenerateStrategies( ElkRoutedEdge[] edges, ElkPositionedNode[] nodes, IterativeRoutingConfig config, double minLineClearance) { var nodesById = nodes.ToDictionary(n => n.Id, StringComparer.Ordinal); var connectionCount = new Dictionary(StringComparer.Ordinal); foreach (var edge in edges) { var srcId = edge.SourceNodeId ?? ""; var tgtId = edge.TargetNodeId ?? ""; connectionCount[srcId] = connectionCount.GetValueOrDefault(srcId) + 1; connectionCount[tgtId] = connectionCount.GetValueOrDefault(tgtId) + 1; } var orderings = new[] { OrderByLongestFirst(edges, nodesById), OrderByShortestFirst(edges, nodesById), OrderByMostConnectedFirst(edges, connectionCount), OrderByReverse(edges), }; // Strategies 1-4: base params, clearance = half avg node dimension var baseParams = new AStarRoutingParams(18d, 200d, 500d, 2.0d, minLineClearance, 40d, true); foreach (var order in orderings) { yield return new RoutingStrategy { EdgeOrder = order, BaseLineClearance = minLineClearance, MinLineClearance = minLineClearance, RoutingParams = baseParams, }; } // Strategies 5-8: higher penalties and tighter clearance var highParams = new AStarRoutingParams(18d, 400d, 600d, 3.0d, minLineClearance, 40d, true); foreach (var order in orderings) { yield return new RoutingStrategy { EdgeOrder = order, BaseLineClearance = minLineClearance, MinLineClearance = minLineClearance, RoutingParams = highParams, }; } // Strategies 9+: seeded random with clearance for (var i = 0; i < 8; i++) { var seed = HashCode.Combine(edges.Length, nodes.Length, i + 8); var rng = new Random(seed); var order = Enumerable.Range(0, edges.Length).ToArray(); Shuffle(order, rng); var randomParams = new AStarRoutingParams( 18d, 80d + (rng.NextDouble() * 420d), 300d + (rng.NextDouble() * 500d), 1.0d + (rng.NextDouble() * 3.0d), minLineClearance * (0.5d + rng.NextDouble()), 40d, true); yield return new RoutingStrategy { EdgeOrder = order, BaseLineClearance = minLineClearance, MinLineClearance = minLineClearance, RoutingParams = randomParams, }; } } private static RoutingRetryState BuildRetryState(EdgeRoutingScore score, int remainingBrokenHighways) { return new RoutingRetryState( RemainingShortHighways: remainingBrokenHighways, RepeatCollectorCorridorViolations: score.RepeatCollectorCorridorViolations, RepeatCollectorNodeClearanceViolations: score.RepeatCollectorNodeClearanceViolations, TargetApproachJoinViolations: score.TargetApproachJoinViolations, TargetApproachBacktrackingViolations: score.TargetApproachBacktrackingViolations, ExcessiveDetourViolations: score.ExcessiveDetourViolations, SharedLaneViolations: score.SharedLaneViolations, BoundarySlotViolations: score.BoundarySlotViolations, BelowGraphViolations: score.BelowGraphViolations, UnderNodeViolations: score.UnderNodeViolations, LongDiagonalViolations: score.LongDiagonalViolations, ProximityViolations: score.ProximityViolations, EntryAngleViolations: score.EntryAngleViolations, GatewaySourceExitViolations: score.GatewaySourceExitViolations, LabelProximityViolations: score.LabelProximityViolations, EdgeCrossings: score.EdgeCrossings); } private static string DescribeRetryState(RoutingRetryState retryState) { var parts = new List(5); if (retryState.RemainingShortHighways > 0) { parts.Add($"short-highways={retryState.RemainingShortHighways}"); } if (retryState.RepeatCollectorCorridorViolations > 0) { parts.Add($"collector-corridors={retryState.RepeatCollectorCorridorViolations}"); } if (retryState.RepeatCollectorNodeClearanceViolations > 0) { parts.Add($"collector-clearance={retryState.RepeatCollectorNodeClearanceViolations}"); } if (retryState.TargetApproachJoinViolations > 0) { parts.Add($"target-joins={retryState.TargetApproachJoinViolations}"); } if (retryState.TargetApproachBacktrackingViolations > 0) { parts.Add($"approach-backtracking={retryState.TargetApproachBacktrackingViolations}"); } if (retryState.ExcessiveDetourViolations > 0) { parts.Add($"detour={retryState.ExcessiveDetourViolations}"); } if (retryState.GatewaySourceExitViolations > 0) { parts.Add($"gateway-source={retryState.GatewaySourceExitViolations}"); } if (retryState.SharedLaneViolations > 0) { parts.Add($"shared-lanes={retryState.SharedLaneViolations}"); } if (retryState.BoundarySlotViolations > 0) { parts.Add($"boundary-slots={retryState.BoundarySlotViolations}"); } if (retryState.BelowGraphViolations > 0) { parts.Add($"below-graph={retryState.BelowGraphViolations}"); } if (retryState.UnderNodeViolations > 0) { parts.Add($"under-node={retryState.UnderNodeViolations}"); } if (retryState.LongDiagonalViolations > 0) { parts.Add($"long-diagonal={retryState.LongDiagonalViolations}"); } if (retryState.ProximityViolations > 0) { parts.Add($"proximity={retryState.ProximityViolations}"); } if (retryState.EntryAngleViolations > 0) { parts.Add($"entry={retryState.EntryAngleViolations}"); } if (retryState.LabelProximityViolations > 0) { parts.Add($"label={retryState.LabelProximityViolations}"); } if (retryState.EdgeCrossings > 0) { parts.Add($"edge-crossings={retryState.EdgeCrossings}"); } return parts.Count > 0 ? string.Join(", ", parts) : "none"; } }