namespace StellaOps.ElkSharp; internal static partial class ElkEdgePostProcessorSimplify { internal static ElkRoutedEdge[] SimplifyEdgePaths( ElkRoutedEdge[] edges, ElkPositionedNode[] nodes) { var obstacles = nodes.Select(n => (L: n.X - 4d, T: n.Y - 4d, R: n.X + n.Width + 4d, B: n.Y + n.Height + 4d, Id: n.Id)).ToArray(); var graphMinY = nodes.Length > 0 ? nodes.Min(n => n.Y) : 0d; var graphMaxY = nodes.Length > 0 ? nodes.Max(n => n.Y + n.Height) : 0d; var result = new ElkRoutedEdge[edges.Length]; for (var i = 0; i < edges.Length; i++) { var edge = edges[i]; var excludeIds = new HashSet(StringComparer.Ordinal) { edge.SourceNodeId ?? "", edge.TargetNodeId ?? "" }; var anyChanged = false; var newSections = new List(edge.Sections.Count); var hasCorridor = ElkEdgePostProcessor.HasCorridorBendPoints(edge, graphMinY, graphMaxY); foreach (var section in edge.Sections) { var pts = new List { section.StartPoint }; pts.AddRange(section.BendPoints); pts.Add(section.EndPoint); // Pass 1: Remove collinear points var cleaned = new List { pts[0] }; for (var j = 1; j < pts.Count - 1; j++) { var prev = cleaned[^1]; var curr = pts[j]; var next = pts[j + 1]; var sameX = Math.Abs(prev.X - curr.X) < 1d && Math.Abs(curr.X - next.X) < 1d; var sameY = Math.Abs(prev.Y - curr.Y) < 1d && Math.Abs(curr.Y - next.Y) < 1d; if (sameX || sameY) { anyChanged = true; } else { cleaned.Add(curr); } } cleaned.Add(pts[^1]); // Pass 2: Try L-shape shortcuts for each triple (skip for corridor-routed edges) var changed = !hasCorridor; var simplifyPass = 0; while (changed && simplifyPass++ < 20) { changed = false; for (var j = 0; j + 2 < cleaned.Count; j++) { var a = cleaned[j]; var c = cleaned[j + 2]; var corner1 = new ElkPoint { X = a.X, Y = c.Y }; var corner2 = new ElkPoint { X = c.X, Y = a.Y }; foreach (var corner in new[] { corner1, corner2 }) { if (SegmentClearsObstacles(a, corner, obstacles, excludeIds) && SegmentClearsObstacles(corner, c, obstacles, excludeIds)) { cleaned[j + 1] = corner; changed = true; anyChanged = true; break; } } } if (!changed && TryApplyOrthogonalShortcut(cleaned, obstacles, excludeIds)) { changed = true; anyChanged = true; } } // Remove trailing duplicates (bend point == endpoint) while (cleaned.Count > 2 && Math.Abs(cleaned[^1].X - cleaned[^2].X) < 1d && Math.Abs(cleaned[^1].Y - cleaned[^2].Y) < 1d) { cleaned.RemoveAt(cleaned.Count - 2); } // Remove leading duplicates (start point == first bend) while (cleaned.Count > 2 && Math.Abs(cleaned[0].X - cleaned[1].X) < 1d && Math.Abs(cleaned[0].Y - cleaned[1].Y) < 1d) { cleaned.RemoveAt(1); } newSections.Add(new ElkEdgeSection { StartPoint = cleaned[0], EndPoint = cleaned[^1], BendPoints = cleaned.Skip(1).Take(cleaned.Count - 2).ToArray(), }); } result[i] = anyChanged ? new ElkRoutedEdge { Id = edge.Id, SourceNodeId = edge.SourceNodeId, TargetNodeId = edge.TargetNodeId, Label = edge.Label, Sections = newSections } : edge; } return result; } }