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

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