Refactor ElkSharp hybrid routing and document speed path
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
namespace StellaOps.ElkSharp;
|
||||
|
||||
internal static partial class ElkEdgeRouterIterative
|
||||
{
|
||||
private static ElkRoutedEdge[] ApplyFinalDetourPolish(
|
||||
ElkRoutedEdge[] edges,
|
||||
ElkPositionedNode[] nodes,
|
||||
double minLineClearance,
|
||||
IReadOnlyCollection<string>? restrictedEdgeIds)
|
||||
{
|
||||
var restrictedSet = restrictedEdgeIds is null
|
||||
? null
|
||||
: restrictedEdgeIds.ToHashSet(StringComparer.Ordinal);
|
||||
var result = edges;
|
||||
|
||||
for (var round = 0; round < 3; round++)
|
||||
{
|
||||
var detourSeverity = new Dictionary<string, int>(StringComparer.Ordinal);
|
||||
ElkEdgeRoutingScoring.CountExcessiveDetourViolations(result, nodes, detourSeverity, 10);
|
||||
if (detourSeverity.Count == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var currentScore = ElkEdgeRoutingScoring.ComputeScore(result, nodes);
|
||||
var currentRetryState = BuildRetryState(
|
||||
currentScore,
|
||||
HighwayProcessingEnabled
|
||||
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(result, nodes).Count
|
||||
: 0);
|
||||
|
||||
var improved = false;
|
||||
foreach (var edgeId in detourSeverity
|
||||
.OrderByDescending(pair => pair.Value)
|
||||
.ThenBy(pair => pair.Key, StringComparer.Ordinal)
|
||||
.Select(pair => pair.Key))
|
||||
{
|
||||
if (restrictedSet is not null && !restrictedSet.Contains(edgeId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var focused = (IReadOnlyCollection<string>)[edgeId];
|
||||
var candidateEdges = ComposeTransactionalFinalDetourCandidate(
|
||||
result,
|
||||
nodes,
|
||||
minLineClearance,
|
||||
focused);
|
||||
candidateEdges = ChoosePreferredHardRuleLayout(result, candidateEdges, nodes);
|
||||
if (ReferenceEquals(candidateEdges, 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;
|
||||
improved = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!improved)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool TryPromoteFinalDetourCandidate(
|
||||
ElkRoutedEdge[] baselineEdges,
|
||||
ElkRoutedEdge[] candidateEdges,
|
||||
ElkPositionedNode[] nodes,
|
||||
EdgeRoutingScore baselineScore,
|
||||
RoutingRetryState baselineRetryState,
|
||||
out ElkRoutedEdge[] promotedEdges)
|
||||
{
|
||||
promotedEdges = baselineEdges;
|
||||
if (ReferenceEquals(candidateEdges, baselineEdges))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var candidateScore = ElkEdgeRoutingScoring.ComputeScore(candidateEdges, nodes);
|
||||
var candidateRetryState = BuildRetryState(
|
||||
candidateScore,
|
||||
HighwayProcessingEnabled
|
||||
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(candidateEdges, nodes).Count
|
||||
: 0);
|
||||
|
||||
var improvedDetours = candidateRetryState.ExcessiveDetourViolations < baselineRetryState.ExcessiveDetourViolations;
|
||||
var improvedGatewaySource = candidateRetryState.GatewaySourceExitViolations < baselineRetryState.GatewaySourceExitViolations;
|
||||
if (HasHardRuleRegression(candidateRetryState, baselineRetryState)
|
||||
|| (!(improvedDetours || improvedGatewaySource)
|
||||
&& !IsBetterBoundarySlotRepairCandidate(
|
||||
candidateScore,
|
||||
candidateRetryState,
|
||||
baselineScore,
|
||||
baselineRetryState)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
promotedEdges = candidateEdges;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ElkRoutedEdge[] ComposeTransactionalFinalDetourCandidate(
|
||||
ElkRoutedEdge[] baseline,
|
||||
ElkPositionedNode[] nodes,
|
||||
double minLineClearance,
|
||||
IReadOnlyCollection<string> focusedEdgeIds)
|
||||
{
|
||||
var candidate = ElkEdgePostProcessor.PreferShortestBoundaryShortcuts(baseline, nodes, focusedEdgeIds);
|
||||
if (ReferenceEquals(candidate, baseline))
|
||||
{
|
||||
return baseline;
|
||||
}
|
||||
|
||||
candidate = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SpreadTargetApproachJoins(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.ElevateUnderNodeViolations(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ClampBelowGraphEdges(candidate, nodes, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SpreadTargetApproachJoins(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.FinalizeDecisionTargetEntries(candidate, nodes, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(candidate, nodes, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.PolishTargetPeerConflicts(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
||||
candidate,
|
||||
nodes,
|
||||
minLineClearance,
|
||||
focusedEdgeIds,
|
||||
enforceAllNodeEndpoints: true);
|
||||
candidate = ApplyPostSlotDetourClosure(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ApplyLateBoundarySlotRestabilization(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SeparateSharedLaneConflicts(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
||||
candidate,
|
||||
nodes,
|
||||
minLineClearance,
|
||||
focusedEdgeIds,
|
||||
enforceAllNodeEndpoints: true);
|
||||
candidate = ApplyPostSlotDetourClosure(candidate, nodes, minLineClearance, focusedEdgeIds);
|
||||
return candidate;
|
||||
}
|
||||
|
||||
internal static ElkRoutedEdge[] ApplyPostSlotDetourClosure(
|
||||
ElkRoutedEdge[] edges,
|
||||
ElkPositionedNode[] nodes,
|
||||
double minLineClearance,
|
||||
IReadOnlyCollection<string>? restrictedEdgeIds = null)
|
||||
{
|
||||
var candidate = ElkEdgePostProcessor.PreferShortestBoundaryShortcuts(edges, nodes, restrictedEdgeIds);
|
||||
if (ReferenceEquals(candidate, edges))
|
||||
{
|
||||
return edges;
|
||||
}
|
||||
|
||||
candidate = ElkEdgePostProcessor.NormalizeBoundaryAngles(candidate, nodes);
|
||||
candidate = ElkEdgePostProcessor.NormalizeSourceExitAngles(candidate, nodes);
|
||||
candidate = ElkEdgePostProcessor.SpreadSourceDepartureJoins(candidate, nodes, minLineClearance, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(candidate, nodes, minLineClearance, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SpreadTargetApproachJoins(candidate, nodes, minLineClearance, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(candidate, nodes, minLineClearance, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.FinalizeDecisionTargetEntries(candidate, nodes, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(candidate, nodes, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.PolishTargetPeerConflicts(candidate, nodes, minLineClearance, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.ElevateUnderNodeViolations(candidate, nodes, minLineClearance, restrictedEdgeIds);
|
||||
candidate = ClampBelowGraphEdges(candidate, nodes, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.NormalizeBoundaryAngles(candidate, nodes);
|
||||
candidate = ElkEdgePostProcessor.NormalizeSourceExitAngles(candidate, nodes);
|
||||
candidate = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(candidate, nodes, minLineClearance, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SeparateSharedLaneConflicts(candidate, nodes, minLineClearance, restrictedEdgeIds);
|
||||
candidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
||||
candidate,
|
||||
nodes,
|
||||
minLineClearance,
|
||||
restrictedEdgeIds,
|
||||
enforceAllNodeEndpoints: true);
|
||||
|
||||
return ElkEdgeRoutingScoring.CountBoundarySlotViolations(edges, nodes) > 0
|
||||
? ChoosePreferredBoundarySlotRepairLayout(edges, candidate, nodes)
|
||||
: ChoosePreferredHardRuleLayout(edges, candidate, nodes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user