Refactor ElkSharp hybrid routing and document speed path

This commit is contained in:
master
2026-03-29 19:33:46 +03:00
parent 7d6bc2b0ab
commit e8f7ad7652
89 changed files with 13280 additions and 10732 deletions

View File

@@ -0,0 +1,154 @@
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;
}
}