mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-28 14:51:06 -04:00
Item.Create builds the circle packer's view of a part: the minimum enclosing circle of its outward-flattened perimeter (padded by the layout check's flattening allowance), its fill ratio, the offset back to the part origin, and the largest disc clear of material inside its cutouts. Gears and sprockets pack by their tip circle around their bore; the fill ratio lets a future engine decide which jobs to bid on instead of a yes/no round test. Adds MinimumEnclosingCircle (Welzl, repeatable order) to Core geometry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
2.4 KiB
C#
76 lines
2.4 KiB
C#
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.Tests.Geometry;
|
|
|
|
public class MinimumEnclosingCircleTests
|
|
{
|
|
[Fact]
|
|
public void SinglePoint_HasZeroRadius()
|
|
{
|
|
var circle = MinimumEnclosingCircle.Compute(new[] { new Vector(3, 4) });
|
|
|
|
Assert.Equal(new Vector(3, 4), circle.Center);
|
|
Assert.Equal(0, circle.Radius);
|
|
}
|
|
|
|
[Fact]
|
|
public void Square_IsCircumscribed()
|
|
{
|
|
var circle = MinimumEnclosingCircle.Compute(new[]
|
|
{
|
|
new Vector(0, 0), new Vector(10, 0), new Vector(10, 10), new Vector(0, 10), new Vector(5, 5),
|
|
});
|
|
|
|
Assert.Equal(5, circle.Center.X, 9);
|
|
Assert.Equal(5, circle.Center.Y, 9);
|
|
Assert.Equal(System.Math.Sqrt(50), circle.Radius, 9);
|
|
}
|
|
|
|
[Fact]
|
|
public void ObtuseTriangle_UsesLongestSideAsDiameter()
|
|
{
|
|
var circle = MinimumEnclosingCircle.Compute(new[] { new Vector(0, 0), new Vector(10, 0), new Vector(5, 1) });
|
|
|
|
Assert.Equal(5, circle.Center.X, 9);
|
|
Assert.Equal(0, circle.Center.Y, 9);
|
|
Assert.Equal(5, circle.Radius, 9);
|
|
}
|
|
|
|
[Fact]
|
|
public void CollinearPoints_SpanTheEnds()
|
|
{
|
|
var circle = MinimumEnclosingCircle.Compute(new[] { new Vector(0, 0), new Vector(4, 4), new Vector(2, 2), new Vector(1, 1) });
|
|
|
|
Assert.Equal(2, circle.Center.X, 9);
|
|
Assert.Equal(2, circle.Center.Y, 9);
|
|
Assert.Equal(System.Math.Sqrt(8), circle.Radius, 9);
|
|
}
|
|
|
|
[Fact]
|
|
public void RandomPoints_AreAllCoveredByAMinimalCircle()
|
|
{
|
|
var random = new Random(7);
|
|
var points = Enumerable.Range(0, 500)
|
|
.Select(_ => new Vector(random.NextDouble() * 100 - 50, random.NextDouble() * 40 - 20))
|
|
.ToList();
|
|
|
|
var circle = MinimumEnclosingCircle.Compute(points);
|
|
|
|
Assert.All(points, p => Assert.True(p.DistanceTo(circle.Center) <= circle.Radius));
|
|
// A minimal circle touches at least two points; a larger one could shrink.
|
|
Assert.True(points.Count(p => circle.Radius - p.DistanceTo(circle.Center) <= 1e-9) >= 2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Result_IsRepeatable()
|
|
{
|
|
var points = Enumerable.Range(0, 50).Select(i => new Vector(System.Math.Cos(i * 0.7) * i, System.Math.Sin(i * 1.3) * i)).ToList();
|
|
|
|
var first = MinimumEnclosingCircle.Compute(points);
|
|
var second = MinimumEnclosingCircle.Compute(points);
|
|
|
|
Assert.Equal(first.Center, second.Center);
|
|
Assert.Equal(first.Radius, second.Radius);
|
|
}
|
|
}
|