From 83b606c486b95a3acc00c9905d0f9b63bbd5f411 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Wed, 14 Oct 2026 18:04:04 -0400 Subject: [PATCH] fix(geometry): end PolyLabel's starting grid where steps stop advancing Far from the origin a grid step smaller than the coordinate's precision leaves x (or y) unchanged: at 1e16, 1e16 + 1 == 1e16. The starting grid loop then never ended and allocated cells without limit, even with the new maxCells budget (a hang dump reached 4.3 GB). Stop a row or column once its step no longer advances. Grids that advance are untouched, so the bit-identical differential against the former sorted-list implementation still passes. Found by the independent quality review of the Clipper2 overlap branch. --- OpenNest.Core/Geometry/PolyLabel.cs | 10 ++++++++++ .../Geometry/PolyLabelDifferentialTests.cs | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/OpenNest.Core/Geometry/PolyLabel.cs b/OpenNest.Core/Geometry/PolyLabel.cs index a0312ff2..92b92e2f 100644 --- a/OpenNest.Core/Geometry/PolyLabel.cs +++ b/OpenNest.Core/Geometry/PolyLabel.cs @@ -73,8 +73,18 @@ namespace OpenNest.Geometry var initial = new List(); for (var x = minX; x < maxX; x += cellSize) + { for (var y = minY; y < maxY; y += cellSize) + { initial.Add(new Cell(x + halfCell, y + halfCell, halfCell, outer, holes)); + // Far from the origin a step smaller than the coordinate's precision + // no longer advances; the grid would never end. + if (y + cellSize == y) + break; + } + if (x + cellSize == x) + break; + } initial.Sort((a, b) => b.MaxDist.CompareTo(a.MaxDist)); diff --git a/OpenNest.Tests/Geometry/PolyLabelDifferentialTests.cs b/OpenNest.Tests/Geometry/PolyLabelDifferentialTests.cs index 95144815..e010d96a 100644 --- a/OpenNest.Tests/Geometry/PolyLabelDifferentialTests.cs +++ b/OpenNest.Tests/Geometry/PolyLabelDifferentialTests.cs @@ -68,6 +68,21 @@ public class PolyLabelDifferentialTests Assert.True(Same(PolyLabel.Find(shape), PolyLabel.Find(shape, null, 0.5, int.MaxValue))); } + // Quality review: at 1e16 a 1-unit grid step no longer changes x (1e16 + 1 == 1e16), and + // the starting grid loop never ended, allocating cells without limit even with a budget. + [Fact] + public async Task Find_FinishesWhenGridStepsStopAdvancingFarFromTheOrigin() + { + var far = Rectangle(1e16, 0, 8, 1); + + var search = Task.Run(() => (PolyLabel.Find(far, null, 0.01, 4096), PolyLabel.Find(far, null, 0.01))); + + Assert.Same(search, await Task.WhenAny(search, Task.Delay(TimeSpan.FromSeconds(20)))); + var (bounded, unbounded) = await search; + Assert.InRange(bounded.Y, 0, 1); + Assert.InRange(unbounded.Y, 0, 1); + } + private static (Polygon outer, List holes) RandomShape(Random random) { var scale = System.Math.Pow(10, random.NextDouble() * 4 - 1);