using System.Collections.Concurrent; using System.Diagnostics; using System.Globalization; namespace StellaOps.ElkSharp; internal static partial class ElkEdgeRouterIterative { private static ElkRoutedEdge[] ApplyLateBoundarySlotRestabilization( ElkRoutedEdge[] edges, ElkPositionedNode[] nodes, double minLineClearance, IReadOnlyCollection focusEdgeIds) { if (focusEdgeIds.Count == 0) { return edges; } var candidate = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts( edges, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after mixed-face separation"); candidate = ElkEdgePostProcessor.SeparateRepeatCollectorLocalLaneConflicts( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after collector separation"); candidate = ElkEdgePostProcessor.SpreadSourceDepartureJoins( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after source-join spread"); candidate = ElkEdgePostProcessor.SeparateSharedLaneConflicts( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after shared-lane separation"); candidate = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after boundary/target repair"); candidate = ElkEdgePostProcessor.SpreadTargetApproachJoins( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after target-join spread"); candidate = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after feeder-band spread"); candidate = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(candidate, nodes, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after gateway finalize"); candidate = ElkEdgePostProcessor.NormalizeBoundaryAngles(candidate, nodes); candidate = ElkEdgePostProcessor.NormalizeSourceExitAngles(candidate, nodes); candidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments( candidate, nodes, minLineClearance, focusEdgeIds, enforceAllNodeEndpoints: true); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after first normalization snap"); candidate = ElkEdgePostProcessor.SeparateRepeatCollectorLocalLaneConflicts( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after second collector separation"); candidate = ElkEdgePostProcessor.SpreadSourceDepartureJoins( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after second source-join spread"); candidate = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after second mixed-face separation"); candidate = ElkEdgePostProcessor.SeparateSharedLaneConflicts( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after second shared-lane separation"); candidate = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after second boundary/target repair"); candidate = ElkEdgePostProcessor.SpreadTargetApproachJoins( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after second target-join spread"); candidate = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands( candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after second feeder-band spread"); candidate = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(candidate, nodes, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after second gateway finalize"); candidate = ApplyPostSlotDetourClosure(candidate, nodes, minLineClearance, focusEdgeIds); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization after detour closure"); candidate = ElkEdgePostProcessor.NormalizeBoundaryAngles(candidate, nodes); candidate = ElkEdgePostProcessor.NormalizeSourceExitAngles(candidate, nodes); candidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments( candidate, nodes, minLineClearance, focusEdgeIds, enforceAllNodeEndpoints: true); ElkLayoutDiagnostics.LogProgress("Late boundary-slot restabilization complete"); return ChoosePreferredBoundarySlotRepairLayout(edges, candidate, nodes); } private static ElkRoutedEdge[] ApplyLateFocusedBoundarySlotClosure( ElkRoutedEdge[] edges, ElkPositionedNode[] nodes, ElkLayoutDirection direction, double minLineClearance, IReadOnlyCollection focusEdgeIds, IReadOnlyCollection? restrictedEdgeIds) { var candidate = ChoosePreferredHardRuleLayout( edges, ApplyAggressiveSharedLaneClosure(edges, nodes, direction, minLineClearance, focusEdgeIds), nodes); candidate = ChoosePreferredBoundarySlotRepairLayout( candidate, ElkEdgePostProcessor.SnapBoundarySlotAssignments( candidate, nodes, minLineClearance, restrictedEdgeIds, enforceAllNodeEndpoints: true), nodes); candidate = ChoosePreferredBoundarySlotRepairLayout( candidate, CloseRemainingTerminalViolations(candidate, nodes, direction, minLineClearance, restrictedEdgeIds), nodes); candidate = ApplyLateBoundarySlotRestabilization(candidate, nodes, minLineClearance, focusEdgeIds); candidate = ChoosePreferredBoundarySlotRepairLayout( candidate, ElkEdgePostProcessor.SnapBoundarySlotAssignments( candidate, nodes, minLineClearance, restrictedEdgeIds, enforceAllNodeEndpoints: true), nodes); return candidate; } }