Files
git.stella-ops.org/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.Finalization.TerminalCleanup.Round.cs
2026-04-01 10:35:23 +03:00

115 lines
9.9 KiB
C#

namespace StellaOps.ElkSharp;
internal static partial class ElkEdgeRouterIterative
{
private static ElkRoutedEdge[] ApplyTerminalRuleCleanupRound(
ElkRoutedEdge[] edges,
ElkPositionedNode[] nodes,
ElkLayoutDirection direction,
double minLineClearance,
IReadOnlyCollection<string>? restrictedEdgeIds = null)
{
var result = edges;
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SeparateRepeatCollectorLocalLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.PreferShortestBoundaryShortcuts(result, nodes, restrictedEdgeIds);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.ElevateRepeatCollectorNodeClearanceViolations(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkRepeatCollectorCorridors.SeparateSharedLanes(result, nodes, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.FinalizeDecisionTargetEntries(result, nodes, restrictedEdgeIds);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes, restrictedEdgeIds);
result = ClampBelowGraphEdges(result, nodes, restrictedEdgeIds);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, direction, restrictedEdgeIds);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes, restrictedEdgeIds);
result = ClampBelowGraphEdges(result, nodes, restrictedEdgeIds);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, direction, restrictedEdgeIds);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
// Final late-stage verification: source/target boundary normalization can collapse
// lanes back onto the same node face, so restabilize the local geometry once more.
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ClampBelowGraphEdges(result, nodes, restrictedEdgeIds);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, direction, restrictedEdgeIds);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.ElevateRepeatCollectorNodeClearanceViolations(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.ElevateUnderNodeViolations(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.PreferShortestBoundaryShortcuts(result, nodes, restrictedEdgeIds);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.ElevateUnderNodeViolations(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes, restrictedEdgeIds);
result = ClampBelowGraphEdges(result, nodes, restrictedEdgeIds);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, direction, restrictedEdgeIds);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.ElevateUnderNodeViolations(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
// Final hard-rule restabilization after the last normalize pass: the final
// boundary normalization can still pull target slots and horizontal lanes back
// into a bad state, so re-apply the local rule fixers once more before scoring.
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.ElevateRepeatCollectorNodeClearanceViolations(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.ElevateUnderNodeViolations(result, nodes, minLineClearance, restrictedEdgeIds);
result = ClampBelowGraphEdges(result, nodes, restrictedEdgeIds);
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, direction, restrictedEdgeIds);
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes, snapToSlots: true);
result = CloseRemainingTerminalViolations(result, nodes, direction, minLineClearance, restrictedEdgeIds);
var lateDetourShortcuts = ElkEdgePostProcessor.PreferShortestBoundaryShortcuts(result, nodes, restrictedEdgeIds);
result = ElkEdgeRoutingScoring.CountBoundarySlotViolations(result, nodes) > 0
? ChoosePreferredBoundarySlotRepairLayout(result, lateDetourShortcuts, nodes)
: ChoosePreferredHardRuleLayout(result, lateDetourShortcuts, nodes);
result = ApplyFinalDetourPolish(result, nodes, direction, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
result,
nodes,
minLineClearance,
restrictedEdgeIds,
enforceAllNodeEndpoints: true);
result = ApplyPostSlotDetourClosure(result, nodes, minLineClearance, restrictedEdgeIds);
result = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
result,
nodes,
minLineClearance,
restrictedEdgeIds,
enforceAllNodeEndpoints: true);
return result;
}
}