Files
git.stella-ops.org/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.CollectorNormalization.cs

275 lines
8.8 KiB
C#

namespace StellaOps.ElkSharp;
internal static partial class ElkEdgeRouterIterative
{
private static double ScoreProtectedCollectorGatewaySourceExitCandidate(
IReadOnlyList<ElkPoint> path,
ElkPositionedNode sourceNode,
ElkPoint exitReference)
{
var score = 0d;
if (path.Count < 2)
{
return double.PositiveInfinity;
}
var boundary = path[0];
var adjacent = path[1];
var centerX = sourceNode.X + (sourceNode.Width / 2d);
var centerY = sourceNode.Y + (sourceNode.Height / 2d);
var desiredDx = exitReference.X - centerX;
var desiredDy = exitReference.Y - centerY;
var boundaryDx = boundary.X - centerX;
var boundaryDy = boundary.Y - centerY;
var firstDx = adjacent.X - boundary.X;
var firstDy = adjacent.Y - boundary.Y;
const double tolerance = 0.5d;
var dominantHorizontal = Math.Abs(desiredDx) >= Math.Abs(desiredDy) * 1.15d && Math.Sign(desiredDx) != 0;
var dominantVertical = Math.Abs(desiredDy) >= Math.Abs(desiredDx) * 1.15d && Math.Sign(desiredDy) != 0;
if (dominantHorizontal)
{
if (Math.Sign(firstDx) != Math.Sign(desiredDx) || Math.Abs(firstDx) <= tolerance)
{
score += 100_000d;
}
if (Math.Abs(boundaryDy) > sourceNode.Height * 0.28d)
{
score += 25_000d;
}
score += Math.Abs(boundaryDy) * 6d;
}
else if (dominantVertical)
{
if (Math.Sign(firstDy) != Math.Sign(desiredDy) || Math.Abs(firstDy) <= tolerance)
{
score += 100_000d;
}
if (Math.Abs(boundaryDx) > sourceNode.Width * 0.28d)
{
score += 25_000d;
}
score += Math.Abs(boundaryDx) * 6d;
}
if (ElkShapeBoundaries.IsNearGatewayVertex(sourceNode, boundary, 8d))
{
score += 4_000d;
}
var length = 0d;
for (var i = 1; i < path.Count; i++)
{
length += ElkEdgeRoutingGeometry.ComputeSegmentLength(path[i - 1], path[i]);
}
return score
+ length
+ (Math.Max(0, path.Count - 2) * 6d);
}
private static List<ElkPoint> NormalizeProtectedCollectorTail(
IReadOnlyList<ElkPoint> path,
double graphMinY,
double graphMaxY)
{
if (path.Count < 5)
{
return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList();
}
const double corridorTolerance = 8d;
const double coordinateTolerance = 0.5d;
var firstCorridorIndex = -1;
var lastCorridorIndex = -1;
for (var i = 0; i < path.Count; i++)
{
if (path[i].Y < graphMinY - corridorTolerance || path[i].Y > graphMaxY + corridorTolerance)
{
if (firstCorridorIndex < 0)
{
firstCorridorIndex = i;
}
lastCorridorIndex = i;
}
}
if (firstCorridorIndex < 0
|| lastCorridorIndex <= firstCorridorIndex
|| lastCorridorIndex >= path.Count - 1
|| path[firstCorridorIndex].Y > graphMinY - corridorTolerance)
{
return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList();
}
var desiredDx = path[^1].X - path[0].X;
if (Math.Abs(desiredDx) <= coordinateTolerance)
{
return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList();
}
var preCorridorHorizontalIndex = -1;
for (var i = 1; i < lastCorridorIndex; i++)
{
if (Math.Abs(path[i].Y - path[i + 1].Y) > coordinateTolerance
|| Math.Abs(path[i].X - path[i + 1].X) <= coordinateTolerance)
{
continue;
}
var horizontalDelta = path[i + 1].X - path[i].X;
if (Math.Sign(horizontalDelta) == 0 || Math.Sign(horizontalDelta) == Math.Sign(desiredDx))
{
continue;
}
preCorridorHorizontalIndex = i;
break;
}
if (preCorridorHorizontalIndex >= 0)
{
var rebuiltPrefix = path
.Take(preCorridorHorizontalIndex + 1)
.Select(point => new ElkPoint { X = point.X, Y = point.Y })
.ToList();
var rewrittenCorridorY = path[lastCorridorIndex].Y;
var rewrittenReentryPoint = path[lastCorridorIndex + 1];
if (Math.Abs(rebuiltPrefix[^1].Y - rewrittenCorridorY) > coordinateTolerance)
{
rebuiltPrefix.Add(new ElkPoint
{
X = rebuiltPrefix[^1].X,
Y = rewrittenCorridorY,
});
}
if (Math.Abs(rebuiltPrefix[^1].X - rewrittenReentryPoint.X) > coordinateTolerance)
{
rebuiltPrefix.Add(new ElkPoint
{
X = rewrittenReentryPoint.X,
Y = rewrittenCorridorY,
});
}
for (var i = lastCorridorIndex + 1; i < path.Count; i++)
{
rebuiltPrefix.Add(new ElkPoint { X = path[i].X, Y = path[i].Y });
}
return NormalizeCollectorPoints(rebuiltPrefix);
}
var firstHorizontalIndex = -1;
for (var i = firstCorridorIndex; i < lastCorridorIndex; i++)
{
if (Math.Abs(path[i].Y - path[i + 1].Y) <= coordinateTolerance
&& Math.Abs(path[i].X - path[i + 1].X) > coordinateTolerance)
{
firstHorizontalIndex = i;
break;
}
}
if (firstHorizontalIndex < 0)
{
return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList();
}
var firstHorizontalDelta = path[firstHorizontalIndex + 1].X - path[firstHorizontalIndex].X;
if (Math.Sign(firstHorizontalDelta) == 0 || Math.Sign(firstHorizontalDelta) == Math.Sign(desiredDx))
{
return path.Select(point => new ElkPoint { X = point.X, Y = point.Y }).ToList();
}
var rebuilt = path
.Take(firstCorridorIndex + 1)
.Select(point => new ElkPoint { X = point.X, Y = point.Y })
.ToList();
var targetCorridorY = path[lastCorridorIndex].Y;
var reentryPoint = path[lastCorridorIndex + 1];
if (Math.Abs(rebuilt[^1].Y - targetCorridorY) > coordinateTolerance)
{
rebuilt.Add(new ElkPoint
{
X = rebuilt[^1].X,
Y = targetCorridorY,
});
}
if (Math.Abs(rebuilt[^1].X - reentryPoint.X) > coordinateTolerance)
{
rebuilt.Add(new ElkPoint
{
X = reentryPoint.X,
Y = targetCorridorY,
});
}
for (var i = lastCorridorIndex + 1; i < path.Count; i++)
{
rebuilt.Add(new ElkPoint { X = path[i].X, Y = path[i].Y });
}
return NormalizeCollectorPoints(rebuilt);
}
private static ElkPoint? BuildOrthogonalCollectorCorner(ElkPoint from, ElkPoint to)
{
const double tolerance = 0.5d;
if (Math.Abs(from.X - to.X) <= tolerance || Math.Abs(from.Y - to.Y) <= tolerance)
{
return null;
}
return Math.Abs(to.Y - from.Y) >= Math.Abs(to.X - from.X)
? new ElkPoint { X = from.X, Y = to.Y }
: new ElkPoint { X = to.X, Y = from.Y };
}
private static List<ElkPoint> NormalizeCollectorPoints(IReadOnlyList<ElkPoint> points)
{
const double coordinateTolerance = 0.5d;
var deduped = new List<ElkPoint>();
foreach (var point in points)
{
if (deduped.Count == 0 || !ElkEdgeRoutingGeometry.PointsEqual(deduped[^1], point))
{
deduped.Add(point);
}
}
if (deduped.Count <= 2)
{
return deduped;
}
var simplified = new List<ElkPoint> { deduped[0] };
for (var i = 1; i < deduped.Count - 1; i++)
{
var previous = simplified[^1];
var current = deduped[i];
var next = deduped[i + 1];
var sameX = Math.Abs(previous.X - current.X) <= coordinateTolerance
&& Math.Abs(current.X - next.X) <= coordinateTolerance;
var sameY = Math.Abs(previous.Y - current.Y) <= coordinateTolerance
&& Math.Abs(current.Y - next.Y) <= coordinateTolerance;
if (!sameX && !sameY)
{
simplified.Add(current);
}
}
simplified.Add(deduped[^1]);
return simplified;
}
}