feat: add Box.Translate and improve NFP/IFP geometry APIs
Add immutable Translate methods to Box. Make NoFitPolygon ToClipperPath/FromClipperPath public with optional offset parameter. Refactor InnerFitPolygon.ComputeFeasibleRegion to accept PathsD directly, letting Clipper2 handle implicit union. Add UpdateBounds calls after polygon construction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,16 @@ namespace OpenNest.Geometry
|
|||||||
Location += voffset;
|
Location += voffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Box Translate(double x, double y)
|
||||||
|
{
|
||||||
|
return new Box(X + x, Y + y, Width, Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Box Translate(Vector offset)
|
||||||
|
{
|
||||||
|
return new Box(X + offset.X, Y + offset.Y, Width, Length);
|
||||||
|
}
|
||||||
|
|
||||||
public double Left
|
public double Left
|
||||||
{
|
{
|
||||||
get { return X; }
|
get { return X; }
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ namespace OpenNest.Geometry
|
|||||||
result.Vertices.Add(new Vector(ifpRight, ifpTop));
|
result.Vertices.Add(new Vector(ifpRight, ifpTop));
|
||||||
result.Vertices.Add(new Vector(ifpLeft, ifpTop));
|
result.Vertices.Add(new Vector(ifpLeft, ifpTop));
|
||||||
result.Close();
|
result.Close();
|
||||||
|
result.UpdateBounds();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -62,36 +63,20 @@ namespace OpenNest.Geometry
|
|||||||
/// Returns the polygon representing valid placement positions, or an empty
|
/// Returns the polygon representing valid placement positions, or an empty
|
||||||
/// polygon if no valid position exists.
|
/// polygon if no valid position exists.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Polygon ComputeFeasibleRegion(Polygon ifp, Polygon[] nfps)
|
public static Polygon ComputeFeasibleRegion(Polygon ifp, PathsD nfpPaths)
|
||||||
{
|
{
|
||||||
if (ifp.Vertices.Count < 3)
|
if (ifp.Vertices.Count < 3)
|
||||||
return new Polygon();
|
return new Polygon();
|
||||||
|
|
||||||
if (nfps == null || nfps.Length == 0)
|
if (nfpPaths == null || nfpPaths.Count == 0)
|
||||||
return ifp;
|
return ifp;
|
||||||
|
|
||||||
var ifpPath = NoFitPolygon.ToClipperPath(ifp);
|
var ifpPath = NoFitPolygon.ToClipperPath(ifp);
|
||||||
var ifpPaths = new PathsD { ifpPath };
|
var ifpPaths = new PathsD { ifpPath };
|
||||||
|
|
||||||
// Union all NFPs.
|
// Subtract the NFPs from the IFP.
|
||||||
var nfpPaths = new PathsD();
|
// Clipper2 handles the implicit union of the clip paths.
|
||||||
|
var feasible = Clipper.Difference(ifpPaths, nfpPaths, FillRule.NonZero);
|
||||||
foreach (var nfp in nfps)
|
|
||||||
{
|
|
||||||
if (nfp.Vertices.Count >= 3)
|
|
||||||
{
|
|
||||||
var path = NoFitPolygon.ToClipperPath(nfp);
|
|
||||||
nfpPaths.Add(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nfpPaths.Count == 0)
|
|
||||||
return ifp;
|
|
||||||
|
|
||||||
var nfpUnion = Clipper.Union(nfpPaths, FillRule.NonZero);
|
|
||||||
|
|
||||||
// Subtract the NFP union from the IFP.
|
|
||||||
var feasible = Clipper.Difference(ifpPaths, nfpUnion, FillRule.NonZero);
|
|
||||||
|
|
||||||
if (feasible.Count == 0)
|
if (feasible.Count == 0)
|
||||||
return new Polygon();
|
return new Polygon();
|
||||||
@@ -118,6 +103,25 @@ namespace OpenNest.Geometry
|
|||||||
return bestPath != null ? NoFitPolygon.FromClipperPath(bestPath) : new Polygon();
|
return bestPath != null ? NoFitPolygon.FromClipperPath(bestPath) : new Polygon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the feasible region for placing a part given already-placed parts.
|
||||||
|
/// (Legacy overload for backward compatibility).
|
||||||
|
/// </summary>
|
||||||
|
public static Polygon ComputeFeasibleRegion(Polygon ifp, Polygon[] nfps)
|
||||||
|
{
|
||||||
|
if (nfps == null || nfps.Length == 0)
|
||||||
|
return ifp;
|
||||||
|
|
||||||
|
var nfpPaths = new PathsD(nfps.Length);
|
||||||
|
foreach (var nfp in nfps)
|
||||||
|
{
|
||||||
|
if (nfp.Vertices.Count >= 3)
|
||||||
|
nfpPaths.Add(NoFitPolygon.ToClipperPath(nfp));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ComputeFeasibleRegion(ifp, nfpPaths);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the bottom-left-most point on a polygon boundary.
|
/// Finds the bottom-left-most point on a polygon boundary.
|
||||||
/// "Bottom-left" means: minimize Y first, then minimize X.
|
/// "Bottom-left" means: minimize Y first, then minimize X.
|
||||||
|
|||||||
@@ -250,9 +250,9 @@ namespace OpenNest.Geometry
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an OpenNest Polygon to a Clipper2 PathD.
|
/// Converts an OpenNest Polygon to a Clipper2 PathD, with an optional offset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static PathD ToClipperPath(Polygon polygon)
|
public static PathD ToClipperPath(Polygon polygon, Vector offset = default)
|
||||||
{
|
{
|
||||||
var path = new PathD();
|
var path = new PathD();
|
||||||
var verts = polygon.Vertices;
|
var verts = polygon.Vertices;
|
||||||
@@ -263,7 +263,7 @@ namespace OpenNest.Geometry
|
|||||||
n--;
|
n--;
|
||||||
|
|
||||||
for (var i = 0; i < n; i++)
|
for (var i = 0; i < n; i++)
|
||||||
path.Add(new PointD(verts[i].X, verts[i].Y));
|
path.Add(new PointD(verts[i].X + offset.X, verts[i].Y + offset.Y));
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
@@ -271,7 +271,7 @@ namespace OpenNest.Geometry
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a Clipper2 PathD to an OpenNest Polygon.
|
/// Converts a Clipper2 PathD to an OpenNest Polygon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static Polygon FromClipperPath(PathD path)
|
public static Polygon FromClipperPath(PathD path)
|
||||||
{
|
{
|
||||||
var polygon = new Polygon();
|
var polygon = new Polygon();
|
||||||
|
|
||||||
@@ -279,6 +279,7 @@ namespace OpenNest.Geometry
|
|||||||
polygon.Vertices.Add(new Vector(pt.x, pt.y));
|
polygon.Vertices.Add(new Vector(pt.x, pt.y));
|
||||||
|
|
||||||
polygon.Close();
|
polygon.Close();
|
||||||
|
polygon.UpdateBounds();
|
||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user