namespace StellaOps.ElkSharp; internal static partial class ElkEdgePostProcessor { private static bool TryResolveSharedLaneByPairedNodeHandoffSlotRepair( ElkRoutedEdge[] currentEdges, int leftIndex, ElkRoutedEdge leftEdge, int rightIndex, ElkRoutedEdge rightEdge, ElkPositionedNode[] nodes, double minLineClearance, double graphMinY, double graphMaxY, out ElkRoutedEdge repairedLeftEdge, out ElkRoutedEdge repairedRightEdge) { repairedLeftEdge = leftEdge; repairedRightEdge = rightEdge; var nodesById = nodes.ToDictionary(node => node.Id, StringComparer.Ordinal); if (!TryResolveSharedLaneNodeHandoffContext(leftEdge, rightEdge, nodesById, graphMinY, graphMaxY, out var leftContext) || !TryResolveSharedLaneNodeHandoffContext(rightEdge, leftEdge, nodesById, graphMinY, graphMaxY, out var rightContext) || !string.Equals(leftContext.SharedNode.Id, rightContext.SharedNode.Id, StringComparison.Ordinal) || !string.Equals(leftContext.Side, rightContext.Side, StringComparison.Ordinal) || leftContext.IsOutgoing == rightContext.IsOutgoing) { return false; } var baselineConflicts = ElkEdgeRoutingScoring.DetectSharedLaneConflicts(currentEdges, nodes); var baselineConflictCount = baselineConflicts.Count; var baselineLeftConflictCount = baselineConflicts.Count(conflict => string.Equals(conflict.LeftEdgeId, leftEdge.Id, StringComparison.Ordinal) || string.Equals(conflict.RightEdgeId, leftEdge.Id, StringComparison.Ordinal)); var baselineRightConflictCount = baselineConflicts.Count(conflict => string.Equals(conflict.LeftEdgeId, rightEdge.Id, StringComparison.Ordinal) || string.Equals(conflict.RightEdgeId, rightEdge.Id, StringComparison.Ordinal)); var baselineCombinedPathLength = ComputePathLength(leftContext.Path) + ComputePathLength(rightContext.Path); var peerCoordinates = CollectSharedLaneNodeFaceBoundaryCoordinates( currentEdges, leftContext.SharedNode, leftContext.Side, graphMinY, graphMaxY, leftEdge.Id); var leftRepairCoordinates = EnumerateSharedLaneBoundaryRepairCoordinates( leftContext.SharedNode, leftContext.Side, leftContext.CurrentBoundaryCoordinate, peerCoordinates) .ToArray(); var rightRepairCoordinates = EnumerateSharedLaneBoundaryRepairCoordinates( rightContext.SharedNode, rightContext.Side, rightContext.CurrentBoundaryCoordinate, peerCoordinates) .ToArray(); ElkRoutedEdge? bestLeft = null; ElkRoutedEdge? bestRight = null; var bestConflictCount = baselineConflictCount; var bestLeftConflictCount = baselineLeftConflictCount; var bestRightConflictCount = baselineRightConflictCount; var bestCombinedPathLength = baselineCombinedPathLength; foreach (var leftCoordinate in leftRepairCoordinates) { var leftCandidatePath = leftContext.IsOutgoing ? BuildMixedSourceFaceCandidate(leftContext.Path, leftContext.SharedNode, leftContext.Side, leftCoordinate, leftContext.AxisValue) : BuildMixedTargetFaceCandidate(leftContext.Path, leftContext.SharedNode, leftContext.Side, leftCoordinate, leftContext.AxisValue); if (!IsValidSharedLaneBoundaryRepairCandidate( leftEdge, leftContext.Path, leftCandidatePath, leftContext.SharedNode, leftContext.IsOutgoing, nodes, graphMinY, graphMaxY)) { continue; } foreach (var rightCoordinate in rightRepairCoordinates) { var rightCandidatePath = rightContext.IsOutgoing ? BuildMixedSourceFaceCandidate(rightContext.Path, rightContext.SharedNode, rightContext.Side, rightCoordinate, rightContext.AxisValue) : BuildMixedTargetFaceCandidate(rightContext.Path, rightContext.SharedNode, rightContext.Side, rightCoordinate, rightContext.AxisValue); if (!IsValidSharedLaneBoundaryRepairCandidate( rightEdge, rightContext.Path, rightCandidatePath, rightContext.SharedNode, rightContext.IsOutgoing, nodes, graphMinY, graphMaxY)) { continue; } var candidateLeft = BuildSingleSectionEdge(leftEdge, leftCandidatePath); var candidateRight = BuildSingleSectionEdge(rightEdge, rightCandidatePath); if (ElkEdgeRoutingScoring.DetectSharedLaneConflicts([candidateLeft, candidateRight], nodes).Count > 0 || ComputeUnderNodeRepairLocalHardPressure(candidateLeft, nodes) > ComputeUnderNodeRepairLocalHardPressure(leftEdge, nodes) || ComputeUnderNodeRepairLocalHardPressure(candidateRight, nodes) > ComputeUnderNodeRepairLocalHardPressure(rightEdge, nodes)) { continue; } var candidateEdges = currentEdges.ToArray(); candidateEdges[leftIndex] = candidateLeft; candidateEdges[rightIndex] = candidateRight; var candidateConflicts = ElkEdgeRoutingScoring.DetectSharedLaneConflicts(candidateEdges, nodes); var candidateConflictCount = candidateConflicts.Count; var candidateLeftConflictCount = candidateConflicts.Count(conflict => string.Equals(conflict.LeftEdgeId, leftEdge.Id, StringComparison.Ordinal) || string.Equals(conflict.RightEdgeId, leftEdge.Id, StringComparison.Ordinal)); var candidateRightConflictCount = candidateConflicts.Count(conflict => string.Equals(conflict.LeftEdgeId, rightEdge.Id, StringComparison.Ordinal) || string.Equals(conflict.RightEdgeId, rightEdge.Id, StringComparison.Ordinal)); if (candidateConflictCount > bestConflictCount || candidateLeftConflictCount > bestLeftConflictCount || candidateRightConflictCount > bestRightConflictCount) { continue; } var candidateCombinedPathLength = ComputePathLength(leftCandidatePath) + ComputePathLength(rightCandidatePath); var isBetter = candidateConflictCount < bestConflictCount || candidateLeftConflictCount < bestLeftConflictCount || candidateRightConflictCount < bestRightConflictCount || candidateCombinedPathLength + 0.5d < bestCombinedPathLength; if (!isBetter) { continue; } bestLeft = candidateLeft; bestRight = candidateRight; bestConflictCount = candidateConflictCount; bestLeftConflictCount = candidateLeftConflictCount; bestRightConflictCount = candidateRightConflictCount; bestCombinedPathLength = candidateCombinedPathLength; } } if (bestLeft is null || bestRight is null || bestConflictCount >= baselineConflictCount) { return false; } repairedLeftEdge = bestLeft; repairedRightEdge = bestRight; return true; } private static bool TryResolveSharedLaneByDirectSourceSlotRepair( ElkRoutedEdge[] currentEdges, int repairIndex, ElkRoutedEdge edge, ElkRoutedEdge otherEdge, ElkPositionedNode[] nodes, double minLineClearance, double graphMinY, double graphMaxY, out ElkRoutedEdge repairedEdge) { repairedEdge = edge; if (string.IsNullOrWhiteSpace(edge.SourceNodeId) || !string.Equals(edge.SourceNodeId, otherEdge.SourceNodeId, StringComparison.Ordinal)) { return false; } var nodesById = nodes.ToDictionary(node => node.Id, StringComparer.Ordinal); if (!nodesById.TryGetValue(edge.SourceNodeId ?? string.Empty, out var sourceNode)) { return false; } var path = ExtractFullPath(edge); var otherPath = ExtractFullPath(otherEdge); if (path.Count < 2 || otherPath.Count < 2 || !ShouldSpreadSourceDeparture(edge, graphMinY, graphMaxY) || !ShouldSpreadSourceDeparture(otherEdge, graphMinY, graphMaxY)) { return false; } var side = ResolveSourceDepartureSide(path, sourceNode); var otherSide = ResolveSourceDepartureSide(otherPath, sourceNode); if (!string.Equals(side, otherSide, StringComparison.Ordinal)) { return false; } var axisValue = TryExtractSourceDepartureRun(path, side, out _, out var runEndIndex) ? side is "left" or "right" ? path[runEndIndex].X : path[runEndIndex].Y : ResolveDefaultSourceDepartureAxis(sourceNode, side); var currentBoundaryCoordinate = side is "left" or "right" ? path[0].Y : path[0].X; var peerCoordinates = CollectSharedLaneSourceBoundaryCoordinates( currentEdges, sourceNode, side, graphMinY, graphMaxY, edge.Id); foreach (var desiredCoordinate in EnumerateSharedLaneBoundaryRepairCoordinates( sourceNode, side, currentBoundaryCoordinate, peerCoordinates)) { var candidatePath = BuildMixedSourceFaceCandidate(path, sourceNode, side, desiredCoordinate, axisValue); if (!IsValidSharedLaneBoundaryRepairCandidate( edge, path, candidatePath, sourceNode, isOutgoing: true, nodes, graphMinY, graphMaxY)) { continue; } var candidateEdges = currentEdges.ToArray(); candidateEdges[repairIndex] = BuildSingleSectionEdge(edge, candidatePath); if (TryAcceptFocusedSharedLanePairRepair( currentEdges, candidateEdges, repairIndex, edge, otherEdge, nodes, graphMinY, graphMaxY, out repairedEdge)) { return true; } } return false; } private static bool TryResolveSharedLaneByDirectNodeHandoffSlotRepair( ElkRoutedEdge[] currentEdges, int repairIndex, ElkRoutedEdge edge, ElkRoutedEdge otherEdge, ElkPositionedNode[] nodes, double minLineClearance, double graphMinY, double graphMaxY, out ElkRoutedEdge repairedEdge) { repairedEdge = edge; var nodesById = nodes.ToDictionary(node => node.Id, StringComparer.Ordinal); if (!TryResolveSharedLaneNodeHandoffContext(edge, otherEdge, nodesById, graphMinY, graphMaxY, out var context)) { return false; } var peerCoordinates = CollectSharedLaneNodeFaceBoundaryCoordinates( currentEdges, context.SharedNode, context.Side, graphMinY, graphMaxY, edge.Id); foreach (var desiredCoordinate in EnumerateSharedLaneBoundaryRepairCoordinates( context.SharedNode, context.Side, context.CurrentBoundaryCoordinate, peerCoordinates)) { var candidatePath = context.IsOutgoing ? BuildMixedSourceFaceCandidate(context.Path, context.SharedNode, context.Side, desiredCoordinate, context.AxisValue) : BuildMixedTargetFaceCandidate(context.Path, context.SharedNode, context.Side, desiredCoordinate, context.AxisValue); if (!IsValidSharedLaneBoundaryRepairCandidate( edge, context.Path, candidatePath, context.SharedNode, context.IsOutgoing, nodes, graphMinY, graphMaxY)) { continue; } var candidateEdges = currentEdges.ToArray(); candidateEdges[repairIndex] = BuildSingleSectionEdge(edge, candidatePath); if (TryAcceptFocusedSharedLanePairRepair( currentEdges, candidateEdges, repairIndex, edge, otherEdge, nodes, graphMinY, graphMaxY, out repairedEdge)) { return true; } } return false; } }