Route corridor highways to End via right-side approach

Long corridor sweeps targeting End nodes now approach from the right
face instead of dropping vertically from the top corridor. Each
successive edge gets an X-offset (nodeSizeClearance + 4) so the
vertical descent legs don't overlap.

Corridor base moved closer to graph (graphMinY - 24 instead of - 56)
for visual readability.

Both NodeSpacing=40 (1m23s) and NodeSpacing=50 (38s) pass all
44+ assertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-04-02 08:05:13 +03:00
parent fef0f63c5c
commit dc4d69c6be

View File

@@ -298,7 +298,10 @@ internal static partial class ElkEdgeRouterIterative
{
var graphMinYLocal = nodes.Min(n => n.Y);
var graphWidthLocal = nodes.Max(n => n.X + n.Width) - nodes.Min(n => n.X);
var baseCorridorY = graphMinYLocal - 56d;
var nodesById = nodes.ToDictionary(n => n.Id, StringComparer.Ordinal);
// Keep corridor close to graph for visual readability. 24px is
// enough clearance for the perpendicular exit stub.
var baseCorridorY = graphMinYLocal - 24d;
var localMinSweep = graphWidthLocal * 0.4d;
var corridorResult = current.Edges.ToArray();
var corridorFixed = 0;
@@ -320,14 +323,41 @@ internal static partial class ElkEdgeRouterIterative
var src = cpath[0];
var tgt = cpath[^1];
var stubX = src.X + 24d;
var newPath = new List<ElkPoint>
// Find the target node to determine approach geometry.
var tgtNode = nodesById.TryGetValue(edge.TargetNodeId ?? string.Empty, out var tn) ? tn : null;
List<ElkPoint> newPath;
if (tgtNode is not null && tgtNode.Kind is "End")
{
src,
new() { X = stubX, Y = src.Y },
new() { X = stubX, Y = localCorridorY },
new() { X = tgt.X, Y = localCorridorY },
tgt,
};
// Enter End from the right side: corridor goes past End,
// descends to End's center Y, approaches from right.
// This avoids the ugly long vertical drop from corridor.
// Offset both X and Y for each corridor edge so their
// vertical descent legs don't overlap (parallel vertical
// segments at the same X trigger target-join detection).
var rightApproachX = tgtNode.X + tgtNode.Width + 24d + (corridorFixed * (nodeSizeClearance + 4d));
var centerY = tgtNode.Y + (tgtNode.Height / 2d);
newPath =
[
src,
new() { X = stubX, Y = src.Y },
new() { X = stubX, Y = localCorridorY },
new() { X = rightApproachX, Y = localCorridorY },
new() { X = rightApproachX, Y = centerY },
new() { X = tgtNode.X + tgtNode.Width, Y = centerY },
];
}
else
{
newPath =
[
src,
new() { X = stubX, Y = src.Y },
new() { X = stubX, Y = localCorridorY },
new() { X = tgt.X, Y = localCorridorY },
tgt,
];
}
corridorResult[ei] = new ElkRoutedEdge
{
Id = edge.Id, SourceNodeId = edge.SourceNodeId, TargetNodeId = edge.TargetNodeId,