Files
git.stella-ops.org/src/__Libraries/StellaOps.ElkSharp/ElkShapeBoundaries.Exterior.Helpers.cs

132 lines
4.2 KiB
C#

namespace StellaOps.ElkSharp;
internal static partial class ElkShapeBoundaries
{
private static double DistanceToSegment(ElkPoint point, ElkPoint start, ElkPoint end)
{
var deltaX = end.X - start.X;
var deltaY = end.Y - start.Y;
var lengthSquared = (deltaX * deltaX) + (deltaY * deltaY);
if (lengthSquared <= 0.001d)
{
return Math.Sqrt(((point.X - start.X) * (point.X - start.X)) + ((point.Y - start.Y) * (point.Y - start.Y)));
}
var t = (((point.X - start.X) * deltaX) + ((point.Y - start.Y) * deltaY)) / lengthSquared;
t = Math.Max(0d, Math.Min(1d, t));
var projectionX = start.X + (t * deltaX);
var projectionY = start.Y + (t * deltaY);
var distanceX = point.X - projectionX;
var distanceY = point.Y - projectionY;
return Math.Sqrt((distanceX * distanceX) + (distanceY * distanceY));
}
private static bool TryGetGatewayBoundaryFace(
ElkPositionedNode node,
ElkPoint boundaryPoint,
out ElkPoint faceStart,
out ElkPoint faceEnd)
{
faceStart = default!;
faceEnd = default!;
var polygon = BuildGatewayBoundaryPoints(node);
var bestDistance = double.PositiveInfinity;
var bestIndex = -1;
for (var index = 0; index < polygon.Count; index++)
{
var start = polygon[index];
var end = polygon[(index + 1) % polygon.Count];
var distance = DistanceToSegment(boundaryPoint, start, end);
if (distance > 2d || distance >= bestDistance)
{
continue;
}
bestDistance = distance;
bestIndex = index;
}
if (bestIndex < 0)
{
return false;
}
faceStart = polygon[bestIndex];
faceEnd = polygon[(bestIndex + 1) % polygon.Count];
return true;
}
private static bool IsDisallowedGatewayVertex(
ElkPositionedNode node,
ElkPoint boundaryPoint)
{
return IsNearGatewayVertex(node, boundaryPoint, GatewayVertexTolerance)
&& !IsAllowedGatewayTipVertex(node, boundaryPoint, GatewayVertexTolerance);
}
private static (double X, double Y) BuildGatewayFaceNormal(
ElkPositionedNode node,
ElkPoint faceStart,
ElkPoint faceEnd,
ElkPoint boundaryPoint)
{
var deltaX = faceEnd.X - faceStart.X;
var deltaY = faceEnd.Y - faceStart.Y;
var length = Math.Sqrt((deltaX * deltaX) + (deltaY * deltaY));
if (length <= 0.001d)
{
return (0d, -1d);
}
var normalAX = deltaY / length;
var normalAY = -deltaX / length;
var normalBX = -normalAX;
var normalBY = -normalAY;
var centerX = node.X + (node.Width / 2d);
var centerY = node.Y + (node.Height / 2d);
var centerToBoundaryX = boundaryPoint.X - centerX;
var centerToBoundaryY = boundaryPoint.Y - centerY;
var dotA = (normalAX * centerToBoundaryX) + (normalAY * centerToBoundaryY);
var dotB = (normalBX * centerToBoundaryX) + (normalBY * centerToBoundaryY);
return dotA >= dotB
? (normalAX, normalAY)
: (normalBX, normalBY);
}
private static double ComputeRayExitDistanceFromBoundingBox(
ElkPositionedNode node,
ElkPoint origin,
double directionX,
double directionY)
{
const double epsilon = 0.0001d;
var bestDistance = double.PositiveInfinity;
if (directionX > epsilon)
{
bestDistance = Math.Min(bestDistance, (node.X + node.Width - origin.X) / directionX);
}
else if (directionX < -epsilon)
{
bestDistance = Math.Min(bestDistance, (node.X - origin.X) / directionX);
}
if (directionY > epsilon)
{
bestDistance = Math.Min(bestDistance, (node.Y + node.Height - origin.Y) / directionY);
}
else if (directionY < -epsilon)
{
bestDistance = Math.Min(bestDistance, (node.Y - origin.Y) / directionY);
}
if (double.IsInfinity(bestDistance) || bestDistance < 0d)
{
return 0d;
}
return bestDistance;
}
}