Files
OpenNest/OpenNest.Engine/BestFit/PolygonHelper.cs
T
ajandClaude Opus 5.5 0186ccfbff feat(nest): keep parts out of tabbed cutouts
A tabbed cutout's slug stays attached while the sheet is cut, but nesting
saw a closed hole and could place parts inside it. Nesting and layout paths
now read material through NestingMaterial.ForNesting, which drops closed
contours with tab spans so the slug counts as part. That covers part
outlines and collision lines, the cutout router, fill expansion, best-fit,
NestLayoutCheck and the GPU bitmaps. Area, overlap diagnostics and cutting
keep the hole, and programs without tabs read exactly as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 01:14:22 -04:00

79 lines
2.6 KiB
C#

using System.Linq;
using OpenNest.Converters;
using OpenNest.Geometry;
using OpenNest.Math;
namespace OpenNest.Engine.BestFit
{
public static class PolygonHelper
{
public static PolygonExtractionResult ExtractPerimeterPolygon(
Drawing drawing,
double halfSpacing
)
{
var entities = ConvertProgram
.ToGeometry(drawing.Program)
.ForNesting()
.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);
// Circumscribe so the polygon never under-estimates the part (or its offset).
var polygon =
halfSpacing > 0
? ClipperBridge
.OffsetPerimeter(perimeter, halfSpacing, 0.01, circumscribe: true)
.LargestOuter()
: perimeter.ToPolygonWithTolerance(0.01, circumscribe: true);
if (polygon == null || polygon.Vertices.Count < 3)
return new PolygonExtractionResult(null, Vector.Zero);
// Normalize: move polygon to origin.
polygon.UpdateBounds();
var bb = polygon.BoundingBox;
polygon.Offset(-bb.Left, -bb.Bottom);
// No correction needed: BestFitFinder always pairs the same drawing with
// itself, so the polygon-to-part offset is identical for both parts and
// cancels out in the NFP displacement.
return new PolygonExtractionResult(polygon, Vector.Zero);
}
public static Polygon RotatePolygon(Polygon polygon, double angle, bool reNormalize = true)
{
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));
}
if (reNormalize)
{
// 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);
}