Refactor ElkSharp hybrid routing and document speed path
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user