mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-03 13:31:39 -05:00
fix(support): measure a part the same however it is turned
A 5 x 5 right triangle fits both its legs' square and its hypotenuse's 7.07 x 3.54 box, each of area 25, and rotating calipers kept whichever rounding found first, so the same gusset measured 5 long at one rotation and 7.07 at another. Of rectangles tied for least area, measure the one with the shorter long side, so length, width and the tab tie-break no longer depend on placement. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ceddd8cf93
commit
bfc36d166c
1 file changed
+41
-3
@@ -180,9 +180,47 @@ namespace OpenNest.Support
|
||||
return new Vector(System.Math.Cos(angle), System.Math.Sin(angle));
|
||||
}
|
||||
|
||||
// The full angle range checks every hull edge.
|
||||
private static BoundingRectangleResult BoundingRectangle(IReadOnlyList<Vector> outline) =>
|
||||
RotatingCalipers.MinimumBoundingRectangle(ConvexHull.Compute(outline.ToList()), 0, Math.Angle.TwoPI);
|
||||
// The minimum-area rectangle over every hull edge. Shapes such as a right isosceles
|
||||
// triangle have more than one (its legs' square and its hypotenuse's 2:1 box have the same
|
||||
// area), and which one rounding picks depends on the part's rotation; of rectangles tied
|
||||
// for area, the one with the shorter long side wins, so a drawing measures the same
|
||||
// however it is placed.
|
||||
private static BoundingRectangleResult BoundingRectangle(IReadOnlyList<Vector> outline)
|
||||
{
|
||||
var hull = ConvexHull.Compute(outline.ToList()).Vertices;
|
||||
var n = hull.Count > 1 && hull[0] == hull[^1] ? hull.Count - 1 : hull.Count;
|
||||
if (n < 3)
|
||||
return RotatingCalipers.MinimumBoundingRectangle(outline.ToList());
|
||||
|
||||
var candidates = new List<BoundingRectangleResult>(n);
|
||||
for (var i = 0; i < n; i++)
|
||||
{
|
||||
var edge = hull[(i + 1) % n] - hull[i];
|
||||
var length = System.Math.Sqrt(edge.X * edge.X + edge.Y * edge.Y);
|
||||
if (length <= Math.Tolerance.Epsilon)
|
||||
continue;
|
||||
var u = new Vector(edge.X / length, edge.Y / length);
|
||||
var v = new Vector(-u.Y, u.X);
|
||||
double minU = double.MaxValue, maxU = double.MinValue, minV = double.MaxValue, maxV = double.MinValue;
|
||||
for (var j = 0; j < n; j++)
|
||||
{
|
||||
var pu = hull[j].X * u.X + hull[j].Y * u.Y;
|
||||
var pv = hull[j].X * v.X + hull[j].Y * v.Y;
|
||||
minU = System.Math.Min(minU, pu);
|
||||
maxU = System.Math.Max(maxU, pu);
|
||||
minV = System.Math.Min(minV, pv);
|
||||
maxV = System.Math.Max(maxV, pv);
|
||||
}
|
||||
candidates.Add(new BoundingRectangleResult(System.Math.Atan2(edge.Y, edge.X), maxU - minU, maxV - minV));
|
||||
}
|
||||
if (candidates.Count == 0)
|
||||
return RotatingCalipers.MinimumBoundingRectangle(outline.ToList());
|
||||
|
||||
var least = candidates.Min(c => c.Area);
|
||||
var slack = Math.Tolerance.Epsilon * System.Math.Max(least, 1);
|
||||
return candidates.Where(c => c.Area <= least + slack)
|
||||
.MinBy(c => System.Math.Max(c.Width, c.Height));
|
||||
}
|
||||
|
||||
// A point `clear` from the nearest inside corner keeps `clearance` from it; with no
|
||||
// clearance it must still not be the corner.
|
||||
|
||||
Reference in new issue
Block a user