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

194 lines
6.8 KiB
C#

using System.Collections.Concurrent;
using System.Diagnostics;
using System.Globalization;
namespace StellaOps.ElkSharp;
internal static partial class ElkEdgeRouterIterative
{
private static IEnumerable<RoutingStrategy> GenerateStrategies(
ElkRoutedEdge[] edges,
ElkPositionedNode[] nodes,
IterativeRoutingConfig config,
double minLineClearance)
{
var nodesById = nodes.ToDictionary(n => n.Id, StringComparer.Ordinal);
var connectionCount = new Dictionary<string, int>(StringComparer.Ordinal);
foreach (var edge in edges)
{
var srcId = edge.SourceNodeId ?? "";
var tgtId = edge.TargetNodeId ?? "";
connectionCount[srcId] = connectionCount.GetValueOrDefault(srcId) + 1;
connectionCount[tgtId] = connectionCount.GetValueOrDefault(tgtId) + 1;
}
var orderings = new[]
{
OrderByLongestFirst(edges, nodesById),
OrderByShortestFirst(edges, nodesById),
OrderByMostConnectedFirst(edges, connectionCount),
OrderByReverse(edges),
};
// Strategies 1-4: base params, clearance = half avg node dimension
var baseParams = new AStarRoutingParams(18d, 200d, 500d, 2.0d, minLineClearance, 40d, true);
foreach (var order in orderings)
{
yield return new RoutingStrategy
{
EdgeOrder = order,
BaseLineClearance = minLineClearance,
MinLineClearance = minLineClearance,
RoutingParams = baseParams,
};
}
// Strategies 5-8: higher penalties and tighter clearance
var highParams = new AStarRoutingParams(18d, 400d, 600d, 3.0d, minLineClearance, 40d, true);
foreach (var order in orderings)
{
yield return new RoutingStrategy
{
EdgeOrder = order,
BaseLineClearance = minLineClearance,
MinLineClearance = minLineClearance,
RoutingParams = highParams,
};
}
// Strategies 9+: seeded random with clearance
for (var i = 0; i < 8; i++)
{
var seed = HashCode.Combine(edges.Length, nodes.Length, i + 8);
var rng = new Random(seed);
var order = Enumerable.Range(0, edges.Length).ToArray();
Shuffle(order, rng);
var randomParams = new AStarRoutingParams(
18d,
80d + (rng.NextDouble() * 420d),
300d + (rng.NextDouble() * 500d),
1.0d + (rng.NextDouble() * 3.0d),
minLineClearance * (0.5d + rng.NextDouble()),
40d,
true);
yield return new RoutingStrategy
{
EdgeOrder = order,
BaseLineClearance = minLineClearance,
MinLineClearance = minLineClearance,
RoutingParams = randomParams,
};
}
}
private static RoutingRetryState BuildRetryState(EdgeRoutingScore score, int remainingBrokenHighways)
{
return new RoutingRetryState(
RemainingShortHighways: remainingBrokenHighways,
RepeatCollectorCorridorViolations: score.RepeatCollectorCorridorViolations,
RepeatCollectorNodeClearanceViolations: score.RepeatCollectorNodeClearanceViolations,
TargetApproachJoinViolations: score.TargetApproachJoinViolations,
TargetApproachBacktrackingViolations: score.TargetApproachBacktrackingViolations,
ExcessiveDetourViolations: score.ExcessiveDetourViolations,
SharedLaneViolations: score.SharedLaneViolations,
BoundarySlotViolations: score.BoundarySlotViolations,
BelowGraphViolations: score.BelowGraphViolations,
UnderNodeViolations: score.UnderNodeViolations,
LongDiagonalViolations: score.LongDiagonalViolations,
ProximityViolations: score.ProximityViolations,
EntryAngleViolations: score.EntryAngleViolations,
GatewaySourceExitViolations: score.GatewaySourceExitViolations,
LabelProximityViolations: score.LabelProximityViolations,
EdgeCrossings: score.EdgeCrossings);
}
private static string DescribeRetryState(RoutingRetryState retryState)
{
var parts = new List<string>(5);
if (retryState.RemainingShortHighways > 0)
{
parts.Add($"short-highways={retryState.RemainingShortHighways}");
}
if (retryState.RepeatCollectorCorridorViolations > 0)
{
parts.Add($"collector-corridors={retryState.RepeatCollectorCorridorViolations}");
}
if (retryState.RepeatCollectorNodeClearanceViolations > 0)
{
parts.Add($"collector-clearance={retryState.RepeatCollectorNodeClearanceViolations}");
}
if (retryState.TargetApproachJoinViolations > 0)
{
parts.Add($"target-joins={retryState.TargetApproachJoinViolations}");
}
if (retryState.TargetApproachBacktrackingViolations > 0)
{
parts.Add($"approach-backtracking={retryState.TargetApproachBacktrackingViolations}");
}
if (retryState.ExcessiveDetourViolations > 0)
{
parts.Add($"detour={retryState.ExcessiveDetourViolations}");
}
if (retryState.GatewaySourceExitViolations > 0)
{
parts.Add($"gateway-source={retryState.GatewaySourceExitViolations}");
}
if (retryState.SharedLaneViolations > 0)
{
parts.Add($"shared-lanes={retryState.SharedLaneViolations}");
}
if (retryState.BoundarySlotViolations > 0)
{
parts.Add($"boundary-slots={retryState.BoundarySlotViolations}");
}
if (retryState.BelowGraphViolations > 0)
{
parts.Add($"below-graph={retryState.BelowGraphViolations}");
}
if (retryState.UnderNodeViolations > 0)
{
parts.Add($"under-node={retryState.UnderNodeViolations}");
}
if (retryState.LongDiagonalViolations > 0)
{
parts.Add($"long-diagonal={retryState.LongDiagonalViolations}");
}
if (retryState.ProximityViolations > 0)
{
parts.Add($"proximity={retryState.ProximityViolations}");
}
if (retryState.EntryAngleViolations > 0)
{
parts.Add($"entry={retryState.EntryAngleViolations}");
}
if (retryState.LabelProximityViolations > 0)
{
parts.Add($"label={retryState.LabelProximityViolations}");
}
if (retryState.EdgeCrossings > 0)
{
parts.Add($"edge-crossings={retryState.EdgeCrossings}");
}
return parts.Count > 0
? string.Join(", ", parts)
: "none";
}
}