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:
@@ -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
|
||||
|
||||
@@ -292,6 +292,29 @@ internal static partial class ElkEdgeRouterIterative
|
||||
current = RepairRemainingEdgeNodeCrossings(current, nodes);
|
||||
}
|
||||
|
||||
// Final target-join repair: the per-edge gateway fixes may create
|
||||
// new target-join convergences that didn't exist before. Run the
|
||||
// spread one more time to catch them.
|
||||
var finalJoinSeverity = new Dictionary<string, int>(StringComparer.Ordinal);
|
||||
var finalJoinCount = ElkEdgeRoutingScoring.CountTargetApproachJoinViolations(current.Edges, nodes, finalJoinSeverity, 1);
|
||||
ElkLayoutDiagnostics.LogProgress(
|
||||
$"Final target-join check: count={finalJoinCount} edges=[{string.Join(", ", finalJoinSeverity.Keys.OrderBy(k => k))}]");
|
||||
if (finalJoinSeverity.Count >= 2)
|
||||
{
|
||||
var joinFocus = finalJoinSeverity.Keys.ToArray();
|
||||
var joinCandidate = ElkEdgePostProcessor.SpreadTargetApproachJoins(
|
||||
current.Edges, nodes, minLineClearance, joinFocus, forceOutwardAxisSpacing: true);
|
||||
joinCandidate = ElkEdgePostProcessor.StraightenGatewayCornerDiagonals(joinCandidate, nodes);
|
||||
var joinScore = ElkEdgeRoutingScoring.ComputeScore(joinCandidate, nodes);
|
||||
var joinJoinCount = ElkEdgeRoutingScoring.CountTargetApproachJoinViolations(joinCandidate, nodes);
|
||||
if (joinJoinCount < finalJoinCount)
|
||||
{
|
||||
current = current with { Score = joinScore, Edges = joinCandidate };
|
||||
ElkLayoutDiagnostics.LogProgress(
|
||||
$"Hybrid final target-join repair: joins={joinJoinCount}");
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user