Merge branch 'fix/bestfit-overlap-test'
Fixes BestFit overlap check to agree with the geometry BuildParts actually places, resolving BestFitOverlapTests.KeptPairs_NoOverlap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -58,19 +58,6 @@ namespace OpenNest.Engine.BestFit
|
||||
part2.Location = candidate.Part2Offset;
|
||||
part2.UpdateBounds();
|
||||
|
||||
// Overlap check — perimeter vs perimeter. Uses Collision.HasOverlap (full polygon
|
||||
// clip) rather than Shape.Intersects (edge-crossing only), which misses containment-
|
||||
// style overlaps where one perimeter's boundary never crosses the other's.
|
||||
var shape1 = GetPerimeterShape(part1);
|
||||
var shape2 = GetPerimeterShape(part2);
|
||||
var overlaps =
|
||||
shape1 != null
|
||||
&& shape2 != null
|
||||
&& Collision.HasOverlap(
|
||||
shape1.ToPolygonWithTolerance(OverlapChordTolerance),
|
||||
shape2.ToPolygonWithTolerance(OverlapChordTolerance)
|
||||
);
|
||||
|
||||
// Convex hull vertices from perimeter polygons only
|
||||
var allPoints = GetPartVertices(part1);
|
||||
allPoints.AddRange(GetPartVertices(part2));
|
||||
@@ -106,7 +93,9 @@ namespace OpenNest.Engine.BestFit
|
||||
|
||||
var trueArea = candidate.Drawing.Area * 2;
|
||||
|
||||
// Normalize to landscape (width >= height) for consistent display.
|
||||
// Normalize to landscape (width >= height) for consistent display. Do this before
|
||||
// the overlap check so bestRotation already matches the final OptimalRotation that
|
||||
// BuildParts will apply.
|
||||
if (bestHeight > bestWidth)
|
||||
{
|
||||
var tmp = bestWidth;
|
||||
@@ -115,6 +104,33 @@ namespace OpenNest.Engine.BestFit
|
||||
bestRotation += Angle.HalfPI;
|
||||
}
|
||||
|
||||
// Overlap check — perimeter vs perimeter, in the same final orientation BuildParts
|
||||
// uses downstream. Uses Collision.HasOverlap (full polygon clip) rather than
|
||||
// Shape.Intersects (edge-crossing only), which misses containment-style overlaps
|
||||
// where one perimeter's boundary never crosses the other's. Checking pre-rotation
|
||||
// geometry here (rather than rotating part1/part2 first, matching BuildParts) would
|
||||
// tessellate arcs at a different orientation than the geometry actually gets placed
|
||||
// with, letting tangent-corner slivers slip through in one frame but not the other.
|
||||
if (!bestRotation.IsEqualTo(0))
|
||||
{
|
||||
var pairBounds = (
|
||||
(IEnumerable<IBoundable>)new IBoundable[] { part1, part2 }
|
||||
).GetBoundingBox();
|
||||
var center = pairBounds.Center;
|
||||
part1.Rotate(-bestRotation, center);
|
||||
part2.Rotate(-bestRotation, center);
|
||||
}
|
||||
|
||||
var shape1 = GetPerimeterShape(part1);
|
||||
var shape2 = GetPerimeterShape(part2);
|
||||
var overlaps =
|
||||
shape1 != null
|
||||
&& shape2 != null
|
||||
&& Collision.HasOverlap(
|
||||
shape1.ToPolygonWithTolerance(OverlapChordTolerance),
|
||||
shape2.ToPolygonWithTolerance(OverlapChordTolerance)
|
||||
);
|
||||
|
||||
return new BestFitResult
|
||||
{
|
||||
Candidate = candidate,
|
||||
|
||||
Reference in New Issue
Block a user