diff --git a/OpenNest.Engine.Tests/NestingEngines/RectanglesClampZoneTests.cs b/OpenNest.Engine.Tests/NestingEngines/RectanglesClampZoneTests.cs index 16b1dddd..5cf4cb11 100644 --- a/OpenNest.Engine.Tests/NestingEngines/RectanglesClampZoneTests.cs +++ b/OpenNest.Engine.Tests/NestingEngines/RectanglesClampZoneTests.cs @@ -43,7 +43,9 @@ public class RectanglesClampZoneTests new[] { Stock("sheet", 48, 96, spacing: 0.25) })))); [Theory] - [InlineData("9B343B6C5CF2CE8753F2E04ACB0A9C4D5921F4ADC15AA2586AF466769C0F019B")] + // Two complete candidates tie here on cost and placed material; the tie must fall to + // enumeration order on every platform (hash identical on Windows and Linux). + [InlineData("BEEEDEC81A7F987720FB5F331873103AD2470C839D568CE587F230BC39D210CC")] public void ZeroClampMixedShapeLayoutIsBitIdenticalToBaseline(string hash) => Assert.Equal(hash, LayoutHash(Engine().Solve(Job( new[] diff --git a/OpenNest.Engine/NestingEngines/Rectangles/SheetPacker.cs b/OpenNest.Engine/NestingEngines/Rectangles/SheetPacker.cs index 51f3b82b..684724fb 100644 --- a/OpenNest.Engine/NestingEngines/Rectangles/SheetPacker.cs +++ b/OpenNest.Engine/NestingEngines/Rectangles/SheetPacker.cs @@ -70,14 +70,18 @@ internal static class SheetPacker .Select(p => new Placed(types[p.Type], types[p.Type].Orientations[p.Size], p.X, p.Y)) .ToList(); - var area = 0.0; + var counts = new int[types.Count]; Box? envelope = null; foreach (var p in placed) { - area += p.Type.MaterialArea; + counts[p.Type.Index]++; var box = new Box(work.Left + p.Left, work.Bottom + p.Bottom, p.Orientation.Width, p.Orientation.Height); envelope = envelope == null ? box : Union(envelope, box); } + // Summed per type in type order, the same form as the engine's demand area: plans placing + // the same parts get bit-identical areas, so the engine's material-area tie-break cannot + // be decided by placement-order rounding (which differs across platform math libraries). + var area = types.Sum(t => counts[t.Index] * t.MaterialArea); var world = placed .Select(p => p with { Left = work.Left + p.Left, Bottom = work.Bottom + p.Bottom })