diff --git a/src/Workflow/__Libraries/StellaOps.Workflow.Renderer.Svg/WorkflowRenderSvgRenderer.cs b/src/Workflow/__Libraries/StellaOps.Workflow.Renderer.Svg/WorkflowRenderSvgRenderer.cs
index b4d518bae..b33340197 100644
--- a/src/Workflow/__Libraries/StellaOps.Workflow.Renderer.Svg/WorkflowRenderSvgRenderer.cs
+++ b/src/Workflow/__Libraries/StellaOps.Workflow.Renderer.Svg/WorkflowRenderSvgRenderer.cs
@@ -2052,7 +2052,7 @@ public sealed class WorkflowRenderSvgRenderer
var dxIn = Math.Abs(curr.X - prev.X);
var dyIn = Math.Abs(curr.Y - prev.Y);
var segLen = dxIn + dyIn;
- if (segLen < 24d && i < mutablePoints.Count - 1)
+ if (segLen < 30d && i < mutablePoints.Count - 1)
{
var next = mutablePoints[i + 1];
if (dxIn < dyIn)
diff --git a/src/__Libraries/StellaOps.ElkSharp/ElkEdgePostProcessor.GatewayBoundary.cs b/src/__Libraries/StellaOps.ElkSharp/ElkEdgePostProcessor.GatewayBoundary.cs
index 1279d89ad..a7b6e9ab0 100644
--- a/src/__Libraries/StellaOps.ElkSharp/ElkEdgePostProcessor.GatewayBoundary.cs
+++ b/src/__Libraries/StellaOps.ElkSharp/ElkEdgePostProcessor.GatewayBoundary.cs
@@ -629,6 +629,46 @@ internal static partial class ElkEdgePostProcessor
return true;
}
+ ///
+ /// Collapses the FIRST short dogleg found across all edges.
+ /// Returns the modified array or the original if none found.
+ /// Call repeatedly to collapse one dogleg at a time.
+ ///
+ internal static ElkRoutedEdge[] CollapseShortDoglegs(ElkRoutedEdge[] edges, ElkPositionedNode[] nodes)
+ {
+ if (edges.Length == 0) return edges;
+ for (var i = 0; i < edges.Length; i++)
+ {
+ var edge = edges[i];
+ var path = ExtractFullPath(edge);
+ if (path.Count < 4) continue;
+ var newPath = new List(path);
+ for (var j = 1; j < newPath.Count - 2; j++)
+ {
+ var prev = newPath[j - 1];
+ var curr = newPath[j];
+ var next = newPath[j + 1];
+ var seg1 = Math.Abs(curr.X - prev.X) + Math.Abs(curr.Y - prev.Y);
+ var seg2 = Math.Abs(next.X - curr.X) + Math.Abs(next.Y - curr.Y);
+ if (seg1 >= 30d || seg2 >= 30d || seg1 < 1d || seg2 < 1d) continue;
+ var s1V = Math.Abs(curr.X - prev.X) < 2d;
+ var s2H = Math.Abs(next.Y - curr.Y) < 2d;
+ var s1H = Math.Abs(curr.Y - prev.Y) < 2d;
+ var s2V = Math.Abs(next.X - curr.X) < 2d;
+ if ((s1V && s2H) || (s1H && s2V))
+ {
+ newPath[j] = new ElkPoint { X = next.X, Y = prev.Y };
+ newPath.RemoveAt(j + 1);
+ var cleaned = RemoveCollinearPoints(newPath);
+ var result = edges.ToArray();
+ result[i] = BuildSingleSectionEdge(edge, cleaned);
+ return result; // return after first collapse
+ }
+ }
+ }
+ return edges;
+ }
+
private static List RemoveCollinearPoints(List path)
{
if (path.Count < 3)
diff --git a/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs b/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs
index a04e17f16..db600fb73 100644
--- a/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs
+++ b/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.WinnerRefinement.Hybrid.cs
@@ -272,6 +272,32 @@ internal static partial class ElkEdgeRouterIterative
}
}
+ // Collapse short doglegs (two consecutive <30px segments forming an
+ // L-shape) into single bends. These create visible zigzag steps.
+ // Collapse short doglegs PER-EDGE: some collapses create entry-angle
+ // violations. Process one edge at a time and only keep safe ones.
+ for (var dei = 0; dei < current.Edges.Length; dei++)
+ {
+ var singleEdge = ElkEdgePostProcessor.CollapseShortDoglegs(
+ current.Edges, nodes);
+ if (ReferenceEquals(singleEdge, current.Edges))
+ {
+ break; // no more doglegs to collapse
+ }
+
+ var singleScore = ElkEdgeRoutingScoring.ComputeScore(singleEdge, nodes);
+ if (singleScore.EntryAngleViolations <= current.Score.EntryAngleViolations
+ && singleScore.NodeCrossings <= current.Score.NodeCrossings
+ && singleScore.SharedLaneViolations <= current.Score.SharedLaneViolations)
+ {
+ current = current with { Score = singleScore, Edges = singleEdge };
+ }
+ else
+ {
+ break; // can't collapse more without violations
+ }
+ }
+
// Straighten short diagonal stubs at gateway boundary vertices.
var straightened = ElkEdgePostProcessor.StraightenGatewayCornerDiagonals(current.Edges, nodes);
if (!ReferenceEquals(straightened, current.Edges))