From ccf8cb0318b4d0a102a90e41b39a51d761f0e110 Mon Sep 17 00:00:00 2001 From: master <> Date: Wed, 1 Apr 2026 16:22:52 +0300 Subject: [PATCH] Add diagonal elimination to hybrid winner refinement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EliminateDiagonalSegments runs in the hybrid baseline finalization but large diagonals can re-appear during iterative optimization. Added a conditional elimination pass in the winner refinement when LongDiagonalViolations > 0. NodeSpacing=40 retained (default). Tested 42/45/50/60 — each creates different violations because the routing is tuned for 40. Wider spacing needs its own tuning pass. Co-Authored-By: Claude Opus 4.6 (1M context) --- ...rocessingWorkflowRenderingTests.Artifacts.cs | 1 + ...geRouterIterative.WinnerRefinement.Hybrid.cs | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Workflow/__Tests/StellaOps.Workflow.Renderer.Tests/DocumentProcessingWorkflowRenderingTests.Artifacts.cs b/src/Workflow/__Tests/StellaOps.Workflow.Renderer.Tests/DocumentProcessingWorkflowRenderingTests.Artifacts.cs index e722e4ed3..4cbeb51e5 100644 --- a/src/Workflow/__Tests/StellaOps.Workflow.Renderer.Tests/DocumentProcessingWorkflowRenderingTests.Artifacts.cs +++ b/src/Workflow/__Tests/StellaOps.Workflow.Renderer.Tests/DocumentProcessingWorkflowRenderingTests.Artifacts.cs @@ -40,6 +40,7 @@ public partial class DocumentProcessingWorkflowRenderingTests var layout = await engine.LayoutAsync(graph, new WorkflowRenderLayoutRequest { Direction = WorkflowRenderLayoutDirection.LeftToRight, + NodeSpacing = 40, }); var svgRenderer = new WorkflowRenderSvgRenderer(); diff --git a/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs b/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs index e99ccefbb..25a2d57a5 100644 --- a/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs +++ b/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs @@ -258,10 +258,21 @@ internal static partial class ElkEdgeRouterIterative ElkLayoutDiagnostics.LogProgress($"Hybrid winner refinement after final boundary-slot snap: {DescribeSolution(current)}"); } + // Eliminate large diagonal segments that may survive through the + // iterative optimization (the hybrid baseline runs EliminateDiagonalSegments + // but subsequent pipeline passes can re-introduce diagonals). + if (current.Score.LongDiagonalViolations > 0) + { + var eliminated = ElkEdgePostProcessor.EliminateDiagonalSegments(current.Edges, nodes); + var elimScore = ElkEdgeRoutingScoring.ComputeScore(eliminated, nodes); + if (elimScore.LongDiagonalViolations < current.Score.LongDiagonalViolations + && elimScore.NodeCrossings <= current.Score.NodeCrossings) + { + current = current with { Score = elimScore, Edges = eliminated }; + } + } + // Straighten short diagonal stubs at gateway boundary vertices. - // The boundary-slot snap and normalization passes may leave small - // diagonal segments (3-8px) at gateway tips. This cosmetic pass - // adjusts the adjacent bend point to make the approach orthogonal. var straightened = ElkEdgePostProcessor.StraightenGatewayCornerDiagonals(current.Edges, nodes); if (!ReferenceEquals(straightened, current.Edges)) {