Fix target-join at NodeSpacing=50 via final post-pipeline spread

Added final target-join detection and repair after per-edge gateway
fixes. The per-edge redirect can create new target-join convergences
that don't exist during the main optimization loop. The post-pipeline
spread fixes them without normalization (which would undo the spread).

NodeSpacing=50 progress: target-join FIXED, shared-lane FIXED.
Remaining at NodeSpacing=50: ExcessiveDetourViolations=1 (from
target-join spread creating longer path).

NodeSpacing=40: all tests pass (artifact 1/1, StraightExit 2/2,
HybridDeterministicMode 3/3).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
master
2026-04-01 17:37:37 +03:00
parent fafcadbc9a
commit e01549c2d6
2 changed files with 40 additions and 0 deletions

View File

@@ -137,6 +137,23 @@ public partial class DocumentProcessingWorkflowRenderingTests
.SelectMany(edge => GetBoundaryAngleViolations(edge, layout.Nodes))
.ToArray();
TestContext.Out.WriteLine($"Boundary angle offenders: {(boundaryAngleOffenders.Length == 0 ? "<none>" : string.Join(", ", boundaryAngleOffenders))}");
// Diagnostic: compare scoring vs test target-join detection
var scoringJoinSeverity = new Dictionary<string, int>(StringComparer.Ordinal);
var elkNodesForDiag = layout.Nodes.Select(ToElkNode).ToArray();
var elkEdgesForDiag = layout.Edges.Select(edge => new ElkRoutedEdge
{
Id = edge.Id, SourceNodeId = edge.SourceNodeId, TargetNodeId = edge.TargetNodeId,
Kind = edge.Kind, Label = edge.Label,
Sections = edge.Sections.Select(s => new ElkEdgeSection
{
StartPoint = new ElkPoint { X = s.StartPoint.X, Y = s.StartPoint.Y },
EndPoint = new ElkPoint { X = s.EndPoint.X, Y = s.EndPoint.Y },
BendPoints = s.BendPoints.Select(p => new ElkPoint { X = p.X, Y = p.Y }).ToArray(),
}).ToArray(),
}).ToArray();
var scoringJoinCount = ElkEdgeRoutingScoring.CountTargetApproachJoinViolations(
elkEdgesForDiag, elkNodesForDiag, scoringJoinSeverity, 1);
TestContext.Out.WriteLine($"Scoring target-join count: {scoringJoinCount}, edges: [{string.Join(", ", scoringJoinSeverity.Keys.OrderBy(k => k))}]");
var targetJoinOffenders = GetTargetApproachJoinOffenders(layout.Edges, layout.Nodes).ToArray();
TestContext.Out.WriteLine($"Target join offenders: {(targetJoinOffenders.Length == 0 ? "<none>" : string.Join(", ", targetJoinOffenders))}");
var elkNodes = layout.Nodes.Select(node => new ElkPositionedNode