229 lines
10 KiB
C#
229 lines
10 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
|
|
namespace StellaOps.ElkSharp;
|
|
|
|
internal static partial class ElkEdgeRouterIterative
|
|
{
|
|
internal static ElkRoutedEdge[] BuildFinalBoundarySlotCandidate(
|
|
ElkRoutedEdge[] edges,
|
|
ElkPositionedNode[] nodes,
|
|
ElkLayoutDirection direction,
|
|
double minLineClearance,
|
|
IReadOnlyCollection<string>? restrictedEdgeIds = null,
|
|
bool allowLateRestabilizedClosure = true)
|
|
{
|
|
var focusEdgeIds = restrictedEdgeIds?.Count > 0
|
|
? restrictedEdgeIds
|
|
: edges
|
|
.Select(edge => edge.Id)
|
|
.OrderBy(edgeId => edgeId, StringComparer.Ordinal)
|
|
.ToArray();
|
|
var useLeanRestrictedBoundarySlotPass =
|
|
restrictedEdgeIds?.Count > 0
|
|
&& (!allowLateRestabilizedClosure
|
|
|| restrictedEdgeIds.Count <= 2);
|
|
var useUltraLeanRestrictedBoundarySlotPass =
|
|
restrictedEdgeIds?.Count > 0
|
|
&& restrictedEdgeIds.Count <= MaxWinnerPolishBatchedRootEdges + 1;
|
|
ElkLayoutDiagnostics.LogProgress(
|
|
$"Boundary-slot candidate start: focus={focusEdgeIds.Count} allowLateRestabilizedClosure={allowLateRestabilizedClosure}");
|
|
|
|
var best = edges;
|
|
var candidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
edges,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after initial snap");
|
|
var terminalClosureCandidate = useLeanRestrictedBoundarySlotPass
|
|
? ApplyHybridTerminalRuleCleanupRound(
|
|
candidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
restrictedEdgeIds)
|
|
: CloseRemainingTerminalViolations(
|
|
candidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
restrictedEdgeIds);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(candidate, terminalClosureCandidate, nodes);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after terminal closure");
|
|
if (focusEdgeIds.Count > 0)
|
|
{
|
|
candidate = ElkEdgePostProcessor.SeparateMixedNodeFaceLaneConflicts(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
candidate = ElkEdgePostProcessor.SeparateSharedLaneConflicts(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after face/shared-lane separation");
|
|
}
|
|
|
|
candidate = ClampBelowGraphEdges(candidate, nodes, restrictedEdgeIds);
|
|
candidate = ElkEdgePostProcessor.NormalizeBoundaryAngles(candidate, nodes);
|
|
candidate = ElkEdgePostProcessor.NormalizeSourceExitAngles(candidate, nodes);
|
|
candidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after normalization snap");
|
|
if (useUltraLeanRestrictedBoundarySlotPass)
|
|
{
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate complete (ultra-lean restricted path)");
|
|
return ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
}
|
|
candidate = ApplyPostSlotDetourClosure(candidate, nodes, minLineClearance, restrictedEdgeIds);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after detour closure");
|
|
candidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after detour snap");
|
|
if (useLeanRestrictedBoundarySlotPass)
|
|
{
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate complete (lean restricted path)");
|
|
return ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
}
|
|
|
|
if (restrictedEdgeIds?.Count > 0)
|
|
{
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
CloseRemainingTerminalViolations(candidate, nodes, direction, minLineClearance, restrictedEdgeIds),
|
|
nodes);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
candidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after restricted terminal recheck");
|
|
}
|
|
|
|
candidate = ApplyLateBoundarySlotRestabilization(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after late restabilization");
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true),
|
|
nodes);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after late restabilization snap");
|
|
if (focusEdgeIds.Count > 0)
|
|
{
|
|
var lateHardRuleCandidate = ApplyAggressiveSharedLaneClosure(
|
|
candidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds);
|
|
lateHardRuleCandidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
lateHardRuleCandidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true);
|
|
candidate = ChoosePreferredHardRuleLayout(candidate, lateHardRuleCandidate, nodes);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true),
|
|
nodes);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after late hard-rule closure");
|
|
}
|
|
if (focusEdgeIds.Count > 0
|
|
&& (ElkEdgeRoutingScoring.CountBoundarySlotViolations(candidate, nodes) > 0
|
|
|| ElkEdgeRoutingScoring.CountGatewaySourceExitViolations(candidate, nodes) > 0))
|
|
{
|
|
var lateClosureCandidate = ApplyLateFocusedBoundarySlotClosure(
|
|
candidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
focusEdgeIds,
|
|
restrictedEdgeIds);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(candidate, lateClosureCandidate, nodes);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
candidate,
|
|
ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
candidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true),
|
|
nodes);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after focused late closure");
|
|
}
|
|
|
|
if (allowLateRestabilizedClosure
|
|
&& focusEdgeIds.Count > 0
|
|
&& focusEdgeIds.Count <= MaxLateRestabilizedClosureFocusEdges)
|
|
{
|
|
var stagedLateRestabilizedCandidate = ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
edges,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true);
|
|
stagedLateRestabilizedCandidate = BuildFinalRestabilizedCandidate(
|
|
stagedLateRestabilizedCandidate,
|
|
nodes,
|
|
direction,
|
|
minLineClearance,
|
|
restrictedEdgeIds);
|
|
stagedLateRestabilizedCandidate = ChoosePreferredBoundarySlotRepairLayout(
|
|
stagedLateRestabilizedCandidate,
|
|
ElkEdgePostProcessor.SnapBoundarySlotAssignments(
|
|
stagedLateRestabilizedCandidate,
|
|
nodes,
|
|
minLineClearance,
|
|
restrictedEdgeIds,
|
|
enforceAllNodeEndpoints: true),
|
|
nodes);
|
|
candidate = ChoosePreferredBoundarySlotRepairLayout(candidate, stagedLateRestabilizedCandidate, nodes);
|
|
best = ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate after staged late restabilized closure");
|
|
}
|
|
|
|
ElkLayoutDiagnostics.LogProgress("Boundary-slot candidate complete");
|
|
return ChoosePreferredBoundarySlotRepairLayout(best, candidate, nodes);
|
|
}
|
|
|
|
}
|