214 lines
8.2 KiB
C#
214 lines
8.2 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
|
|
namespace StellaOps.ElkSharp;
|
|
|
|
internal static partial class ElkEdgeRouterIterative
|
|
{
|
|
internal static ElkRoutedEdge[] BuildFinalRestabilizedCandidate(
|
|
ElkRoutedEdge[] edges,
|
|
ElkPositionedNode[] nodes,
|
|
ElkLayoutDirection direction,
|
|
double minLineClearance,
|
|
IReadOnlyCollection<string>? restrictedEdgeIds = null)
|
|
{
|
|
var focusEdgeIds = restrictedEdgeIds?.Count > 0
|
|
? restrictedEdgeIds
|
|
: edges
|
|
.Select(edge => edge.Id)
|
|
.OrderBy(edgeId => edgeId, StringComparer.Ordinal)
|
|
.ToArray();
|
|
var focusEdgeSet = focusEdgeIds.ToHashSet(StringComparer.Ordinal);
|
|
|
|
var candidate = ChoosePreferredHardRuleLayout(
|
|
edges,
|
|
CloseRemainingTerminalViolations(edges, nodes, direction, minLineClearance, restrictedEdgeIds),
|
|
nodes);
|
|
if (focusEdgeIds.Count > 0)
|
|
{
|
|
candidate = ChoosePreferredHardRuleLayout(
|
|
candidate,
|
|
ApplyAggressiveSharedLaneClosure(
|
|
candidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds),
|
|
nodes);
|
|
}
|
|
|
|
candidate = BuildFinalBoundarySlotCandidate(
|
|
candidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
allowLateRestabilizedClosure: false);
|
|
if (focusEdgeIds.Count > 0)
|
|
{
|
|
candidate = ChoosePreferredHardRuleLayout(
|
|
candidate,
|
|
ApplyAggressiveSharedLaneClosure(
|
|
candidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds),
|
|
nodes);
|
|
}
|
|
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true),
|
|
nodes);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
CloseRemainingTerminalViolations(candidate, nodes, direction, minLineClearance, restrictedEdgeIds),
|
|
nodes);
|
|
if (focusEdgeIds.Count > 0)
|
|
{
|
|
var lateHardRuleCandidate = ApplyAggressiveSharedLaneClosure(
|
|
candidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
lateHardRuleCandidate = CloseRemainingTerminalViolations(
|
|
lateHardRuleCandidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
candidate = ChoosePreferredHardRuleLayout(candidate, lateHardRuleCandidate, nodes);
|
|
|
|
var remainingBadAngles = new Dictionary<string, int>(StringComparer.Ordinal);
|
|
ElkEdgeRoutingScoring.CountBadBoundaryAngles(candidate, nodes, remainingBadAngles, 10);
|
|
var remainingTerminalFocus = ExpandWinningSolutionFocus(
|
|
candidate,
|
|
remainingBadAngles.Keys.Where(focusEdgeSet.Contains))
|
|
.Where(focusEdgeSet.Contains)
|
|
.OrderBy(edgeId => edgeId, StringComparer.Ordinal)
|
|
.ToArray();
|
|
if (remainingTerminalFocus.Length > 0)
|
|
{
|
|
var terminalFocus = (IReadOnlyCollection<string>)remainingTerminalFocus;
|
|
var lateTerminalCandidate = ElkEdgePostProcessor.RepairBoundaryAnglesAndTargetApproaches(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
terminalFocus);
|
|
lateTerminalCandidate = BuildFinalBoundarySlotCandidate(
|
|
lateTerminalCandidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
terminalFocus,
|
|
allowLateRestabilizedClosure: false);
|
|
lateTerminalCandidate = CloseRemainingTerminalViolations(
|
|
lateTerminalCandidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
terminalFocus);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(candidate, lateTerminalCandidate, nodes);
|
|
}
|
|
}
|
|
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true),
|
|
nodes);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
ApplyPostSlotDetourClosure(candidate, nodes, minLineClearance, restrictedEdgeIds),
|
|
nodes);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true),
|
|
nodes);
|
|
candidate = ApplyLateBoundarySlotRestabilization(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true),
|
|
nodes);
|
|
|
|
if (focusEdgeIds.Count > 0)
|
|
{
|
|
var finalSharedLaneCandidate = ElkEdgePostProcessor.SeparateSharedLaneConflicts(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
candidate = ChoosePreferredSharedLanePolishLayout(candidate, finalSharedLaneCandidate, nodes);
|
|
|
|
var finalSourceJoinCandidate = ElkEdgePostProcessor.SpreadSourceDepartureJoins(
|
|
finalSharedLaneCandidate,
|
|
nodes,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
candidate = ChoosePreferredSharedLanePolishLayout(candidate, finalSourceJoinCandidate, nodes);
|
|
|
|
var finalTargetJoinCandidate = ElkEdgePostProcessor.SpreadTargetApproachJoins(
|
|
finalSourceJoinCandidate,
|
|
nodes,
|
|
minLineClearance,
|
|
focusEdgeIds,
|
|
forceOutwardAxisSpacing: true);
|
|
candidate = ChoosePreferredSharedLanePolishLayout(candidate, finalTargetJoinCandidate, nodes);
|
|
|
|
var finalAggressiveCandidate = ApplyAggressiveSharedLaneClosure(
|
|
candidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
candidate = ChoosePreferredSharedLanePolishLayout(candidate, finalAggressiveCandidate, nodes);
|
|
|
|
var forcedFinalSharedLaneCandidate = ElkEdgePostProcessor.SeparateSharedLaneConflicts(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
var baselineSharedLaneViolations = ElkEdgeRoutingScoring.CountSharedLaneViolations(candidate, nodes);
|
|
var forcedSharedLaneViolations = ElkEdgeRoutingScoring.CountSharedLaneViolations(forcedFinalSharedLaneCandidate, nodes);
|
|
if (forcedSharedLaneViolations < baselineSharedLaneViolations)
|
|
{
|
|
var baselineScore = ElkEdgeRoutingScoring.ComputeScore(candidate, nodes);
|
|
var forcedScore = ElkEdgeRoutingScoring.ComputeScore(forcedFinalSharedLaneCandidate, nodes);
|
|
if (forcedScore.NodeCrossings <= baselineScore.NodeCrossings)
|
|
{
|
|
candidate = forcedFinalSharedLaneCandidate;
|
|
}
|
|
}
|
|
}
|
|
|
|
return candidate;
|
|
}
|
|
|
|
}
|