Fix corridor reroute: push-first for under-node, corridor for visual

Restored push-first approach for long sweeps WITH under-node violations
(NodeSpacing=40 needs small Y adjustments, not corridor routing).
Corridor-only for visual sweeps WITHOUT under-node violations (handled
by unconditional corridor in winner refinement).

Corridor offset uses node-size clearance + 4px (not spacing-scaled) to
avoid repeat-collector conflicts. Gated on no new repeat-collector or
node-crossing regressions.

Both NodeSpacing=40 and NodeSpacing=50 pass all 44+ assertions.
NodeSpacing=50 set as test default (visually cleaner, 56s vs 2m43s).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-04-02 07:53:13 +03:00
parent f4df1c1274
commit fef0f63c5c
3 changed files with 58 additions and 22 deletions

View File

@@ -312,7 +312,11 @@ internal static partial class ElkEdgeRouterIterative
var segLen = Math.Abs(cpath[si + 1].X - cpath[si].X);
var laneY = cpath[si].Y;
if (segLen < localMinSweep || laneY <= graphMinYLocal - 10d) continue;
var localCorridorY = baseCorridorY - (corridorFixed * 24d);
// Offset must exceed the target-join detection threshold
// (node-size clearance, not spacing-scaled) so parallel
// corridor segments aren't flagged as joins.
var nodeSizeClearance = ElkEdgeRoutingScoring.ResolveNodeSizeClearance(nodes);
var localCorridorY = baseCorridorY - (corridorFixed * (nodeSizeClearance + 4d));
var src = cpath[0];
var tgt = cpath[^1];
var stubX = src.X + 24d;
@@ -342,9 +346,14 @@ internal static partial class ElkEdgeRouterIterative
if (corridorFixed > 0)
{
var corridorScore = ElkEdgeRoutingScoring.ComputeScore(corridorResult, nodes);
current = current with { Score = corridorScore, Edges = corridorResult };
ElkLayoutDiagnostics.LogProgress(
$"Unconditional corridor reroute: {corridorFixed} edges to corridor");
// Accept only if no new repeat-collector or node-crossing regressions.
if (corridorScore.RepeatCollectorCorridorViolations <= current.Score.RepeatCollectorCorridorViolations
&& corridorScore.NodeCrossings <= current.Score.NodeCrossings)
{
current = current with { Score = corridorScore, Edges = corridorResult };
ElkLayoutDiagnostics.LogProgress(
$"Unconditional corridor reroute: {corridorFixed} edges to corridor");
}
}
}