refactor: clean up SpatialQuery duplications and redundancies

Extract ArcToLineClosestDistance helper to eliminate duplicate Phase 3
arc-to-line loops, remove redundant MaxValue guard in curve-to-curve
check, consolidate CollectVertices overloads, and add entity-based
PushDirection overload for API consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 09:33:29 -04:00
parent f090a2e299
commit b18a82df7a

View File

@@ -510,6 +510,17 @@ namespace OpenNest.Geometry
return minDist;
}
/// <summary>
/// Computes the minimum translation distance along a push direction
/// before any vertex/edge of movingEntities contacts any vertex/edge of
/// stationaryEntities. Delegates to the Vector-based overload.
/// </summary>
public static double DirectionalDistance(
List<Entity> movingEntities, List<Entity> stationaryEntities, PushDirection direction)
{
return DirectionalDistance(movingEntities, stationaryEntities, DirectionToOffset(direction, 1.0));
}
/// <summary>
/// Computes the minimum translation distance along an arbitrary unit direction
/// before any vertex/edge of movingEntities contacts any vertex/edge of
@@ -566,43 +577,10 @@ namespace OpenNest.Geometry
// Phases 1-2 sample arc endpoints and cardinal extremes, but the actual
// closest point on a small corner arc to a straight edge may lie between
// those samples. Use ClosestPointTo to find it and fire a ray from there.
for (var i = 0; i < movingEntities.Count; i++)
{
if (movingEntities[i] is Arc mArc)
{
for (var j = 0; j < stationaryEntities.Count; j++)
{
if (stationaryEntities[j] is Line sLine)
{
var linePt = sLine.ClosestPointTo(mArc.Center);
var arcPt = mArc.ClosestPointTo(linePt);
var d = RayEdgeDistance(arcPt.X, arcPt.Y,
sLine.pt1.X, sLine.pt1.Y, sLine.pt2.X, sLine.pt2.Y,
dirX, dirY);
if (d < minDist) { minDist = d; if (d <= 0) return 0; }
}
}
}
}
for (var i = 0; i < stationaryEntities.Count; i++)
{
if (stationaryEntities[i] is Arc sArc2)
{
for (var j = 0; j < movingEntities.Count; j++)
{
if (movingEntities[j] is Line mLine)
{
var linePt = mLine.ClosestPointTo(sArc2.Center);
var arcPt = sArc2.ClosestPointTo(linePt);
var d = RayEdgeDistance(arcPt.X, arcPt.Y,
mLine.pt1.X, mLine.pt1.Y, mLine.pt2.X, mLine.pt2.Y,
oppX, oppY);
if (d < minDist) { minDist = d; if (d <= 0) return 0; }
}
}
}
}
minDist = ArcToLineClosestDistance(movingEntities, stationaryEntities, dirX, dirY, minDist);
if (minDist <= 0) return 0;
minDist = ArcToLineClosestDistance(stationaryEntities, movingEntities, oppX, oppY, minDist);
if (minDist <= 0) return 0;
// Phase 4: Curve-to-curve direct distance.
// The vertex-to-entity approach misses the closest contact between two
@@ -624,7 +602,7 @@ namespace OpenNest.Geometry
var d = RayCircleDistance(mcx, mcy, scx, scy, mr + sr, dirX, dirY);
if (d >= minDist || d == double.MaxValue)
if (d >= minDist)
continue;
// For arcs, verify the contact point falls within both arcs' angular ranges.
@@ -658,6 +636,31 @@ namespace OpenNest.Geometry
return minDist;
}
private static double ArcToLineClosestDistance(
List<Entity> arcEntities, List<Entity> lineEntities,
double dirX, double dirY, double minDist)
{
for (var i = 0; i < arcEntities.Count; i++)
{
if (arcEntities[i] is Arc arc)
{
for (var j = 0; j < lineEntities.Count; j++)
{
if (lineEntities[j] is Line line)
{
var linePt = line.ClosestPointTo(arc.Center);
var arcPt = arc.ClosestPointTo(linePt);
var d = RayEdgeDistance(arcPt.X, arcPt.Y,
line.pt1.X, line.pt1.Y, line.pt2.X, line.pt2.Y,
dirX, dirY);
if (d < minDist) { minDist = d; if (d <= 0) return 0; }
}
}
}
}
return minDist;
}
private static double RayEntityDistance(
double vx, double vy, Entity entity, double dirX, double dirY)
{
@@ -737,13 +740,7 @@ namespace OpenNest.Geometry
private static HashSet<Vector> CollectVertices(List<Line> lines, Vector offset)
{
var vertices = new HashSet<Vector>();
for (var i = 0; i < lines.Count; i++)
{
vertices.Add(lines[i].pt1 + offset);
vertices.Add(lines[i].pt2 + offset);
}
return vertices;
return CollectVertices(ToEdgeArray(lines), offset);
}
private static HashSet<Vector> CollectVertices((Vector start, Vector end)[] edges, Vector offset)