Refactor ElkSharp hybrid routing and document speed path
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace StellaOps.ElkSharp;
|
||||
|
||||
internal static partial class ElkEdgeRouterIterative
|
||||
{
|
||||
private static RouteAllEdgesResult RepairPenalizedEdges(
|
||||
ElkRoutedEdge[] existingEdges,
|
||||
ElkPositionedNode[] nodes,
|
||||
double baseObstacleMargin,
|
||||
RoutingStrategy strategy,
|
||||
RepairPlan repairPlan,
|
||||
CancellationToken cancellationToken,
|
||||
int maxParallelRepairBuilds,
|
||||
bool trustIndependentParallelBuilds = false)
|
||||
{
|
||||
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;
|
||||
var repairSet = repairPlan.EdgeIndices.ToHashSet();
|
||||
var routeRepairEdgeIdSet = repairPlan.RouteRepairEdgeIds.ToHashSet(StringComparer.Ordinal);
|
||||
var collectorRepairSet = repairPlan.Reasons.Contains("collector-corridors", StringComparer.Ordinal)
|
||||
? repairSet
|
||||
.Where(edgeIndex => edgeIndex >= 0
|
||||
&& edgeIndex < existingEdges.Length
|
||||
&& ElkEdgePostProcessor.IsRepeatCollectorLabel(existingEdges[edgeIndex].Label))
|
||||
.Where(edgeIndex => !routeRepairEdgeIdSet.Contains(existingEdges[edgeIndex].Id))
|
||||
.ToHashSet()
|
||||
: [];
|
||||
var preferredShortestEdgeIdSet = repairPlan.PreferredShortestEdgeIds.ToHashSet(StringComparer.Ordinal);
|
||||
if (collectorRepairSet.Count > 0)
|
||||
{
|
||||
var collectorEdgeIds = collectorRepairSet
|
||||
.Select(edgeIndex => existingEdges[edgeIndex].Id)
|
||||
.ToArray();
|
||||
routedEdges = ElkRepeatCollectorCorridors.SeparateSharedLanes(routedEdges, nodes, collectorEdgeIds);
|
||||
routedEdgeCount += collectorRepairSet.Count;
|
||||
}
|
||||
|
||||
var aStarRepairSet = repairSet
|
||||
.Where(edgeIndex => !collectorRepairSet.Contains(edgeIndex))
|
||||
.ToHashSet();
|
||||
var spreadEndpoints = SpreadTargetEndpoints(existingEdges, nodesById, graphMinY, graphMaxY, strategy.MinLineClearance);
|
||||
var softObstacles = new List<OrthogonalSoftObstacle>();
|
||||
|
||||
for (var edgeIndex = 0; edgeIndex < existingEdges.Length; edgeIndex++)
|
||||
{
|
||||
if (aStarRepairSet.Contains(edgeIndex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var segment in ElkEdgeRoutingGeometry.FlattenSegments(routedEdges[edgeIndex]))
|
||||
{
|
||||
softObstacles.Add(new OrthogonalSoftObstacle(segment.Start, segment.End));
|
||||
}
|
||||
}
|
||||
|
||||
var orderedRepairIndices = strategy.EdgeOrder
|
||||
.Where(aStarRepairSet.Contains)
|
||||
.Concat(aStarRepairSet.Where(edgeIndex => !strategy.EdgeOrder.Contains(edgeIndex)))
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
var repairBuilderParallelism = (trustIndependentParallelBuilds || CanParallelizeRepairBuilds(orderedRepairIndices, existingEdges))
|
||||
? DetermineRepairBuildParallelism(orderedRepairIndices.Length, maxParallelRepairBuilds)
|
||||
: 1;
|
||||
var builtRepairResults = new ConcurrentDictionary<int, RepairEdgeBuildResult>();
|
||||
var repairBuildLocks = new ConcurrentDictionary<string, object>(StringComparer.Ordinal);
|
||||
if (repairBuilderParallelism > 1 && orderedRepairIndices.Length > 1)
|
||||
{
|
||||
var immutableSoftObstacles = softObstacles.ToArray();
|
||||
var parallelOptions = new ParallelOptions
|
||||
{
|
||||
CancellationToken = cancellationToken,
|
||||
MaxDegreeOfParallelism = repairBuilderParallelism,
|
||||
};
|
||||
|
||||
Parallel.ForEach(
|
||||
orderedRepairIndices,
|
||||
parallelOptions,
|
||||
edgeIndex =>
|
||||
{
|
||||
if (edgeIndex < 0 || edgeIndex >= existingEdges.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var edge = existingEdges[edgeIndex];
|
||||
var lockKeys = trustIndependentParallelBuilds
|
||||
? []
|
||||
: GetRepairBuildLockKeys(edge);
|
||||
ExecuteWithRepairBuildLocks(
|
||||
repairBuildLocks,
|
||||
lockKeys,
|
||||
() =>
|
||||
{
|
||||
builtRepairResults[edgeIndex] = BuildRepairEdgeResult(
|
||||
edgeIndex,
|
||||
existingEdges,
|
||||
nodes,
|
||||
obstacles,
|
||||
spreadEndpoints,
|
||||
nodesById,
|
||||
immutableSoftObstacles,
|
||||
routeRepairEdgeIdSet,
|
||||
preferredShortestEdgeIdSet,
|
||||
repairPlan.Reasons,
|
||||
graphMinY,
|
||||
graphMaxY,
|
||||
strategy,
|
||||
cancellationToken);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var edgeIndex in orderedRepairIndices)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (edgeIndex < 0 || edgeIndex >= existingEdges.Length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var buildResult = builtRepairResults.TryGetValue(edgeIndex, out var parallelBuildResult)
|
||||
? parallelBuildResult
|
||||
: BuildRepairEdgeResult(
|
||||
edgeIndex,
|
||||
existingEdges,
|
||||
nodes,
|
||||
obstacles,
|
||||
spreadEndpoints,
|
||||
nodesById,
|
||||
softObstacles,
|
||||
routeRepairEdgeIdSet,
|
||||
preferredShortestEdgeIdSet,
|
||||
repairPlan.Reasons,
|
||||
graphMinY,
|
||||
graphMaxY,
|
||||
strategy,
|
||||
cancellationToken);
|
||||
if (buildResult.WasSkipped)
|
||||
{
|
||||
skippedEdgeCount++;
|
||||
foreach (var segment in ElkEdgeRoutingGeometry.FlattenSegments(buildResult.Edge))
|
||||
{
|
||||
softObstacles.Add(new OrthogonalSoftObstacle(segment.Start, segment.End));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
routedSectionCount += buildResult.RoutedSections;
|
||||
fallbackSectionCount += buildResult.FallbackSections;
|
||||
routedEdgeCount++;
|
||||
routedEdges[edgeIndex] = buildResult.Edge;
|
||||
|
||||
foreach (var segment in ElkEdgeRoutingGeometry.FlattenSegments(routedEdges[edgeIndex]))
|
||||
{
|
||||
softObstacles.Add(new OrthogonalSoftObstacle(segment.Start, segment.End));
|
||||
}
|
||||
}
|
||||
|
||||
var repeatRouteRepairIds = repairPlan.RouteRepairEdgeIds
|
||||
.Where(edgeId => routedEdges.Any(edge =>
|
||||
string.Equals(edge.Id, edgeId, StringComparison.Ordinal)
|
||||
&& ElkEdgePostProcessor.IsRepeatCollectorLabel(edge.Label)))
|
||||
.ToArray();
|
||||
if (repeatRouteRepairIds.Length > 0)
|
||||
{
|
||||
routedEdges = ElkRepeatCollectorCorridors.SeparateSharedLanes(routedEdges, nodes, repeatRouteRepairIds);
|
||||
}
|
||||
|
||||
return new RouteAllEdgesResult(
|
||||
routedEdges,
|
||||
new ElkIterativeRouteDiagnostics
|
||||
{
|
||||
Mode = "local-repair",
|
||||
TotalEdges = existingEdges.Length,
|
||||
RoutedEdges = routedEdgeCount,
|
||||
SkippedEdges = skippedEdgeCount,
|
||||
RoutedSections = routedSectionCount,
|
||||
FallbackSections = fallbackSectionCount,
|
||||
SoftObstacleSegments = softObstacles.Count,
|
||||
RepairedEdgeIds = repairPlan.EdgeIds,
|
||||
RepairReasons = repairPlan.Reasons,
|
||||
BuilderMode = repairBuilderParallelism > 1 ? "parallel-locked-local-build" : "sequential-local-build",
|
||||
BuilderParallelism = repairBuilderParallelism,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user