Files
OpenNest/OpenNest.Engine.Tests/NestingEngines/FlatBarNestingEngineTests.cs
T
ajandClaude Opus 5.5 4d55e11318 style: use one spelling across the codebase
The repo mixed variants such as center/centre and color/colour, so a
name could be spelled either way and searches missed half the hits.
Standardize on the US spelling, matching .NET's own APIs (Color, Center).

Wire values owned by other systems keep their spelling: the material
ledger's "Cancelled" hold status and GitHub's "cancelled" job status.

No behavior change beyond the spelling of a few status messages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:11:30 -04:00

102 lines
3.6 KiB
C#

using System.Linq;
using OpenNest.Engine.Jobs;
using OpenNest.Engine.NestingEngines.FlatBar;
using OpenNest.Engine.NestingEngines.Rectangles;
using OpenNest.Geometry;
using static OpenNest.Engine.Tests.NestingEngines.JobBuilder;
using static OpenNest.Engine.Tests.NestingEngines.Shapes;
namespace OpenNest.Engine.Tests.NestingEngines;
public class FlatBarNestingEngineTests
{
[Fact]
public void CutListLengthsUseTheFewestBars()
{
// First fit decreasing needs three bars ([45,40] [35,30,25] [25]); two are exact.
var lengths = new[] { 45.0, 40, 35, 30, 25, 25 };
var job = Job(
lengths.Select((l, i) => Rectangle($"p{i}", l, 3, 1)).ToArray(),
[Stock("bar", 3, 100)]);
var result = new FlatBarNestingEngine().Solve(job);
LayoutAssert.Valid(job, result);
Assert.Equal(NestJobStatus.Complete, result.Status);
Assert.Equal(2, result.Plates.Count);
}
[Fact]
public void PartsLieInOneRowAlongTheBar()
{
var job = Job([Rectangle("p", 20, 3, 6)], [Stock("bar", 3, 144, spacing: 0.125)]);
var result = new FlatBarNestingEngine().Solve(job);
LayoutAssert.Valid(job, result);
var placements = Assert.Single(result.Plates).Placements;
Assert.Equal(6, placements.Count);
Assert.All(placements, p => Assert.Equal(0, p.Y, 9));
}
[Fact]
public void BarRunningAlongYIsCutAlongY()
{
var job = Job([Part("p", Rectangle(3, 20), 6, RotationPolicy.Automatic)], [Stock("bar", 144, 3, spacing: 0.125)]);
var result = new FlatBarNestingEngine().Solve(job);
LayoutAssert.Valid(job, result);
Assert.Equal(6, Assert.Single(result.Plates).Placements.Count);
}
[Fact]
public void MitredEndsInterlockByTurningAlternateParts()
{
// 45-degree mitres: a 23-long box, but turned copies nest 20 apart. Five fit on a
// 105 bar only when every other part is turned and slid into its neighbor.
var trapezoid = Part("mitre", Polyline((0, 0), (23, 0), (20, 3), (3, 3)), 5, RotationPolicy.Automatic);
var job = Job([trapezoid], [Stock("bar", 3, 105, spacing: 0.25)]);
var flatBar = new FlatBarNestingEngine().Solve(job);
var rectangles = new RectanglesNestingEngine().Solve(job);
LayoutAssert.Valid(job, flatBar);
Assert.Equal(NestJobStatus.Complete, flatBar.Status);
Assert.Single(flatBar.Plates);
Assert.Equal(2, rectangles.Plates.Count);
}
[Fact]
public void ClampZoneSplitsTheBar()
{
var clamp = new ClampZone("c", new Box(48, -1, 4, 5));
var job = Job([Rectangle("p", 45, 3, 2)], [Stock("bar", 3, 100, quantity: 1, clampZones: [clamp])]);
var result = new FlatBarNestingEngine().Solve(job);
LayoutAssert.Valid(job, result);
Assert.Equal(NestJobStatus.Complete, result.Status);
}
[Fact]
public void CheaperShortBarFinishesTheJob()
{
// 150 of parts: one long bar plus one short bar is cheaper than two long bars.
var job = Job(
[Rectangle("a", 90, 3, 1), Rectangle("b", 60, 3, 1)],
[
new NestPlateStock("long", new Size(3, 100), null, 0, default, 1, 100.0),
new NestPlateStock("short", new Size(3, 60), null, 0, default, 1, 60.0),
]);
var result = new FlatBarNestingEngine().Solve(job);
LayoutAssert.Valid(job, result);
Assert.Equal(NestJobStatus.Complete, result.Status);
Assert.Equal(["long", "short"], result.Plates.Select(p => p.StockId).Order());
}
}
public sealed class FlatBarContractTests : EngineContractTests<FlatBarNestingEngine> { }