155 lines
5.6 KiB
C#
155 lines
5.6 KiB
C#
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<string, int>(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<string, int>(StringComparer.Ordinal);
|
|
ElkEdgeRoutingScoring.CountUnderNodeViolations(current.Edges, nodes, underNodeSeverity, 10);
|
|
|
|
var focusSeverity = new Dictionary<string, int>(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;
|
|
}
|
|
|
|
}
|