135 lines
4.8 KiB
C#
135 lines
4.8 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
|
|
namespace StellaOps.ElkSharp;
|
|
|
|
internal static partial class ElkEdgeRouterIterative
|
|
{
|
|
private static RouteAllEdgesResult? RouteAllEdges(
|
|
ElkRoutedEdge[] existingEdges,
|
|
ElkPositionedNode[] nodes,
|
|
double baseObstacleMargin,
|
|
RoutingStrategy strategy,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var routedEdges = new ElkRoutedEdge[existingEdges.Length];
|
|
Array.Copy(existingEdges, routedEdges, existingEdges.Length);
|
|
|
|
var obstacleMargin = Math.Max(
|
|
baseObstacleMargin,
|
|
Math.Max(strategy.MinLineClearance + 4d, strategy.RoutingParams.Margin));
|
|
var obstacles = BuildObstacles(nodes, obstacleMargin);
|
|
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 nodesById = nodes.ToDictionary(n => n.Id, StringComparer.Ordinal);
|
|
var routedEdgeCount = 0;
|
|
var skippedEdgeCount = 0;
|
|
var routedSectionCount = 0;
|
|
var fallbackSectionCount = 0;
|
|
|
|
// Spread endpoints: distribute edges arriving at the same target side
|
|
var spreadEndpoints = SpreadTargetEndpoints(existingEdges, nodesById, graphMinY, graphMaxY, strategy.MinLineClearance);
|
|
|
|
var softObstacles = new List<OrthogonalSoftObstacle>();
|
|
|
|
foreach (var edgeIndex in strategy.EdgeOrder)
|
|
{
|
|
if (edgeIndex < 0 || edgeIndex >= existingEdges.Length)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
var edge = existingEdges[edgeIndex];
|
|
|
|
// Skip edges that need special routing (backward, ports, corridors, collectors)
|
|
if (!CanRepairEdgeLocally(edge, nodes, graphMinY, graphMaxY))
|
|
{
|
|
skippedEdgeCount++;
|
|
foreach (var segment in ElkEdgeRoutingGeometry.FlattenSegments(edge))
|
|
{
|
|
softObstacles.Add(new OrthogonalSoftObstacle(segment.Start, segment.End));
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
var newSections = new List<ElkEdgeSection>(edge.Sections.Count);
|
|
|
|
foreach (var section in edge.Sections)
|
|
{
|
|
var endPoint = spreadEndpoints.TryGetValue(edge.Id, out var spread)
|
|
? spread
|
|
: section.EndPoint;
|
|
var (startPoint, adjustedEndPoint) = ResolveRoutingEndpoints(
|
|
section.StartPoint,
|
|
endPoint,
|
|
edge.SourceNodeId,
|
|
edge.TargetNodeId,
|
|
nodesById);
|
|
|
|
var rerouted = ElkEdgeRouterAStar8Dir.Route(
|
|
startPoint,
|
|
adjustedEndPoint,
|
|
obstacles,
|
|
edge.SourceNodeId ?? "",
|
|
edge.TargetNodeId ?? "",
|
|
strategy.RoutingParams,
|
|
softObstacles,
|
|
cancellationToken);
|
|
|
|
if (rerouted is not null && rerouted.Count >= 2)
|
|
{
|
|
routedSectionCount++;
|
|
newSections.Add(new ElkEdgeSection
|
|
{
|
|
StartPoint = rerouted[0],
|
|
EndPoint = rerouted[^1],
|
|
BendPoints = rerouted.Skip(1).Take(rerouted.Count - 2).ToArray(),
|
|
});
|
|
}
|
|
else
|
|
{
|
|
fallbackSectionCount++;
|
|
newSections.Add(section);
|
|
}
|
|
}
|
|
|
|
routedEdgeCount++;
|
|
|
|
routedEdges[edgeIndex] = new ElkRoutedEdge
|
|
{
|
|
Id = edge.Id,
|
|
SourceNodeId = edge.SourceNodeId,
|
|
TargetNodeId = edge.TargetNodeId,
|
|
SourcePortId = edge.SourcePortId,
|
|
TargetPortId = edge.TargetPortId,
|
|
Kind = edge.Kind,
|
|
Label = edge.Label,
|
|
Sections = newSections,
|
|
};
|
|
|
|
foreach (var segment in ElkEdgeRoutingGeometry.FlattenSegments(routedEdges[edgeIndex]))
|
|
{
|
|
softObstacles.Add(new OrthogonalSoftObstacle(segment.Start, segment.End));
|
|
}
|
|
}
|
|
|
|
return new RouteAllEdgesResult(
|
|
routedEdges,
|
|
new ElkIterativeRouteDiagnostics
|
|
{
|
|
Mode = "full-strategy",
|
|
TotalEdges = existingEdges.Length,
|
|
RoutedEdges = routedEdgeCount,
|
|
SkippedEdges = skippedEdgeCount,
|
|
RoutedSections = routedSectionCount,
|
|
FallbackSections = fallbackSectionCount,
|
|
SoftObstacleSegments = softObstacles.Count,
|
|
});
|
|
}
|
|
|
|
}
|