Refactor ElkSharp hybrid routing and document speed path
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace StellaOps.ElkSharp;
|
||||
|
||||
internal static partial class ElkEdgeRouterIterative
|
||||
{
|
||||
private static bool TryPromoteFinalBoundarySlotCandidate(
|
||||
CandidateSolution current,
|
||||
ElkRoutedEdge[] candidateEdges,
|
||||
ElkPositionedNode[] nodes,
|
||||
out CandidateSolution promoted)
|
||||
{
|
||||
promoted = current;
|
||||
var candidateScore = ElkEdgeRoutingScoring.ComputeScore(candidateEdges, nodes);
|
||||
var candidateRetryState = BuildRetryState(
|
||||
candidateScore,
|
||||
HighwayProcessingEnabled
|
||||
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(candidateEdges, nodes).Count
|
||||
: 0);
|
||||
if (!IsBetterBoundarySlotRepairCandidate(
|
||||
candidateScore,
|
||||
candidateRetryState,
|
||||
current.Score,
|
||||
current.RetryState))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
promoted = current with
|
||||
{
|
||||
Score = candidateScore,
|
||||
RetryState = candidateRetryState,
|
||||
Edges = candidateEdges,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HasBlockingBoundarySlotPromotionRegression(
|
||||
RoutingRetryState candidate,
|
||||
RoutingRetryState baseline)
|
||||
{
|
||||
return candidate.RemainingShortHighways > baseline.RemainingShortHighways
|
||||
|| candidate.RepeatCollectorCorridorViolations > baseline.RepeatCollectorCorridorViolations
|
||||
|| candidate.RepeatCollectorNodeClearanceViolations > baseline.RepeatCollectorNodeClearanceViolations
|
||||
|| candidate.BelowGraphViolations > baseline.BelowGraphViolations
|
||||
|| candidate.UnderNodeViolations > baseline.UnderNodeViolations;
|
||||
}
|
||||
|
||||
private static bool HasEdgeGeometryChanged(
|
||||
IReadOnlyList<ElkRoutedEdge> baselineEdges,
|
||||
IReadOnlyList<ElkRoutedEdge> candidateEdges,
|
||||
string edgeId)
|
||||
{
|
||||
var baseline = baselineEdges.FirstOrDefault(edge => string.Equals(edge.Id, edgeId, StringComparison.Ordinal));
|
||||
var candidate = candidateEdges.FirstOrDefault(edge => string.Equals(edge.Id, edgeId, StringComparison.Ordinal));
|
||||
if (baseline is null || candidate is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var baselinePath = ExtractEdgePath(baseline);
|
||||
var candidatePath = ExtractEdgePath(candidate);
|
||||
if (baselinePath.Count != candidatePath.Count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < baselinePath.Count; i++)
|
||||
{
|
||||
if (!ElkEdgeRoutingGeometry.PointsEqual(baselinePath[i], candidatePath[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<ElkPoint> ExtractEdgePath(ElkRoutedEdge edge)
|
||||
{
|
||||
var path = new List<ElkPoint>();
|
||||
foreach (var section in edge.Sections)
|
||||
{
|
||||
if (path.Count == 0 || !ElkEdgeRoutingGeometry.PointsEqual(path[^1], section.StartPoint))
|
||||
{
|
||||
path.Add(section.StartPoint);
|
||||
}
|
||||
|
||||
foreach (var bendPoint in section.BendPoints)
|
||||
{
|
||||
if (path.Count == 0 || !ElkEdgeRoutingGeometry.PointsEqual(path[^1], bendPoint))
|
||||
{
|
||||
path.Add(bendPoint);
|
||||
}
|
||||
}
|
||||
|
||||
if (path.Count == 0 || !ElkEdgeRoutingGeometry.PointsEqual(path[^1], section.EndPoint))
|
||||
{
|
||||
path.Add(section.EndPoint);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private static bool TryPromoteFinalHardRuleCandidate(
|
||||
CandidateSolution current,
|
||||
ElkRoutedEdge[] candidateEdges,
|
||||
ElkPositionedNode[] nodes,
|
||||
out CandidateSolution promoted)
|
||||
{
|
||||
promoted = current;
|
||||
var candidateScore = ElkEdgeRoutingScoring.ComputeScore(candidateEdges, nodes);
|
||||
var candidateRetryState = BuildRetryState(
|
||||
candidateScore,
|
||||
HighwayProcessingEnabled
|
||||
? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(candidateEdges, nodes).Count
|
||||
: 0);
|
||||
if (!IsBetterBoundarySlotRepairCandidate(
|
||||
candidateScore,
|
||||
candidateRetryState,
|
||||
current.Score,
|
||||
current.RetryState))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
promoted = current with
|
||||
{
|
||||
Score = candidateScore,
|
||||
RetryState = candidateRetryState,
|
||||
Edges = candidateEdges,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ElkRoutedEdge[] ApplyAggressiveSharedLaneClosure(
|
||||
ElkRoutedEdge[] edges,
|
||||
ElkPositionedNode[] nodes,
|
||||
ElkLayoutDirection direction,
|
||||
double minLineClearance,
|
||||
IReadOnlyCollection<string> focusEdgeIds)
|
||||
{
|
||||
var result = edges;
|
||||
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance, focusEdgeIds);
|
||||
result = ElkEdgePostProcessor.SeparateRepeatCollectorLocalLaneConflicts(result, nodes, minLineClearance, focusEdgeIds);
|
||||
result = ElkRepeatCollectorCorridors.SeparateSharedLanes(result, nodes, focusEdgeIds);
|
||||
result = ElkEdgePostProcessor.SpreadSourceDepartureJoins(result, nodes, minLineClearance, focusEdgeIds);
|
||||
result = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(result, nodes, minLineClearance, focusEdgeIds);
|
||||
result = ElkEdgePostProcessor.SpreadTargetApproachJoins(result, nodes, minLineClearance, focusEdgeIds, forceOutwardAxisSpacing: true);
|
||||
result = ElkEdgePostProcessor.SpreadRectTargetApproachFeederBands(result, nodes, minLineClearance, focusEdgeIds);
|
||||
result = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(result, nodes, minLineClearance, focusEdgeIds);
|
||||
result = ElkEdgePostProcessor.FinalizeGatewayBoundaryGeometry(result, nodes, focusEdgeIds);
|
||||
result = ClampBelowGraphEdges(result, nodes, focusEdgeIds);
|
||||
result = ElkEdgePostProcessor.AvoidNodeCrossings(result, nodes, direction, focusEdgeIds);
|
||||
result = ElkEdgePostProcessor.NormalizeBoundaryAngles(result, nodes);
|
||||
result = ElkEdgePostProcessor.NormalizeSourceExitAngles(result, nodes);
|
||||
result = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(result, nodes, minLineClearance, focusEdgeIds);
|
||||
result = ElkEdgePostProcessor.SeparateSharedLaneConflicts(result, nodes, minLineClearance, focusEdgeIds);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user