refactor: extract PolygonHelper from AutoNester for shared polygon operations
Creates PolygonHelper.cs in OpenNest.Engine.BestFit with ExtractPerimeterPolygon (returning PolygonExtractionResult with polygon + correction vector) and RotatePolygon. AutoNester.ExtractPerimeterPolygon and RotatePolygon become thin delegates. Adds MakeSquareDrawing/MakeLShapeDrawing to TestHelpers and 6 PolygonHelperTests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
89
OpenNest.Engine/BestFit/PolygonHelper.cs
Normal file
89
OpenNest.Engine/BestFit/PolygonHelper.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using OpenNest.Converters;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.Math;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenNest.Engine.BestFit
|
||||
{
|
||||
public static class PolygonHelper
|
||||
{
|
||||
public static PolygonExtractionResult ExtractPerimeterPolygon(Drawing drawing, double halfSpacing)
|
||||
{
|
||||
var entities = ConvertProgram.ToGeometry(drawing.Program)
|
||||
.Where(e => e.Layer != SpecialLayers.Rapid)
|
||||
.ToList();
|
||||
|
||||
if (entities.Count == 0)
|
||||
return new PolygonExtractionResult(null, Vector.Zero);
|
||||
|
||||
var definedShape = new ShapeProfile(entities);
|
||||
var perimeter = definedShape.Perimeter;
|
||||
|
||||
if (perimeter == null)
|
||||
return new PolygonExtractionResult(null, Vector.Zero);
|
||||
|
||||
// Compute the perimeter bounding box before inflation for coordinate correction.
|
||||
perimeter.UpdateBounds();
|
||||
var perimeterBb = perimeter.BoundingBox;
|
||||
|
||||
// Inflate by half-spacing if spacing is non-zero.
|
||||
Shape inflated;
|
||||
|
||||
if (halfSpacing > 0)
|
||||
{
|
||||
var offsetEntity = perimeter.OffsetEntity(halfSpacing, OffsetSide.Left);
|
||||
inflated = offsetEntity as Shape ?? perimeter;
|
||||
}
|
||||
else
|
||||
{
|
||||
inflated = perimeter;
|
||||
}
|
||||
|
||||
// Convert to polygon with circumscribed arcs for tight nesting.
|
||||
var polygon = inflated.ToPolygonWithTolerance(0.01, circumscribe: true);
|
||||
|
||||
if (polygon.Vertices.Count < 3)
|
||||
return new PolygonExtractionResult(null, Vector.Zero);
|
||||
|
||||
// Compute correction: difference between program origin and perimeter origin.
|
||||
// Part.CreateAtOrigin normalizes to program bbox; polygon normalizes to perimeter bbox.
|
||||
var programBb = drawing.Program.BoundingBox();
|
||||
var correction = new Vector(
|
||||
perimeterBb.Left - programBb.Location.X,
|
||||
perimeterBb.Bottom - programBb.Location.Y);
|
||||
|
||||
// Normalize: move reference point to origin.
|
||||
polygon.UpdateBounds();
|
||||
var bb = polygon.BoundingBox;
|
||||
polygon.Offset(-bb.Left, -bb.Bottom);
|
||||
|
||||
return new PolygonExtractionResult(polygon, correction);
|
||||
}
|
||||
|
||||
public static Polygon RotatePolygon(Polygon polygon, double angle)
|
||||
{
|
||||
if (angle.IsEqualTo(0))
|
||||
return polygon;
|
||||
|
||||
var result = new Polygon();
|
||||
var cos = System.Math.Cos(angle);
|
||||
var sin = System.Math.Sin(angle);
|
||||
|
||||
foreach (var v in polygon.Vertices)
|
||||
{
|
||||
result.Vertices.Add(new Vector(
|
||||
v.X * cos - v.Y * sin,
|
||||
v.X * sin + v.Y * cos));
|
||||
}
|
||||
|
||||
// Re-normalize to origin.
|
||||
result.UpdateBounds();
|
||||
var bb = result.BoundingBox;
|
||||
result.Offset(-bb.Left, -bb.Bottom);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public record PolygonExtractionResult(Polygon Polygon, Vector Correction);
|
||||
}
|
||||
Reference in New Issue
Block a user