mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-08 02:05:44 -05:00
fix(rectangles): break exact material-area ties the same way on every platform
ZeroClampMixedShapeLayoutIsBitIdenticalToBaseline has failed on Windows since it was added inde9a087while passing on Linux CI (bisect pointing atc374e02was an artifact: the assumed-good start commit fails too). No commit changed the layout; the two platforms always chose differently. In that fixture two candidate sheets (ContactPoint/Global and BestShortSide/Ordered) both place all 30 parts at the same cost, so Choose falls to its "more material placed" tie-break. SheetPacker summed material area in placement order, and the disc's area differs in the last bit between the Windows CRT and glibc math libraries. On Linux the two orders rounded to ...253 vs ...257, so the later candidate won on rounding noise; on Windows both rounded to ...257 and the tie fell to enumeration order as Choose documents. Sum a plan's area per type in type order instead (the same form as the engine's demand area), so plans placing the same parts get bit-identical areas and exact ties keep enumeration order everywhere. No tolerance is loosened. The Windows layout is unchanged; Linux now matches it, so the frozen hash moves to the Windows value, verified identical on both. Verified: full Engine.Tests pass on Windows and Linux (1105 passed, 2 skipped); Rectangles and Default benchmark results on the six synthetic nests are identical before and after (validity, placed parts, cost, utilization, plates). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9f627c1a9d
commit
4e0699de7a
2 files changed
+9
-3
No files matched your search
@@ -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[]
|
||||
|
||||
@@ -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 })
|
||||
|
||||
Reference in new issue
Block a user