Files
git.stella-ops.org/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.Finalization.cs

181 lines
12 KiB
C#

using System.Collections.Concurrent;
using System.Diagnostics;
using System.Globalization;
namespace StellaOps.ElkSharp;
internal static partial class ElkEdgeRouterIterative
{
private static ElkRoutedEdge[] ApplyPostProcessing(
ElkRoutedEdge[] edges,
ElkPositionedNode[] nodes,
ElkLayoutOptions layoutOptions,
bool preferHybridTerminalCleanup = false)
{
if (preferHybridTerminalCleanup)
{
return ApplyLeanHybridBaselinePostProcessing(edges, nodes, layoutOptions);
}
var result = ElkEdgePostProcessor.AvoidNodeCrossings(edges, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.EliminateDiagonalSegments(result, nodes);
result = ElkEdgePostProcessorSimplify.SimplifyEdgePaths(result, nodes);
result = ElkEdgePostProcessorSimplify.TightenOuterCorridors(result, nodes);
if (HighwayProcessingEnabled)
{
result = ElkEdgeRouterHighway.BreakShortHighways(result, nodes);
}
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessorSimplify.SimplifyEdgePaths(result, nodes);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
var serviceNodes = nodes.Where(n => n.Kind is not "Start" and not "End").ToArray();
var minLineClearance = serviceNodes.Length > 0
? Math.Min(serviceNodes.Average(n => n.Width), serviceNodes.Average(n => n.Height)) / 2d
: 50d;
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.ElevateUnderNodeViolations(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = RestoreProtectedRepeatCollectorCorridors(result, edges, nodes);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ClampBelowGraphEdges(result, nodes);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.FinalizeDecisionTargetEntries(result, nodes);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes);
result = ElkEdgePostProcessor.FinalizeDecisionTargetEntries(result, nodes);
result = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SeparateRepeatCollectorLocalLaneConflicts(result, nodes, minLineClearance);
result = ClampBelowGraphEdges(result, nodes);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, layoutOptions.Direction);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.ElevateRepeatCollectorNodeClearanceViolations(result, nodes, minLineClearance);
result = ElkRepeatCollectorCorridors.SeparateSharedLanes(result, nodes);
result = ElkEdgePostProcessor.SeparateRepeatCollectorLocalLaneConflicts(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.PreferShortestBoundaryShortcuts(result, nodes);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
// The final hard-rule closure must end on lane separation so later
// boundary slot normalizers cannot collapse a repaired handoff strip
// back onto the same effective rail.
result = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance);
result = ClampBelowGraphEdges(result, nodes);
result = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
result,
nodes,
minLineClearance,
enforceAllNodeEndpoints: true);
result = ApplyPostSlotDetourClosure(result, nodes, minLineClearance);
result = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
result,
nodes,
minLineClearance,
enforceAllNodeEndpoints: true);
var score = ElkEdgeRoutingScoring.ComputeScore(result, nodes);
var remainingBrokenHighways = HighwayProcessingEnabled
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(result, nodes).Count
: 0;
var retryState = BuildRetryState(score, remainingBrokenHighways);
if (retryState.RequiresBlockingRetry || retryState.RequiresLengthRetry)
{
var stabilized = preferHybridTerminalCleanup
? ApplyHybridTerminalRuleCleanupRound(
result,
nodes,
layoutOptions.Direction,
minLineClearance)
: ApplyTerminalRuleCleanupRound(
result,
nodes,
layoutOptions.Direction,
minLineClearance);
var stabilizedScore = ElkEdgeRoutingScoring.ComputeScore(stabilized, nodes);
var stabilizedBrokenHighways = HighwayProcessingEnabled
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(stabilized, nodes).Count
: 0;
var stabilizedRetryState = BuildRetryState(stabilizedScore, stabilizedBrokenHighways);
if (IsBetterBoundarySlotRepairCandidate(
stabilizedScore,
stabilizedRetryState,
score,
retryState))
{
result = stabilized;
}
}
return result;
}
}