ElkSharp: gateway face overflow redirect, under-node push-first routing, boundary-slot snap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ internal static partial class ElkEdgeRouterIterative
|
||||
private static ElkRoutedEdge[] ApplyFinalDetourPolish(
|
||||
ElkRoutedEdge[] edges,
|
||||
ElkPositionedNode[] nodes,
|
||||
ElkLayoutDirection direction,
|
||||
double minLineClearance,
|
||||
IReadOnlyCollection<string>? restrictedEdgeIds)
|
||||
{
|
||||
@@ -40,38 +41,94 @@ internal static partial class ElkEdgeRouterIterative
|
||||
continue;
|
||||
}
|
||||
|
||||
var focused = (IReadOnlyCollection<string>)[edgeId];
|
||||
var candidateEdges = ComposeTransactionalFinalDetourCandidate(
|
||||
result,
|
||||
nodes,
|
||||
minLineClearance,
|
||||
focused);
|
||||
candidateEdges = ChoosePreferredHardRuleLayout(result, candidateEdges, nodes);
|
||||
if (ReferenceEquals(candidateEdges, result))
|
||||
var directFocus = (IReadOnlyCollection<string>)[edgeId];
|
||||
var expandedFocus = ExpandWinningSolutionFocus(result, [edgeId])
|
||||
.Where(id => restrictedSet is null || restrictedSet.Contains(id))
|
||||
.OrderBy(id => id, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
if (expandedFocus.Length == 0)
|
||||
{
|
||||
expandedFocus = [edgeId];
|
||||
}
|
||||
|
||||
var bestCandidateEdges = result;
|
||||
var bestCandidateScore = currentScore;
|
||||
var bestCandidateRetryState = currentRetryState;
|
||||
|
||||
void ConsiderDetourCandidate(ElkRoutedEdge[] candidate)
|
||||
{
|
||||
candidate = ChoosePreferredHardRuleLayout(result, candidate, nodes);
|
||||
if (ReferenceEquals(candidate, result))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var candidateScore = ElkEdgeRoutingScoring.ComputeScore(candidate, nodes);
|
||||
var candidateRetryState = BuildRetryState(
|
||||
candidateScore,
|
||||
HighwayProcessingEnabled
|
||||
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(candidate, nodes).Count
|
||||
: 0);
|
||||
|
||||
var improvedDetours = candidateRetryState.ExcessiveDetourViolations < currentRetryState.ExcessiveDetourViolations;
|
||||
if (HasHardRuleRegression(candidateRetryState, currentRetryState)
|
||||
|| (!improvedDetours
|
||||
&& !IsBetterBoundarySlotRepairCandidate(
|
||||
candidateScore,
|
||||
candidateRetryState,
|
||||
currentScore,
|
||||
currentRetryState)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(bestCandidateEdges, result)
|
||||
|| IsBetterCandidate(candidateScore, candidateRetryState, bestCandidateScore, bestCandidateRetryState))
|
||||
{
|
||||
bestCandidateEdges = candidate;
|
||||
bestCandidateScore = candidateScore;
|
||||
bestCandidateRetryState = candidateRetryState;
|
||||
}
|
||||
}
|
||||
|
||||
ConsiderDetourCandidate(
|
||||
ComposeDirectionalTransactionalFinalDetourCandidate(
|
||||
result,
|
||||
nodes,
|
||||
direction,
|
||||
minLineClearance,
|
||||
directFocus));
|
||||
|
||||
if (expandedFocus.Length != 1
|
||||
|| !string.Equals(expandedFocus[0], edgeId, StringComparison.Ordinal))
|
||||
{
|
||||
ConsiderDetourCandidate(
|
||||
ComposeDirectionalTransactionalFinalDetourCandidate(
|
||||
result,
|
||||
nodes,
|
||||
direction,
|
||||
minLineClearance,
|
||||
expandedFocus));
|
||||
}
|
||||
|
||||
var focusedSeed = ElkEdgePostProcessor.PreferShortestBoundaryShortcuts(result, nodes, directFocus);
|
||||
if (!ReferenceEquals(focusedSeed, result))
|
||||
{
|
||||
ConsiderDetourCandidate(
|
||||
BuildFinalRestabilizedCandidate(
|
||||
focusedSeed,
|
||||
nodes,
|
||||
direction,
|
||||
minLineClearance,
|
||||
expandedFocus));
|
||||
}
|
||||
|
||||
if (ReferenceEquals(bestCandidateEdges, result))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var candidateScore = ElkEdgeRoutingScoring.ComputeScore(candidateEdges, nodes);
|
||||
var candidateRetryState = BuildRetryState(
|
||||
candidateScore,
|
||||
HighwayProcessingEnabled
|
||||
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(candidateEdges, nodes).Count
|
||||
: 0);
|
||||
|
||||
var improvedDetours = candidateRetryState.ExcessiveDetourViolations < currentRetryState.ExcessiveDetourViolations;
|
||||
if (HasHardRuleRegression(candidateRetryState, currentRetryState)
|
||||
|| (!improvedDetours
|
||||
&& !IsBetterBoundarySlotRepairCandidate(
|
||||
candidateScore,
|
||||
candidateRetryState,
|
||||
currentScore,
|
||||
currentRetryState)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result = candidateEdges;
|
||||
result = bestCandidateEdges;
|
||||
improved = true;
|
||||
break;
|
||||
}
|
||||
@@ -128,6 +185,21 @@ internal static partial class ElkEdgeRouterIterative
|
||||
ElkPositionedNode[] nodes,
|
||||
double minLineClearance,
|
||||
IReadOnlyCollection<string> focusedEdgeIds)
|
||||
{
|
||||
return ComposeDirectionalTransactionalFinalDetourCandidate(
|
||||
baseline,
|
||||
nodes,
|
||||
ElkLayoutDirection.LeftToRight,
|
||||
minLineClearance,
|
||||
focusedEdgeIds);
|
||||
}
|
||||
|
||||
private static ElkRoutedEdge[] ComposeDirectionalTransactionalFinalDetourCandidate(
|
||||
ElkRoutedEdge[] baseline,
|
||||
ElkPositionedNode[] nodes,
|
||||
ElkLayoutDirection direction,
|
||||
double minLineClearance,
|
||||
IReadOnlyCollection<string> focusedEdgeIds)
|
||||
{
|
||||
var candidate = ElkEdgePostProcessor.PreferShortestBoundaryShortcuts(baseline, nodes, focusedEdgeIds);
|
||||
if (ReferenceEquals(candidate, baseline))
|
||||
@@ -163,7 +235,12 @@ internal static partial class ElkEdgeRouterIterative
|
||||
focusedEdgeIds,
|
||||
enforceAllNodeEndpoints: true);
|
||||
candidate = ApplyPostSlotDetourClosure(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
return candidate;
|
||||
return BuildFinalRestabilizedCandidate(
|
||||
candidate,
|
||||
nodes,
|
||||
direction,
|
||||
minLineClearance,
|
||||
focusedEdgeIds);
|
||||
}
|
||||
|
||||
internal static ElkRoutedEdge[] ApplyPostSlotDetourClosure(
|
||||
|
||||
Reference in New Issue
Block a user