Files
OpenNest/OpenNest.Tests/Geometry/PolygonHelperTests.cs
AJ Isaacs 3e340e67e0 refactor: organize test project into subdirectories by feature area
Move 43 root-level test files into feature-specific subdirectories
mirroring the main codebase structure: Geometry, Fill, BestFit, CutOffs,
CuttingStrategy, Engine, IO. Update namespaces to match folder paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 20:46:43 -04:00

130 lines
5.3 KiB
C#

using OpenNest.CNC;
using OpenNest.Engine.BestFit;
using OpenNest.Geometry;
using OpenNest.Math;
namespace OpenNest.Tests.Geometry;
public class PolygonHelperTests
{
[Fact]
public void ExtractPerimeterPolygon_ReturnsPolygon_ForValidDrawing()
{
var drawing = TestHelpers.MakeSquareDrawing();
var result = PolygonHelper.ExtractPerimeterPolygon(drawing, 0);
Assert.NotNull(result.Polygon);
Assert.True(result.Polygon.Vertices.Count >= 4);
}
[Fact]
public void ExtractPerimeterPolygon_InflatesPolygon_WhenSpacingNonZero()
{
var drawing = TestHelpers.MakeSquareDrawing(10);
var noSpacing = PolygonHelper.ExtractPerimeterPolygon(drawing, 0);
var withSpacing = PolygonHelper.ExtractPerimeterPolygon(drawing, 1);
noSpacing.Polygon.UpdateBounds();
withSpacing.Polygon.UpdateBounds();
// The offset polygon should differ in size from the non-offset polygon.
// OffsetSide.Left offsets outward or inward depending on winding,
// but either way the result must be a different size.
Assert.True(
System.Math.Abs(withSpacing.Polygon.BoundingBox.Width - noSpacing.Polygon.BoundingBox.Width) > 0.5,
$"Expected polygon width to differ by >0.5 with 1mm spacing. " +
$"No-spacing width: {noSpacing.Polygon.BoundingBox.Width:F3}, " +
$"With-spacing width: {withSpacing.Polygon.BoundingBox.Width:F3}");
}
[Fact]
public void ExtractPerimeterPolygon_InflatedPolygonIsLarger_ForCWWinding()
{
// CW winding (standard CNC convention): (0,0)→(0,10)→(10,10)→(10,0)→(0,0)
var drawing = TestHelpers.MakeSquareDrawing(10);
var noSpacing = PolygonHelper.ExtractPerimeterPolygon(drawing, 0);
var withSpacing = PolygonHelper.ExtractPerimeterPolygon(drawing, 1);
noSpacing.Polygon.UpdateBounds();
withSpacing.Polygon.UpdateBounds();
Assert.True(withSpacing.Polygon.BoundingBox.Width > noSpacing.Polygon.BoundingBox.Width,
$"Inflated width {withSpacing.Polygon.BoundingBox.Width:F3} should be > original {noSpacing.Polygon.BoundingBox.Width:F3}");
Assert.True(withSpacing.Polygon.BoundingBox.Length > noSpacing.Polygon.BoundingBox.Length,
$"Inflated length {withSpacing.Polygon.BoundingBox.Length:F3} should be > original {noSpacing.Polygon.BoundingBox.Length:F3}");
}
[Fact]
public void ExtractPerimeterPolygon_InflatedPolygonIsLarger_ForCCWWinding()
{
// CCW winding: (0,0)→(10,0)→(10,10)→(0,10)→(0,0)
var pgm = new CNC.Program();
pgm.Codes.Add(new CNC.RapidMove(new Vector(0, 0)));
pgm.Codes.Add(new CNC.LinearMove(new Vector(10, 0)));
pgm.Codes.Add(new CNC.LinearMove(new Vector(10, 10)));
pgm.Codes.Add(new CNC.LinearMove(new Vector(0, 10)));
pgm.Codes.Add(new CNC.LinearMove(new Vector(0, 0)));
var drawing = new Drawing("ccw-square", pgm);
var noSpacing = PolygonHelper.ExtractPerimeterPolygon(drawing, 0);
var withSpacing = PolygonHelper.ExtractPerimeterPolygon(drawing, 1);
noSpacing.Polygon.UpdateBounds();
withSpacing.Polygon.UpdateBounds();
Assert.True(withSpacing.Polygon.BoundingBox.Width > noSpacing.Polygon.BoundingBox.Width,
$"Inflated width {withSpacing.Polygon.BoundingBox.Width:F3} should be > original {noSpacing.Polygon.BoundingBox.Width:F3}");
Assert.True(withSpacing.Polygon.BoundingBox.Length > noSpacing.Polygon.BoundingBox.Length,
$"Inflated length {withSpacing.Polygon.BoundingBox.Length:F3} should be > original {noSpacing.Polygon.BoundingBox.Length:F3}");
}
[Fact]
public void ExtractPerimeterPolygon_ReturnsNull_ForEmptyDrawing()
{
var pgm = new Program();
var drawing = new Drawing("empty", pgm);
var result = PolygonHelper.ExtractPerimeterPolygon(drawing, 0);
Assert.Null(result.Polygon);
}
[Fact]
public void ExtractPerimeterPolygon_CorrectionVector_ReflectsOriginDifference()
{
var drawing = TestHelpers.MakeSquareDrawing();
var result = PolygonHelper.ExtractPerimeterPolygon(drawing, 0);
Assert.NotNull(result.Polygon);
Assert.True(System.Math.Abs(result.Correction.X) < 1);
Assert.True(System.Math.Abs(result.Correction.Y) < 1);
}
[Fact]
public void RotatePolygon_AtZero_ReturnsSamePolygon()
{
var polygon = new Polygon();
polygon.Vertices.Add(new Vector(0, 0));
polygon.Vertices.Add(new Vector(10, 0));
polygon.Vertices.Add(new Vector(10, 10));
polygon.Vertices.Add(new Vector(0, 10));
polygon.UpdateBounds();
var rotated = PolygonHelper.RotatePolygon(polygon, 0);
Assert.Same(polygon, rotated);
}
[Fact]
public void RotatePolygon_At90Degrees_SwapsDimensions()
{
var polygon = new Polygon();
polygon.Vertices.Add(new Vector(0, 0));
polygon.Vertices.Add(new Vector(20, 0));
polygon.Vertices.Add(new Vector(20, 10));
polygon.Vertices.Add(new Vector(0, 10));
polygon.UpdateBounds();
var rotated = PolygonHelper.RotatePolygon(polygon, Angle.HalfPI);
rotated.UpdateBounds();
Assert.True(System.Math.Abs(rotated.BoundingBox.Width - 10) < 0.1);
Assert.True(System.Math.Abs(rotated.BoundingBox.Length - 20) < 0.1);
}
}