147 lines
6.4 KiB
C#
147 lines
6.4 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
|
|
namespace StellaOps.ElkSharp;
|
|
|
|
internal static partial class ElkEdgeRouterIterative
|
|
{
|
|
private static CandidateSolution ApplyFinalSharedLanePolish(
|
|
CandidateSolution solution,
|
|
ElkPositionedNode[] nodes,
|
|
ElkLayoutDirection direction,
|
|
double minLineClearance,
|
|
bool preferLeanTerminalCleanup = false)
|
|
{
|
|
var current = solution;
|
|
if (current.RetryState.SharedLaneViolations <= 0)
|
|
{
|
|
return current;
|
|
}
|
|
|
|
var maxRounds = preferLeanTerminalCleanup ? 1 : 3;
|
|
for (var round = 0; round < maxRounds; round++)
|
|
{
|
|
var sharedLaneSeverity = new Dictionary<string, int>(StringComparer.Ordinal);
|
|
ElkEdgeRoutingScoring.CountSharedLaneViolations(current.Edges, nodes, sharedLaneSeverity, 10);
|
|
if (sharedLaneSeverity.Count == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var improved = false;
|
|
foreach (var edgeId in sharedLaneSeverity
|
|
.OrderByDescending(pair => pair.Value)
|
|
.ThenBy(pair => pair.Key, StringComparer.Ordinal)
|
|
.Select(pair => pair.Key))
|
|
{
|
|
var focusEdgeIds = ExpandSharedLanePolishFocus(current.Edges, nodes, edgeId).ToArray();
|
|
if (focusEdgeIds.Length == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var directCandidate = ElkEdgePostProcessor.SeparateSharedLaneConflicts(
|
|
current.Edges,
|
|
nodes,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
var directClosureCandidate = preferLeanTerminalCleanup
|
|
? ApplyHybridTerminalRuleCleanupRound(
|
|
directCandidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds)
|
|
: CloseRemainingTerminalViolations(
|
|
directCandidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
var closureCandidate = preferLeanTerminalCleanup
|
|
? ApplyHybridTerminalRuleCleanupRound(
|
|
current.Edges,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds)
|
|
: CloseRemainingTerminalViolations(
|
|
current.Edges,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
var aggressiveCandidate = ApplyAggressiveSharedLaneClosure(
|
|
current.Edges,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
var directRetryState = BuildRetryState(
|
|
ElkEdgeRoutingScoring.ComputeScore(directCandidate, nodes),
|
|
HighwayProcessingEnabled
|
|
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(directCandidate, nodes).Count
|
|
: 0);
|
|
var directClosureRetryState = BuildRetryState(
|
|
ElkEdgeRoutingScoring.ComputeScore(directClosureCandidate, nodes),
|
|
HighwayProcessingEnabled
|
|
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(directClosureCandidate, nodes).Count
|
|
: 0);
|
|
var closureRetryState = BuildRetryState(
|
|
ElkEdgeRoutingScoring.ComputeScore(closureCandidate, nodes),
|
|
HighwayProcessingEnabled
|
|
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(closureCandidate, nodes).Count
|
|
: 0);
|
|
var aggressiveRetryState = BuildRetryState(
|
|
ElkEdgeRoutingScoring.ComputeScore(aggressiveCandidate, nodes),
|
|
HighwayProcessingEnabled
|
|
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(aggressiveCandidate, nodes).Count
|
|
: 0);
|
|
ElkLayoutDiagnostics.LogProgress(
|
|
$"Winner shared-lane focus edge={edgeId} focus=[{string.Join(", ", focusEdgeIds)}] " +
|
|
$"direct={DescribeRetryState(directRetryState)} " +
|
|
$"direct-closure={DescribeRetryState(directClosureRetryState)} " +
|
|
$"closure={DescribeRetryState(closureRetryState)} " +
|
|
$"aggressive={DescribeRetryState(aggressiveRetryState)}");
|
|
var candidateEdges = ChoosePreferredSharedLanePolishLayout(directCandidate, directClosureCandidate, nodes);
|
|
candidateEdges = ChoosePreferredSharedLanePolishLayout(candidateEdges, closureCandidate, nodes);
|
|
candidateEdges = ChoosePreferredSharedLanePolishLayout(candidateEdges, aggressiveCandidate, nodes);
|
|
candidateEdges = ChoosePreferredSharedLanePolishLayout(current.Edges, candidateEdges, nodes);
|
|
|
|
var candidateScore = ElkEdgeRoutingScoring.ComputeScore(candidateEdges, nodes);
|
|
var candidateRetryState = BuildRetryState(
|
|
candidateScore,
|
|
HighwayProcessingEnabled
|
|
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(candidateEdges, nodes).Count
|
|
: 0);
|
|
|
|
var improvedSharedLanes = candidateRetryState.SharedLaneViolations < current.RetryState.SharedLaneViolations;
|
|
if (HasBlockingSharedLanePromotionRegression(candidateRetryState, current.RetryState)
|
|
|| (!improvedSharedLanes
|
|
&& !IsBetterCandidate(candidateScore, candidateRetryState, current.Score, current.RetryState)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
current = current with
|
|
{
|
|
Score = candidateScore,
|
|
RetryState = candidateRetryState,
|
|
Edges = candidateEdges,
|
|
};
|
|
improved = true;
|
|
break;
|
|
}
|
|
|
|
if (!improved)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
}
|