Refactor ElkSharp hybrid routing and document speed path
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
namespace StellaOps.ElkSharp;
|
||||
|
||||
internal static class ElkEdgeRoutingGeometry
|
||||
internal static partial class ElkEdgeRoutingGeometry
|
||||
{
|
||||
private const double CoordinateTolerance = 0.5d;
|
||||
|
||||
@@ -45,80 +45,6 @@ internal static class ElkEdgeRoutingGeometry
|
||||
return Math.Sqrt((dx * dx) + (dy * dy));
|
||||
}
|
||||
|
||||
internal static bool SegmentsIntersect(ElkPoint a1, ElkPoint a2, ElkPoint b1, ElkPoint b2)
|
||||
{
|
||||
if (ShareEndpoint(a1, a2, b1, b2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AreCollinearAndOverlapping(a1, a2, b1, b2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsHorizontal(a1, a2) && IsVertical(b1, b2))
|
||||
{
|
||||
return IntersectsOrthogonal(a1, a2, b1, b2);
|
||||
}
|
||||
|
||||
if (IsVertical(a1, a2) && IsHorizontal(b1, b2))
|
||||
{
|
||||
return IntersectsOrthogonal(b1, b2, a1, a2);
|
||||
}
|
||||
|
||||
return SegmentsIntersectGeneral(a1, a2, b1, b2);
|
||||
}
|
||||
|
||||
internal static bool AreParallelAndClose(
|
||||
ElkPoint a1,
|
||||
ElkPoint a2,
|
||||
ElkPoint b1,
|
||||
ElkPoint b2,
|
||||
double clearance)
|
||||
{
|
||||
if (IsHorizontal(a1, a2) && IsHorizontal(b1, b2))
|
||||
{
|
||||
return Math.Abs(a1.Y - b1.Y) <= clearance
|
||||
&& OverlapLength(Math.Min(a1.X, a2.X), Math.Max(a1.X, a2.X), Math.Min(b1.X, b2.X), Math.Max(b1.X, b2.X)) > 1d;
|
||||
}
|
||||
|
||||
if (IsVertical(a1, a2) && IsVertical(b1, b2))
|
||||
{
|
||||
return Math.Abs(a1.X - b1.X) <= clearance
|
||||
&& OverlapLength(Math.Min(a1.Y, a2.Y), Math.Max(a1.Y, a2.Y), Math.Min(b1.Y, b2.Y), Math.Max(b1.Y, b2.Y)) > 1d;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static double ComputeSharedSegmentLength(
|
||||
ElkPoint a1,
|
||||
ElkPoint a2,
|
||||
ElkPoint b1,
|
||||
ElkPoint b2)
|
||||
{
|
||||
if (IsHorizontal(a1, a2) && IsHorizontal(b1, b2) && Math.Abs(a1.Y - b1.Y) <= CoordinateTolerance)
|
||||
{
|
||||
return Math.Max(0d, OverlapLength(
|
||||
Math.Min(a1.X, a2.X),
|
||||
Math.Max(a1.X, a2.X),
|
||||
Math.Min(b1.X, b2.X),
|
||||
Math.Max(b1.X, b2.X)));
|
||||
}
|
||||
|
||||
if (IsVertical(a1, a2) && IsVertical(b1, b2) && Math.Abs(a1.X - b1.X) <= CoordinateTolerance)
|
||||
{
|
||||
return Math.Max(0d, OverlapLength(
|
||||
Math.Min(a1.Y, a2.Y),
|
||||
Math.Max(a1.Y, a2.Y),
|
||||
Math.Min(b1.Y, b2.Y),
|
||||
Math.Max(b1.Y, b2.Y)));
|
||||
}
|
||||
|
||||
return 0d;
|
||||
}
|
||||
|
||||
internal static double ComputeLongestSharedSegmentLength(ElkRoutedEdge left, ElkRoutedEdge right)
|
||||
{
|
||||
var leftSegments = FlattenSegments(left);
|
||||
@@ -164,111 +90,6 @@ internal static class ElkEdgeRoutingGeometry
|
||||
return longest;
|
||||
}
|
||||
|
||||
internal static string ResolveBoundarySide(ElkPoint point, ElkPositionedNode node)
|
||||
{
|
||||
var distLeft = Math.Abs(point.X - node.X);
|
||||
var distRight = Math.Abs(point.X - (node.X + node.Width));
|
||||
var distTop = Math.Abs(point.Y - node.Y);
|
||||
var distBottom = Math.Abs(point.Y - (node.Y + node.Height));
|
||||
var min = Math.Min(Math.Min(distLeft, distRight), Math.Min(distTop, distBottom));
|
||||
|
||||
if (Math.Abs(min - distLeft) <= CoordinateTolerance)
|
||||
{
|
||||
return "left";
|
||||
}
|
||||
|
||||
if (Math.Abs(min - distRight) <= CoordinateTolerance)
|
||||
{
|
||||
return "right";
|
||||
}
|
||||
|
||||
if (Math.Abs(min - distTop) <= CoordinateTolerance)
|
||||
{
|
||||
return "top";
|
||||
}
|
||||
|
||||
return "bottom";
|
||||
}
|
||||
|
||||
internal static string ResolveBoundaryApproachSide(
|
||||
ElkPoint boundaryPoint,
|
||||
ElkPoint adjacentPoint,
|
||||
ElkPositionedNode node)
|
||||
{
|
||||
if (!ElkShapeBoundaries.IsGatewayShape(node))
|
||||
{
|
||||
return ResolveBoundarySide(boundaryPoint, node);
|
||||
}
|
||||
|
||||
var deltaX = boundaryPoint.X - adjacentPoint.X;
|
||||
var deltaY = boundaryPoint.Y - adjacentPoint.Y;
|
||||
var absDx = Math.Abs(deltaX);
|
||||
var absDy = Math.Abs(deltaY);
|
||||
if (absDx <= CoordinateTolerance && absDy > CoordinateTolerance)
|
||||
{
|
||||
return deltaY >= 0d ? "top" : "bottom";
|
||||
}
|
||||
|
||||
if (absDy <= CoordinateTolerance && absDx > CoordinateTolerance)
|
||||
{
|
||||
return deltaX >= 0d ? "left" : "right";
|
||||
}
|
||||
|
||||
if (absDx > absDy * 1.25d)
|
||||
{
|
||||
return deltaX >= 0d ? "left" : "right";
|
||||
}
|
||||
|
||||
if (absDy > absDx * 1.25d)
|
||||
{
|
||||
return deltaY >= 0d ? "top" : "bottom";
|
||||
}
|
||||
|
||||
return ResolveBoundarySide(boundaryPoint, node);
|
||||
}
|
||||
|
||||
internal static double ComputeParallelOverlapLength(
|
||||
ElkPoint a1,
|
||||
ElkPoint a2,
|
||||
ElkPoint b1,
|
||||
ElkPoint b2)
|
||||
{
|
||||
if (IsHorizontal(a1, a2) && IsHorizontal(b1, b2))
|
||||
{
|
||||
return OverlapLength(
|
||||
Math.Min(a1.X, a2.X),
|
||||
Math.Max(a1.X, a2.X),
|
||||
Math.Min(b1.X, b2.X),
|
||||
Math.Max(b1.X, b2.X));
|
||||
}
|
||||
|
||||
if (IsVertical(a1, a2) && IsVertical(b1, b2))
|
||||
{
|
||||
return OverlapLength(
|
||||
Math.Min(a1.Y, a2.Y),
|
||||
Math.Max(a1.Y, a2.Y),
|
||||
Math.Min(b1.Y, b2.Y),
|
||||
Math.Max(b1.Y, b2.Y));
|
||||
}
|
||||
|
||||
return 0d;
|
||||
}
|
||||
|
||||
internal static bool AreCollinearAndOverlapping(ElkPoint a1, ElkPoint a2, ElkPoint b1, ElkPoint b2)
|
||||
{
|
||||
if (IsHorizontal(a1, a2) && IsHorizontal(b1, b2) && Math.Abs(a1.Y - b1.Y) <= CoordinateTolerance)
|
||||
{
|
||||
return OverlapLength(Math.Min(a1.X, a2.X), Math.Max(a1.X, a2.X), Math.Min(b1.X, b2.X), Math.Max(b1.X, b2.X)) > 1d;
|
||||
}
|
||||
|
||||
if (IsVertical(a1, a2) && IsVertical(b1, b2) && Math.Abs(a1.X - b1.X) <= CoordinateTolerance)
|
||||
{
|
||||
return OverlapLength(Math.Min(a1.Y, a2.Y), Math.Max(a1.Y, a2.Y), Math.Min(b1.Y, b2.Y), Math.Max(b1.Y, b2.Y)) > 1d;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static ElkPoint ResolveApproachPoint(ElkRoutedEdge edge)
|
||||
{
|
||||
var lastSection = edge.Sections.Last();
|
||||
@@ -285,90 +106,4 @@ internal static class ElkEdgeRoutingGeometry
|
||||
return Math.Abs(left.X - right.X) <= CoordinateTolerance
|
||||
&& Math.Abs(left.Y - right.Y) <= CoordinateTolerance;
|
||||
}
|
||||
|
||||
private static bool IsHorizontal(ElkPoint start, ElkPoint end) => Math.Abs(start.Y - end.Y) <= CoordinateTolerance;
|
||||
|
||||
private static bool IsVertical(ElkPoint start, ElkPoint end) => Math.Abs(start.X - end.X) <= CoordinateTolerance;
|
||||
|
||||
private static IReadOnlyList<RoutedEdgeSegment> FlattenSegmentsNearEnd(
|
||||
IReadOnlyList<ElkPoint> path,
|
||||
int maxSegmentsFromEnd)
|
||||
{
|
||||
if (path.Count < 2 || maxSegmentsFromEnd <= 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var startIndex = Math.Max(0, path.Count - (maxSegmentsFromEnd + 1));
|
||||
var segments = new List<RoutedEdgeSegment>();
|
||||
for (var i = startIndex; i < path.Count - 1; i++)
|
||||
{
|
||||
segments.Add(new RoutedEdgeSegment(string.Empty, path[i], path[i + 1]));
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
private static bool IntersectsOrthogonal(ElkPoint horizontalStart, ElkPoint horizontalEnd, ElkPoint verticalStart, ElkPoint verticalEnd)
|
||||
{
|
||||
var minHorizontalX = Math.Min(horizontalStart.X, horizontalEnd.X);
|
||||
var maxHorizontalX = Math.Max(horizontalStart.X, horizontalEnd.X);
|
||||
var minVerticalY = Math.Min(verticalStart.Y, verticalEnd.Y);
|
||||
var maxVerticalY = Math.Max(verticalStart.Y, verticalEnd.Y);
|
||||
|
||||
return verticalStart.X > minHorizontalX + CoordinateTolerance
|
||||
&& verticalStart.X < maxHorizontalX - CoordinateTolerance
|
||||
&& horizontalStart.Y > minVerticalY + CoordinateTolerance
|
||||
&& horizontalStart.Y < maxVerticalY - CoordinateTolerance;
|
||||
}
|
||||
|
||||
private static bool ShareEndpoint(ElkPoint a1, ElkPoint a2, ElkPoint b1, ElkPoint b2)
|
||||
{
|
||||
return PointsEqual(a1, b1)
|
||||
|| PointsEqual(a1, b2)
|
||||
|| PointsEqual(a2, b1)
|
||||
|| PointsEqual(a2, b2);
|
||||
}
|
||||
|
||||
private static bool SegmentsIntersectGeneral(ElkPoint a1, ElkPoint a2, ElkPoint b1, ElkPoint b2)
|
||||
{
|
||||
var o1 = Orientation(a1, a2, b1);
|
||||
var o2 = Orientation(a1, a2, b2);
|
||||
var o3 = Orientation(b1, b2, a1);
|
||||
var o4 = Orientation(b1, b2, a2);
|
||||
|
||||
if (o1 != o2 && o3 != o4)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return o1 == 0 && OnSegment(a1, b1, a2)
|
||||
|| o2 == 0 && OnSegment(a1, b2, a2)
|
||||
|| o3 == 0 && OnSegment(b1, a1, b2)
|
||||
|| o4 == 0 && OnSegment(b1, a2, b2);
|
||||
}
|
||||
|
||||
private static int Orientation(ElkPoint start, ElkPoint middle, ElkPoint end)
|
||||
{
|
||||
var value = ((middle.Y - start.Y) * (end.X - middle.X)) - ((middle.X - start.X) * (end.Y - middle.Y));
|
||||
if (Math.Abs(value) <= CoordinateTolerance)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value > 0 ? 1 : 2;
|
||||
}
|
||||
|
||||
private static bool OnSegment(ElkPoint start, ElkPoint point, ElkPoint end)
|
||||
{
|
||||
return point.X <= Math.Max(start.X, end.X) + CoordinateTolerance
|
||||
&& point.X >= Math.Min(start.X, end.X) - CoordinateTolerance
|
||||
&& point.Y <= Math.Max(start.Y, end.Y) + CoordinateTolerance
|
||||
&& point.Y >= Math.Min(start.Y, end.Y) - CoordinateTolerance;
|
||||
}
|
||||
|
||||
private static double OverlapLength(double firstMin, double firstMax, double secondMin, double secondMax)
|
||||
{
|
||||
return Math.Min(firstMax, secondMax) - Math.Max(firstMin, secondMin);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user