namespace StellaOps.ElkSharp; internal static partial class ElkEdgePostProcessor { private static List ShiftSingleOrthogonalRun( IReadOnlyList path, int segmentIndex, double desiredCoordinate) { var candidate = path .Select(point => new ElkPoint { X = point.X, Y = point.Y }) .ToList(); if (segmentIndex < 0 || segmentIndex >= candidate.Count - 1) { return candidate; } var start = candidate[segmentIndex]; var end = candidate[segmentIndex + 1]; if (Math.Abs(start.Y - end.Y) <= 0.5d) { var original = start.Y; for (var i = 0; i < candidate.Count; i++) { if (Math.Abs(candidate[i].Y - original) <= 0.5d) { candidate[i] = new ElkPoint { X = candidate[i].X, Y = desiredCoordinate }; } } } else if (Math.Abs(start.X - end.X) <= 0.5d) { var original = start.X; for (var i = 0; i < candidate.Count; i++) { if (Math.Abs(candidate[i].X - original) <= 0.5d) { candidate[i] = new ElkPoint { X = desiredCoordinate, Y = candidate[i].Y }; } } } return NormalizePathPoints(candidate); } private static List ShiftStraightOrthogonalPath( IReadOnlyList path, double desiredCoordinate) { var candidate = path .Select(point => new ElkPoint { X = point.X, Y = point.Y }) .ToList(); if (candidate.Count != 2) { return candidate; } var start = candidate[0]; var end = candidate[1]; if (Math.Abs(start.Y - end.Y) <= 0.5d) { return NormalizePathPoints( [ new ElkPoint { X = start.X, Y = start.Y }, new ElkPoint { X = start.X, Y = desiredCoordinate }, new ElkPoint { X = end.X, Y = desiredCoordinate }, new ElkPoint { X = end.X, Y = end.Y }, ]); } if (Math.Abs(start.X - end.X) <= 0.5d) { return NormalizePathPoints( [ new ElkPoint { X = start.X, Y = start.Y }, new ElkPoint { X = desiredCoordinate, Y = start.Y }, new ElkPoint { X = desiredCoordinate, Y = end.Y }, new ElkPoint { X = end.X, Y = end.Y }, ]); } return candidate; } private static double[] ResolveLaneShiftCoordinates( ElkPoint start, ElkPoint end, ElkPoint otherStart, ElkPoint otherEnd, double minLineClearance) { var offset = minLineClearance + 4d; if (Math.Abs(start.Y - end.Y) <= 0.5d && Math.Abs(otherStart.Y - otherEnd.Y) <= 0.5d) { var lower = otherStart.Y - offset; var upper = otherStart.Y + offset; return start.Y <= otherStart.Y ? [lower, upper] : [upper, lower]; } if (Math.Abs(start.X - end.X) <= 0.5d && Math.Abs(otherStart.X - otherEnd.X) <= 0.5d) { var lower = otherStart.X - offset; var upper = otherStart.X + offset; return start.X <= otherStart.X ? [lower, upper] : [upper, lower]; } return []; } private static bool SegmentLeavesGraphBand( IReadOnlyList path, double graphMinY, double graphMaxY) { return path.Any(point => point.Y < graphMinY - 96d || point.Y > graphMaxY + 96d); } private static bool SegmentsShareLane( ElkPoint leftStart, ElkPoint leftEnd, ElkPoint rightStart, ElkPoint rightEnd, double minLineClearance) { var laneTolerance = Math.Max(4d, Math.Min(12d, minLineClearance * 0.2d)); var minSharedLength = Math.Max(24d, minLineClearance * 0.4d); if (Math.Abs(leftStart.Y - leftEnd.Y) <= 0.5d && Math.Abs(rightStart.Y - rightEnd.Y) <= 0.5d && Math.Abs(leftStart.Y - rightStart.Y) <= laneTolerance) { var leftMinX = Math.Min(leftStart.X, leftEnd.X); var leftMaxX = Math.Max(leftStart.X, leftEnd.X); var rightMinX = Math.Min(rightStart.X, rightEnd.X); var rightMaxX = Math.Max(rightStart.X, rightEnd.X); return Math.Min(leftMaxX, rightMaxX) - Math.Max(leftMinX, rightMinX) >= minSharedLength; } if (Math.Abs(leftStart.X - leftEnd.X) <= 0.5d && Math.Abs(rightStart.X - rightEnd.X) <= 0.5d && Math.Abs(leftStart.X - rightStart.X) <= laneTolerance) { var leftMinY = Math.Min(leftStart.Y, leftEnd.Y); var leftMaxY = Math.Max(leftStart.Y, leftEnd.Y); var rightMinY = Math.Min(rightStart.Y, rightEnd.Y); var rightMaxY = Math.Max(rightStart.Y, rightEnd.Y); return Math.Min(leftMaxY, rightMaxY) - Math.Max(leftMinY, rightMinY) >= minSharedLength; } return false; } }