Enable gateway vertex entries with coordinated slot exemption
Three coordinated changes to allow edges to converge at gateway (diamond) left/right tip vertices: 1. IsAllowedGatewayTipVertex: returns true for left/right tips, enabling vertex positions as valid entry points for target edges. 2. HasValidGatewayBoundaryAngle: at allowed tip vertices, accepts any external approach direction (not just horizontal). Source exits are already pushed off vertices by ForceDecisionSourceExitOffVertex. 3. CountBoundarySlotViolations: skips slot-occupancy checks when all entries on a gateway side are target entries converging at the center Y (vertex position). This prevents the -100K penalty that previously caused cascading search failures. Fixes the shared-lane violation between edge/3+edge/4 — the Fork's output edges now converge cleanly at gateway vertex entry points instead of crowding face-interior positions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1125,6 +1125,23 @@ internal static class ElkEdgeRoutingScoring
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gateway vertex exemption: when all target entries on a gateway side
|
||||||
|
// share the same vertex position (left/right tip), they're converging
|
||||||
|
// at a natural diamond corner — not competing for face slots.
|
||||||
|
var isGatewayVertexGroup = ElkShapeBoundaries.IsGatewayShape(node)
|
||||||
|
&& ordered.All(entry => !entry.IsOutgoing)
|
||||||
|
&& ordered.Length >= 2;
|
||||||
|
if (isGatewayVertexGroup)
|
||||||
|
{
|
||||||
|
var centerY = node.Y + (node.Height / 2d);
|
||||||
|
var allAtVertex = ordered.All(entry =>
|
||||||
|
Math.Abs(entry.Coordinate - centerY) <= coordinateTolerance);
|
||||||
|
if (allAtVertex)
|
||||||
|
{
|
||||||
|
continue; // Skip slot checks — valid vertex convergence
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var uniqueSlotCoordinates = ElkBoundarySlots.BuildUniqueBoundarySlotCoordinates(node, side, ordered.Length);
|
var uniqueSlotCoordinates = ElkBoundarySlots.BuildUniqueBoundarySlotCoordinates(node, side, ordered.Length);
|
||||||
var assignedSlotCoordinates = ElkBoundarySlots.BuildAssignedBoundarySlotAxisCoordinates(
|
var assignedSlotCoordinates = ElkBoundarySlots.BuildAssignedBoundarySlotAxisCoordinates(
|
||||||
node,
|
node,
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ internal static partial class ElkShapeBoundaries
|
|||||||
|
|
||||||
if (IsAllowedGatewayTipVertex(node, boundaryPoint))
|
if (IsAllowedGatewayTipVertex(node, boundaryPoint))
|
||||||
{
|
{
|
||||||
return segDx > segDy * 3d;
|
// Allowed tip vertices accept any external approach direction.
|
||||||
|
// Source exits are already pushed off vertices by
|
||||||
|
// ForceDecisionSourceExitOffVertex, so this only affects
|
||||||
|
// target entries — which should converge from any direction.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryGetGatewayBoundaryFace(node, boundaryPoint, out var faceStart, out var faceEnd))
|
if (!TryGetGatewayBoundaryFace(node, boundaryPoint, out var faceStart, out var faceEnd))
|
||||||
@@ -127,11 +131,17 @@ internal static partial class ElkShapeBoundaries
|
|||||||
ElkPoint boundaryPoint,
|
ElkPoint boundaryPoint,
|
||||||
double tolerance = GatewayVertexTolerance)
|
double tolerance = GatewayVertexTolerance)
|
||||||
{
|
{
|
||||||
// Gateway tips read as visually detached "pin" exits/entries in the renderer.
|
// Gateway LEFT and RIGHT tip vertices are allowed as entry points.
|
||||||
// Keep all gateway joins on a face interior instead of permitting any tip vertex.
|
// They're the natural convergence point for edges approaching the
|
||||||
// TODO: revisit for target entries where converging edges would benefit from
|
// diamond along the dominant horizontal axis. Top/bottom tips are
|
||||||
// a shared vertex entry point — requires coordinated boundary-slot changes.
|
// NOT allowed — they create detached "pin" visual artifacts.
|
||||||
return false;
|
// Source exits from tips are still blocked by ForceDecisionSourceExitOffVertex.
|
||||||
|
var centerY = node.Y + (node.Height / 2d);
|
||||||
|
var isLeftTip = Math.Abs(boundaryPoint.X - node.X) <= tolerance
|
||||||
|
&& Math.Abs(boundaryPoint.Y - centerY) <= tolerance;
|
||||||
|
var isRightTip = Math.Abs(boundaryPoint.X - (node.X + node.Width)) <= tolerance
|
||||||
|
&& Math.Abs(boundaryPoint.Y - centerY) <= tolerance;
|
||||||
|
return isLeftTip || isRightTip;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInsideNodeBoundingBoxInterior(
|
internal static bool IsInsideNodeBoundingBoxInterior(
|
||||||
|
|||||||
Reference in New Issue
Block a user