feat: update FindBestRotation to use rotating calipers algorithm

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 22:49:09 -05:00
parent a190ed25b4
commit 1c8763a201
3 changed files with 56 additions and 36 deletions
+47 -26
View File
@@ -231,39 +231,60 @@ namespace OpenNest.Geometry
public static class EntityExtensions public static class EntityExtensions
{ {
public static double FindBestRotation(this List<Entity> entities, double stepAngle, double startAngle = 0, double endAngle = Angle.TwoPI) public static BoundingRectangleResult FindBestRotation(this List<Entity> entities, double startAngle = 0, double endAngle = Angle.TwoPI)
{ {
startAngle = Angle.NormalizeRad(startAngle); var points = new List<Vector>();
if (!endAngle.IsEqualTo(Angle.TwoPI)) foreach (var entity in entities)
endAngle = Angle.NormalizeRad(endAngle);
if (stepAngle.IsEqualTo(0.0))
return startAngle;
entities.ForEach(e => e.Rotate(startAngle));
var bestAngle = startAngle;
var bestArea = entities.GetBoundingBox().Area();
var steps = startAngle < endAngle
? (endAngle - startAngle) / stepAngle
: (endAngle + Angle.TwoPI) - startAngle / stepAngle;
for (int i = 1; i <= steps; ++i)
{ {
entities.ForEach(e => e.Rotate(stepAngle)); switch (entity.Type)
var area = entities.GetBoundingBox().Area();
if (area < bestArea)
{ {
bestArea = area; case EntityType.Line:
bestAngle = startAngle + stepAngle * i; var line = (Line)entity;
points.Add(line.StartPoint);
points.Add(line.EndPoint);
break;
case EntityType.Arc:
var arc = (Arc)entity;
points.Add(arc.StartPoint());
points.Add(arc.EndPoint());
points.Add(arc.Center.Offset(arc.Radius, 0));
points.Add(arc.Center.Offset(-arc.Radius, 0));
points.Add(arc.Center.Offset(0, arc.Radius));
points.Add(arc.Center.Offset(0, -arc.Radius));
break;
case EntityType.Circle:
var circle = (Circle)entity;
points.Add(circle.Center.Offset(circle.Radius, 0));
points.Add(circle.Center.Offset(-circle.Radius, 0));
points.Add(circle.Center.Offset(0, circle.Radius));
points.Add(circle.Center.Offset(0, -circle.Radius));
break;
case EntityType.Polygon:
var polygon = (Polygon)entity;
points.AddRange(polygon.Vertices);
break;
case EntityType.Shape:
var shape = (Shape)entity;
var subResult = shape.Entities.FindBestRotation(startAngle, endAngle);
return subResult;
} }
} }
return bestAngle; if (points.Count == 0)
return new BoundingRectangleResult(startAngle, 0, 0);
var hull = ConvexHull.Compute(points);
bool constrained = !startAngle.IsEqualTo(0) || !endAngle.IsEqualTo(Angle.TwoPI);
return constrained
? RotatingCalipers.MinimumBoundingRectangle(hull, startAngle, endAngle)
: RotatingCalipers.MinimumBoundingRectangle(hull);
} }
} }
} }
+5 -6
View File
@@ -598,16 +598,15 @@ namespace OpenNest.Geometry
} }
} }
public double FindBestRotation(double stepAngle) public BoundingRectangleResult FindBestRotation()
{ {
var entities = new List<Entity>(ToLines()); return RotatingCalipers.MinimumBoundingRectangle(Vertices);
return entities.FindBestRotation(stepAngle);
} }
public double FindBestRotation(double stepAngle, double startAngle, double endAngle) public BoundingRectangleResult FindBestRotation(double startAngle, double endAngle)
{ {
var entities = new List<Entity>(ToLines()); var hull = ConvexHull.Compute(Vertices);
return entities.FindBestRotation(stepAngle, startAngle, endAngle); return RotatingCalipers.MinimumBoundingRectangle(hull, startAngle, endAngle);
} }
} }
} }
+4 -4
View File
@@ -599,14 +599,14 @@ namespace OpenNest.Geometry
get { return EntityType.Shape; } get { return EntityType.Shape; }
} }
public double FindBestRotation(double stepAngle) public BoundingRectangleResult FindBestRotation()
{ {
return Entities.FindBestRotation(stepAngle); return Entities.FindBestRotation();
} }
public double FindBestRotation(double stepAngle, double startAngle, double endAngle) public BoundingRectangleResult FindBestRotation(double startAngle, double endAngle)
{ {
return Entities.FindBestRotation(stepAngle, startAngle, endAngle); return Entities.FindBestRotation(startAngle, endAngle);
} }
} }
} }