From 365825bc5ad21ed27d1fcd8df3c0816c02aaaa99 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 21 Sep 2026 10:05:18 -0400 Subject: [PATCH] fix(engine): shrink the requested axis in ShrinkFiller estimates ShrinkAxis.Length must shrink the Y extent and ShrinkAxis.Width the X extent to agree with MeasureDimension/TrimToCount; Box's constructor takes the X extent first. The estimate previously shrank the wrong dimension and returned a mis-ordered box. Add a parameterized regression test over both axes and both translated-remnant orientations. --- .../Fill/ShrinkWorkAreaTests.cs | 40 +++++++++++++++++++ OpenNest.Engine/Fill/ShrinkFiller.cs | 8 ++-- 2 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 OpenNest.Engine.Tests/Fill/ShrinkWorkAreaTests.cs diff --git a/OpenNest.Engine.Tests/Fill/ShrinkWorkAreaTests.cs b/OpenNest.Engine.Tests/Fill/ShrinkWorkAreaTests.cs new file mode 100644 index 0000000..4763c88 --- /dev/null +++ b/OpenNest.Engine.Tests/Fill/ShrinkWorkAreaTests.cs @@ -0,0 +1,40 @@ +using OpenNest.Engine.Fill; +using OpenNest.Engine.Tests.Jobs; +using OpenNest.Geometry; + +namespace OpenNest.Engine.Tests.Fill; + +public class ShrinkWorkAreaTests +{ + [Theory] + [InlineData(ShrinkAxis.Length, 30, 60)] + [InlineData(ShrinkAxis.Length, 60, 30)] + [InlineData(ShrinkAxis.Width, 30, 60)] + [InlineData(ShrinkAxis.Width, 60, 30)] + public void EstimateStartBox_ShrinksOnlyRequestedAxis_WithinTranslatedRectangularRemnant( + ShrinkAxis axis, double length, double width) + { + var item = new NestItem + { + Drawing = new Drawing("rectangle", TestDrawingFactory.Rectangle(4, 3)), + Quantity = 2, + }; + var box = new Box(80, 7, length, width); + + var estimate = ShrinkFiller.EstimateStartBox(item, box, 0.3, axis, 2); + + Assert.Equal(box.Location, estimate.Location); + Assert.True(box.Contains(estimate), "A shrink estimate must never expand beyond its input remnant."); + // ShrinkAxis follows the fill/trim direction: Length trims top, Width trims right. + if (axis == ShrinkAxis.Length) + { + Assert.Equal(box.Length, estimate.Length); + Assert.InRange(estimate.Width, double.Epsilon, box.Width - double.Epsilon); + } + else + { + Assert.Equal(box.Width, estimate.Width); + Assert.InRange(estimate.Length, double.Epsilon, box.Length - double.Epsilon); + } + } +} diff --git a/OpenNest.Engine/Fill/ShrinkFiller.cs b/OpenNest.Engine/Fill/ShrinkFiller.cs index 5fefd8a..2f7a8e6 100644 --- a/OpenNest.Engine/Fill/ShrinkFiller.cs +++ b/OpenNest.Engine/Fill/ShrinkFiller.cs @@ -120,7 +120,9 @@ namespace OpenNest.Engine.Fill if (bbox.Width <= 0 || bbox.Length <= 0) return box; - var maxDim = axis == ShrinkAxis.Length ? box.Length : box.Width; + // Match MeasureDimension/TrimToCount: Length shrinks Y, Width shrinks X. + // Box's constructor takes X extent (Length), then Y extent (Width). + var maxDim = axis == ShrinkAxis.Length ? box.Width : box.Length; // Use FillBestFit for a fast, accurate rectangle count on the full box. var bin = new Bin { Size = new Size(box.Width, box.Length) }; @@ -144,8 +146,8 @@ namespace OpenNest.Engine.Fill return box; return axis == ShrinkAxis.Length - ? new Box(box.X, box.Y, box.Width, estimate) - : new Box(box.X, box.Y, estimate, box.Length); + ? new Box(box.X, box.Y, box.Length, estimate) + : new Box(box.X, box.Y, estimate, box.Width); } private static double MeasureDimension(List parts, Box box, ShrinkAxis axis)