Files
git.stella-ops.org/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.StrategyRepair.RepairPenalizedEdges.cs

206 lines
8.5 KiB
C#

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,
});
}
}