namespace StellaOps.ElkSharp; internal static partial class ElkEdgeRouterIterative { private static ElkRoutedEdge[] ApplyTerminalRuleCleanupRound( ElkRoutedEdge[] edges, ElkPositionedNode[] nodes, ElkLayoutDirection direction, double minLineClearance, IReadOnlyCollection? 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); 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, 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; } }