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

216 lines
7.0 KiB
C#

using System.Collections.Concurrent;
using System.Diagnostics;
using System.Globalization;
namespace StellaOps.ElkSharp;
internal static partial class ElkEdgeRouterIterative
{
private static string[] ExpandRepeatCollectorRepairSet(
IReadOnlyCollection<string> selectedEdgeIds,
IReadOnlyCollection<ElkRoutedEdge> edges,
IReadOnlyCollection<ElkPositionedNode> nodes)
{
var selected = selectedEdgeIds.ToHashSet(StringComparer.Ordinal);
foreach (var group in ElkRepeatCollectorCorridors.DetectSharedLaneGroups(edges, nodes))
{
if (!group.EdgeIds.Any(selected.Contains))
{
continue;
}
foreach (var edgeId in group.EdgeIds)
{
selected.Add(edgeId);
}
}
return selected
.OrderBy(edgeId => edgeId, StringComparer.Ordinal)
.ToArray();
}
private static string[] ExpandTargetApproachJoinRepairSet(
IReadOnlyCollection<string> selectedEdgeIds,
IReadOnlyCollection<ElkRoutedEdge> edges,
IReadOnlyCollection<ElkPositionedNode> nodes,
double minLineClearance)
{
var selected = selectedEdgeIds.ToHashSet(StringComparer.Ordinal);
var nodesById = nodes.ToDictionary(node => node.Id, StringComparer.Ordinal);
var edgeArray = edges.ToArray();
foreach (var group in edgeArray.GroupBy(edge => edge.TargetNodeId ?? string.Empty, StringComparer.Ordinal))
{
if (string.IsNullOrWhiteSpace(group.Key)
|| !nodesById.TryGetValue(group.Key, out var targetNode))
{
continue;
}
var targetEdges = group.ToArray();
for (var i = 0; i < targetEdges.Length; i++)
{
var leftEdge = targetEdges[i];
var leftPath = ExtractPath(leftEdge);
if (leftPath.Count < 2)
{
continue;
}
var leftSide = ElkEdgeRoutingGeometry.ResolveBoundaryApproachSide(leftPath[^1], leftPath[^2], targetNode);
for (var j = i + 1; j < targetEdges.Length; j++)
{
var rightEdge = targetEdges[j];
var rightPath = ExtractPath(rightEdge);
if (rightPath.Count < 2)
{
continue;
}
var rightSide = ElkEdgeRoutingGeometry.ResolveBoundaryApproachSide(rightPath[^1], rightPath[^2], targetNode);
if (!string.Equals(leftSide, rightSide, StringComparison.Ordinal))
{
continue;
}
if (!HasTargetApproachJoinPair(leftPath, rightPath, minLineClearance))
{
continue;
}
selected.Add(leftEdge.Id);
selected.Add(rightEdge.Id);
}
}
}
return selected
.OrderBy(edgeId => edgeId, StringComparer.Ordinal)
.ToArray();
}
private static string[] ExpandSharedLaneRepairSet(
IReadOnlyCollection<string> selectedEdgeIds,
IReadOnlyCollection<ElkRoutedEdge> edges,
IReadOnlyCollection<ElkPositionedNode> nodes)
{
var selected = selectedEdgeIds.ToHashSet(StringComparer.Ordinal);
foreach (var (leftEdgeId, rightEdgeId) in ElkEdgeRoutingScoring.DetectSharedLaneConflicts(edges, nodes))
{
if (!selected.Contains(leftEdgeId) && !selected.Contains(rightEdgeId))
{
continue;
}
selected.Add(leftEdgeId);
selected.Add(rightEdgeId);
}
return selected
.OrderBy(edgeId => edgeId, StringComparer.Ordinal)
.ToArray();
}
private static string[] ExpandUnderNodeRepairSet(
IReadOnlyCollection<string> selectedEdgeIds,
IReadOnlyCollection<ElkRoutedEdge> edges,
IReadOnlyCollection<ElkPositionedNode> nodes)
{
var selected = selectedEdgeIds.ToHashSet(StringComparer.Ordinal);
foreach (var edge in edges)
{
if (ElkEdgeRoutingScoring.CountUnderNodeViolations([edge], nodes) > 0)
{
selected.Add(edge.Id);
}
}
return selected
.OrderBy(edgeId => edgeId, StringComparer.Ordinal)
.ToArray();
}
private static List<ElkPoint> ExtractPath(ElkRoutedEdge edge)
{
var path = new List<ElkPoint>();
foreach (var section in edge.Sections)
{
if (path.Count == 0)
{
path.Add(section.StartPoint);
}
path.AddRange(section.BendPoints);
path.Add(section.EndPoint);
}
return path;
}
private static bool HasTargetApproachJoinPair(
IReadOnlyList<ElkPoint> leftPath,
IReadOnlyList<ElkPoint> rightPath,
double minLineClearance,
int maxSegmentsFromEnd = 3)
{
var leftSegments = FlattenSegmentsNearEnd(leftPath, maxSegmentsFromEnd);
var rightSegments = FlattenSegmentsNearEnd(rightPath, maxSegmentsFromEnd);
foreach (var leftSegment in leftSegments)
{
foreach (var rightSegment in rightSegments)
{
if (!ElkEdgeRoutingGeometry.AreParallelAndClose(
leftSegment.Start,
leftSegment.End,
rightSegment.Start,
rightSegment.End,
minLineClearance))
{
continue;
}
var overlap = ElkEdgeRoutingGeometry.ComputeSharedSegmentLength(
leftSegment.Start,
leftSegment.End,
rightSegment.Start,
rightSegment.End);
if (overlap > 8d)
{
return true;
}
var leftLength = ElkEdgeRoutingGeometry.ComputeSegmentLength(leftSegment.Start, leftSegment.End);
var rightLength = ElkEdgeRoutingGeometry.ComputeSegmentLength(rightSegment.Start, rightSegment.End);
if (Math.Min(leftLength, rightLength) > 8d)
{
return true;
}
}
}
return false;
}
private static IReadOnlyList<RoutedEdgeSegment> FlattenSegmentsNearEnd(
IReadOnlyList<ElkPoint> path,
int maxSegmentsFromEnd)
{
if (path.Count < 2 || maxSegmentsFromEnd <= 0)
{
return [];
}
var startIndex = Math.Max(0, path.Count - (maxSegmentsFromEnd + 1));
var segments = new List<RoutedEdgeSegment>();
for (var i = startIndex; i < path.Count - 1; i++)
{
segments.Add(new RoutedEdgeSegment(string.Empty, path[i], path[i + 1]));
}
return segments;
}
}