using System.Collections.Concurrent; using System.Diagnostics; using System.Globalization; namespace StellaOps.ElkSharp; internal static partial class ElkEdgeRouterIterative { private static CandidateSolution ApplyFinalDirectUnderNodePolish( CandidateSolution solution, ElkPositionedNode[] nodes, double minLineClearance) { var current = solution; var underNodeSeverity = new Dictionary(StringComparer.Ordinal); ElkEdgeRoutingScoring.CountUnderNodeViolations(current.Edges, nodes, underNodeSeverity, 10); if (underNodeSeverity.Count == 0) { return current; } foreach (var edgeId in underNodeSeverity .OrderByDescending(pair => pair.Value) .ThenBy(pair => pair.Key, StringComparer.Ordinal) .Select(pair => pair.Key)) { var candidateEdges = ElkEdgePostProcessor.ElevateUnderNodeViolations( current.Edges, nodes, minLineClearance, [edgeId]); var candidateScore = ElkEdgeRoutingScoring.ComputeScore(candidateEdges, nodes); var candidateRetryState = BuildRetryState( candidateScore, HighwayProcessingEnabled ? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(candidateEdges, nodes).Count : 0); if (!IsBetterCandidate(candidateScore, candidateRetryState, current.Score, current.RetryState)) { continue; } current = current with { Score = candidateScore, RetryState = candidateRetryState, Edges = candidateEdges, }; } return current; } private static CandidateSolution ApplyFinalProtectedLocalBundlePolish( CandidateSolution solution, ElkPositionedNode[] nodes, double minLineClearance) { var current = solution; var underNodeSeverity = new Dictionary(StringComparer.Ordinal); ElkEdgeRoutingScoring.CountUnderNodeViolations(current.Edges, nodes, underNodeSeverity, 10); var focusSeverity = new Dictionary(underNodeSeverity, StringComparer.Ordinal); ElkEdgeRoutingScoring.CountTargetApproachJoinViolations(current.Edges, nodes, focusSeverity, 10); if (focusSeverity.Count == 0) { return current; } foreach (var edgeId in focusSeverity .OrderByDescending(pair => pair.Value) .ThenBy(pair => pair.Key, StringComparer.Ordinal) .Select(pair => pair.Key)) { var focusEdgeIds = ExpandWinningSolutionFocus(current.Edges, [edgeId]).ToArray(); if (focusEdgeIds.Length == 0) { continue; } var focusedRepairSeeds = focusEdgeIds .Where(underNodeSeverity.ContainsKey) .OrderBy(id => id, StringComparer.Ordinal) .ToArray(); if (focusedRepairSeeds.Length == 0) { focusedRepairSeeds = [edgeId]; } var candidateEdges = ElkEdgePostProcessor.ElevateUnderNodeViolations( current.Edges, nodes, minLineClearance, focusedRepairSeeds); candidateEdges = ElkEdgePostProcessor.SpreadTargetApproachJoins( candidateEdges, nodes, minLineClearance, focusEdgeIds, forceOutwardAxisSpacing: true); candidateEdges = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands( candidateEdges, nodes, minLineClearance, focusEdgeIds); candidateEdges = ElkEdgePostProcessor.SeparateSharedLaneConflicts( candidateEdges, nodes, minLineClearance, focusEdgeIds); candidateEdges = ElkEdgePostProcessor.ElevateUnderNodeViolations( candidateEdges, nodes, minLineClearance, focusEdgeIds); candidateEdges = ElkEdgePostProcessor.SpreadTargetApproachJoins( candidateEdges, nodes, minLineClearance, focusEdgeIds, forceOutwardAxisSpacing: true); candidateEdges = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands( candidateEdges, nodes, minLineClearance, focusEdgeIds); candidateEdges = ChoosePreferredHardRuleLayout(current.Edges, candidateEdges, nodes); var candidateScore = ElkEdgeRoutingScoring.ComputeScore(candidateEdges, nodes); var candidateRetryState = BuildRetryState( candidateScore, HighwayProcessingEnabled ? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(candidateEdges, nodes).Count : 0); if (!IsBetterCandidate(candidateScore, candidateRetryState, current.Score, current.RetryState)) { continue; } current = current with { Score = candidateScore, RetryState = candidateRetryState, Edges = candidateEdges, }; underNodeSeverity.Clear(); ElkEdgeRoutingScoring.CountUnderNodeViolations(current.Edges, nodes, underNodeSeverity, 10); } return current; } }