Files
OpenNest/OpenNest.Tests/Geometry/MinimumEnclosingCircleTests.cs
T
ajandClaude Opus 5.5 2174416dcc feat(engine): model any part as a circle for circle packing
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>
2026-10-18 12:49:36 -04:00

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);
}
}