Refactor ElkSharp hybrid routing and document speed path
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
namespace StellaOps.ElkSharp;
|
||||
|
||||
internal static partial class ElkShapeBoundaries
|
||||
{
|
||||
internal static bool TryProjectGatewayBoundarySlot(
|
||||
ElkPositionedNode node,
|
||||
string side,
|
||||
double slotCoordinate,
|
||||
out ElkPoint boundaryPoint)
|
||||
{
|
||||
boundaryPoint = default!;
|
||||
if (!IsGatewayShape(node))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var candidates = new List<ElkPoint>();
|
||||
var polygon = BuildGatewayBoundaryPoints(node);
|
||||
switch (side)
|
||||
{
|
||||
case "left":
|
||||
case "right":
|
||||
{
|
||||
var y = Math.Max(node.Y + 4d, Math.Min(node.Y + node.Height - 4d, slotCoordinate));
|
||||
for (var index = 0; index < polygon.Count; index++)
|
||||
{
|
||||
var start = polygon[index];
|
||||
var end = polygon[(index + 1) % polygon.Count];
|
||||
AddGatewaySlotIntersections(candidates, TryIntersectHorizontalSlot(start, end, y));
|
||||
}
|
||||
|
||||
if (candidates.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
boundaryPoint = side == "left"
|
||||
? candidates.OrderBy(point => point.X).ThenBy(point => point.Y).First()
|
||||
: candidates.OrderByDescending(point => point.X).ThenBy(point => point.Y).First();
|
||||
boundaryPoint = PreferGatewayEdgeInteriorBoundary(
|
||||
node,
|
||||
boundaryPoint,
|
||||
new ElkPoint
|
||||
{
|
||||
X = side == "left" ? node.X - 32d : node.X + node.Width + 32d,
|
||||
Y = y,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
case "top":
|
||||
case "bottom":
|
||||
{
|
||||
var x = Math.Max(node.X + 4d, Math.Min(node.X + node.Width - 4d, slotCoordinate));
|
||||
for (var index = 0; index < polygon.Count; index++)
|
||||
{
|
||||
var start = polygon[index];
|
||||
var end = polygon[(index + 1) % polygon.Count];
|
||||
AddGatewaySlotIntersections(candidates, TryIntersectVerticalSlot(start, end, x));
|
||||
}
|
||||
|
||||
if (candidates.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
boundaryPoint = side == "top"
|
||||
? candidates.OrderBy(point => point.Y).ThenBy(point => point.X).First()
|
||||
: candidates.OrderByDescending(point => point.Y).ThenBy(point => point.X).First();
|
||||
boundaryPoint = PreferGatewayEdgeInteriorBoundary(
|
||||
node,
|
||||
boundaryPoint,
|
||||
new ElkPoint
|
||||
{
|
||||
X = x,
|
||||
Y = side == "top" ? node.Y - 32d : node.Y + node.Height + 32d,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddGatewaySlotIntersections(
|
||||
ICollection<ElkPoint> candidates,
|
||||
IEnumerable<ElkPoint> intersections)
|
||||
{
|
||||
foreach (var candidate in intersections)
|
||||
{
|
||||
if (candidates.Any(existing =>
|
||||
Math.Abs(existing.X - candidate.X) <= CoordinateTolerance
|
||||
&& Math.Abs(existing.Y - candidate.Y) <= CoordinateTolerance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.Add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ElkPoint> TryIntersectHorizontalSlot(
|
||||
ElkPoint start,
|
||||
ElkPoint end,
|
||||
double y)
|
||||
{
|
||||
if (Math.Abs(start.Y - end.Y) <= CoordinateTolerance)
|
||||
{
|
||||
if (Math.Abs(y - start.Y) > CoordinateTolerance)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return new ElkPoint { X = start.X, Y = y };
|
||||
if (Math.Abs(end.X - start.X) > CoordinateTolerance)
|
||||
{
|
||||
yield return new ElkPoint { X = end.X, Y = y };
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
var minY = Math.Min(start.Y, end.Y) - CoordinateTolerance;
|
||||
var maxY = Math.Max(start.Y, end.Y) + CoordinateTolerance;
|
||||
if (y < minY || y > maxY)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var t = (y - start.Y) / (end.Y - start.Y);
|
||||
if (t < -CoordinateTolerance || t > 1d + CoordinateTolerance)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return new ElkPoint
|
||||
{
|
||||
X = start.X + ((end.X - start.X) * t),
|
||||
Y = y,
|
||||
};
|
||||
}
|
||||
|
||||
private static IEnumerable<ElkPoint> TryIntersectVerticalSlot(
|
||||
ElkPoint start,
|
||||
ElkPoint end,
|
||||
double x)
|
||||
{
|
||||
if (Math.Abs(start.X - end.X) <= CoordinateTolerance)
|
||||
{
|
||||
if (Math.Abs(x - start.X) > CoordinateTolerance)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return new ElkPoint { X = x, Y = start.Y };
|
||||
if (Math.Abs(end.Y - start.Y) > CoordinateTolerance)
|
||||
{
|
||||
yield return new ElkPoint { X = x, Y = end.Y };
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
var minX = Math.Min(start.X, end.X) - CoordinateTolerance;
|
||||
var maxX = Math.Max(start.X, end.X) + CoordinateTolerance;
|
||||
if (x < minX || x > maxX)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var t = (x - start.X) / (end.X - start.X);
|
||||
if (t < -CoordinateTolerance || t > 1d + CoordinateTolerance)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return new ElkPoint
|
||||
{
|
||||
X = x,
|
||||
Y = start.Y + ((end.Y - start.Y) * t),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user