namespace StellaOps.ElkSharp; internal static partial class ElkEdgeRouterIterative { private static CandidateSolution? TryBoundaryFirstBaseline( ElkRoutedEdge[] baselineEdges, ElkPositionedNode[] nodes, ElkLayoutOptions layoutOptions, RoutingStrategy strategy, double minLineClearance, CancellationToken cancellationToken) { var graphMinY = nodes.Length > 0 ? nodes.Min(n => n.Y) : 0d; var graphMaxY = nodes.Length > 0 ? nodes.Max(n => n.Y + n.Height) : 0d; // Phase 1: Compute boundary slot assignments for all routable edges // (needed for correct slot positions), then filter to only violating edges. var allAssignments = ComputeGlobalBoundarySlotAssignments( baselineEdges, nodes, graphMinY, graphMaxY); if (allAssignments.Count == 0) { return null; } // Identify edges with violations in the baseline. var violatingSeverity = new Dictionary(StringComparer.Ordinal); ElkEdgeRoutingScoring.CountBadBoundaryAngles(baselineEdges, nodes, violatingSeverity, 10); ElkEdgeRoutingScoring.CountGatewaySourceExitViolations(baselineEdges, nodes, violatingSeverity, 10); ElkEdgeRoutingScoring.CountTargetApproachJoinViolations(baselineEdges, nodes, violatingSeverity, 10); ElkEdgeRoutingScoring.CountTargetApproachBacktrackingViolations(baselineEdges, nodes, violatingSeverity, 10); ElkEdgeRoutingScoring.CountSharedLaneViolations(baselineEdges, nodes, violatingSeverity, 10); ElkEdgeRoutingScoring.CountBoundarySlotViolations(baselineEdges, nodes, violatingSeverity, 10); ElkEdgeRoutingScoring.CountUnderNodeViolations(baselineEdges, nodes, violatingSeverity, 10); ElkEdgeRoutingScoring.CountExcessiveDetourViolations(baselineEdges, nodes, violatingSeverity, 10); ElkEdgeRoutingScoring.CountBelowGraphViolations(baselineEdges, nodes, violatingSeverity, 10); if (violatingSeverity.Count == 0) { return null; } // Expand to include neighbor edges sharing source/target nodes. var repairEdgeIds = ExpandWinningSolutionFocus(baselineEdges, violatingSeverity.Keys) .ToHashSet(StringComparer.Ordinal); // Filter assignments to only repair edges. var assignments = new Dictionary(StringComparer.Ordinal); foreach (var (edgeId, assignment) in allAssignments) { if (repairEdgeIds.Contains(edgeId)) { assignments[edgeId] = assignment; } } if (assignments.Count == 0) { return null; } ElkLayoutDiagnostics.LogProgress( $"Boundary-first: {allAssignments.Count} slots computed, {assignments.Count}/{repairEdgeIds.Count} edges targeted for repair"); // Phase 2: Route only the targeted edges between pre-assigned boundary slots. // Non-targeted edges keep their baseline paths and contribute soft obstacles. var routed = RouteBoundaryFirstEdges( baselineEdges, nodes, assignments, strategy, cancellationToken); ElkLayoutDiagnostics.LogProgress( $"Boundary-first: routed={routed.Diagnostics.RoutedEdges} " + $"skipped={routed.Diagnostics.SkippedEdges} " + $"sections={routed.Diagnostics.RoutedSections} " + $"fallback={routed.Diagnostics.FallbackSections}"); // Phase 3: Apply lean verification pass. var verified = ApplyBoundaryFirstVerification( routed.Edges, baselineEdges, nodes, layoutOptions.Direction, minLineClearance); // Score and build candidate. var score = ElkEdgeRoutingScoring.ComputeScore(verified, nodes); var brokenHighways = HighwayProcessingEnabled ? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(verified, nodes).Count : 0; var retryState = BuildRetryState(score, brokenHighways); ElkLayoutDiagnostics.LogProgress( $"Boundary-first result: score={score.Value:F0} retry={DescribeRetryState(retryState)}"); // Record in diagnostics if available. var diagnostics = ElkLayoutDiagnostics.Current; if (diagnostics is not null) { lock (diagnostics.SyncRoot) { diagnostics.IterativeStrategies.Add(new ElkIterativeStrategyDiagnostics { StrategyIndex = -1, OrderingName = "boundary-first", Attempts = 1, BestScore = score, Outcome = retryState.RequiresPrimaryRetry ? $"retry({DescribeRetryState(retryState)})" : "valid", BestEdges = verified, }); } ElkLayoutDiagnostics.FlushSnapshot(diagnostics); } return new CandidateSolution(score, retryState, verified, -1); } }