0424d8db20
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.1 KiB
4.1 KiB
Rotating Calipers for Optimal Rotation
Problem
The current FindBestRotation is brute-force: it samples rotations at fixed stepAngle intervals and returns the angle with the smallest axis-aligned bounding box area. This is O(n * s) where s = number of step samples, and it can miss the true optimum between samples.
Solution
Replace it with the rotating calipers algorithm, which finds the exact minimum bounding rectangle of a convex polygon in O(n log n) time (dominated by hull computation). The algorithm exploits the fact that the minimum-area bounding rectangle of a convex polygon must have one side flush with a hull edge.
New Files
1. OpenNest.Core/Geometry/ConvexHull.cs
- Static class in
namespace OpenNest.Geometry Compute(IList<Vector> points)returns aPolygon(convex hull)- Algorithm: Andrew's monotone chain -- O(n log n), numerically stable
- Returns vertices in counter-clockwise order (consistent with existing
Polygonconventions)
2. OpenNest.Core/Geometry/RotatingCalipers.cs
- Static class in
namespace OpenNest.Geometry - Primary method:
MinimumBoundingRectangle(Polygon convexHull)returnsBoundingRectangleResult - Overloads:
MinimumBoundingRectangle(Polygon hull, double startAngle, double endAngle)-- filters candidates to constraint range, evaluates range boundaries as fallbackMinimumBoundingRectangle(IList<Vector> points)-- convenience overload that computes hull internally
- Optimization target: minimum area by default; result contains both dimensions so callers can choose minimum width
3. BoundingRectangleResult (in RotatingCalipers.cs)
public class BoundingRectangleResult
{
public double Angle { get; } // Optimal rotation in radians
public double Width { get; } // Rectangle width at optimal angle
public double Height { get; } // Rectangle height at optimal angle
public double Area { get; } // Width * Height
}
Modified Files
4. OpenNest.Core/Geometry/Entity.cs
- Change
FindBestRotationextension method signature:- Old:
double FindBestRotation(this List<Entity> entities, double stepAngle, ...) - New:
BoundingRectangleResult FindBestRotation(this List<Entity> entities, double startAngle = 0, double endAngle = Angle.TwoPI)
- Old:
stepAngleparameter removed (no longer needed -- exact solution)- Implementation: extract vertices from entities, call
RotatingCalipers.MinimumBoundingRectangle()
5. OpenNest.Core/Geometry/Polygon.cs
- Update
FindBestRotationwrapper methods to match new signature and returnBoundingRectangleResult
6. OpenNest.Core/Geometry/Shape.cs
- Update
FindBestRotationwrapper methods to match new signature and returnBoundingRectangleResult
7. All callers of FindBestRotation
- Update to use
BoundingRectangleResultinstead ofdouble - Access
.Anglefor the rotation value where only the angle was used before
Algorithm Detail
Rotating Calipers for Minimum Bounding Rectangle
- Compute convex hull (Andrew's monotone chain)
- For each edge of the hull, compute the angle it makes with the x-axis
- Project all hull vertices onto the edge direction and its perpendicular
- Compute the bounding rectangle from the min/max projections
- Track the rectangle with minimum area (and separately, minimum width)
- When constrained by
startAngle/endAngle: filter candidate angles to range, also evaluate the range boundaries, return best valid result
Rotation Constraint Handling
The algorithm naturally produces a set of candidate angles (one per hull edge). For constrained rotation:
- Compute all candidate angles and their bounding rectangles
- Filter to candidates within
[startAngle, endAngle] - Also evaluate the bounding rectangle at
startAngleandendAngle(range boundaries) - Return the best result from the filtered set
What This Does NOT Change
- The nesting engine's packing algorithms (still uses axis-aligned rectangles for bin packing)
- The
NestItem,Part, orProgramrotation mechanics - The
Box/BoundingBoxclasses (remain axis-aligned)