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

166 lines
6.3 KiB
C#

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