The Drawing Library Grid check found seven drawings with 1,000-1,700
holes (90-6138-26/27, Reeves bakery vent pans, 4938 A15) that took
0.5-3.6 s to preview, all of it in the part label point.
- PolyLabel skips a hole's containment test outside its bounds and its
edge distances when its bounds are farther than the nearest edge so
far. Both skips leave the result exactly as before; label points on
all seven drawings are unchanged and the search is 4-35x faster.
- ShapeBuilder.GetShapes rescanned every remaining entity at each
chaining step. An endpoint grid now finds the candidates, tested in
list order with the same checks, so chosen entities, reversals and
shapes are identical (compared against the old loop on the library
drawings and 300 random entity soups). 90-6138: 700 ms -> 40 ms.
- The label builds its shapes once: ShapeProfile.FromShapes reuses the
ones it already chained instead of chaining them again.
Previews of all seven now take 57-166 ms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A drawing of two equal, separate outlines (RC-204441A23 PT13) froze the
drawing library preview. ShapeProfile takes one outline as the perimeter
and the other as a cutout, so the net area is rounding noise and
ThicknessPrecision fell to 1e-9. A rectangle's pole is a whole ridge, so
the label search refined it into billions of cells on the UI thread.
Net area negligible next to the outline now falls back to the outline's
own width, and the paint-time label search has a cell budget so no shape
can stall painting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
The plate view searched for each part's label point with PolyLabel's fixed
0.5" precision, a third of a 1.5" part, so the search stopped on the first
interior cell and put labels near edges instead of in the thickest spot.
Precision is now 5% of the part's mean material width (2 x net area over
total outline length), so small, slender or heavily cut-out parts refine
further while large solid ones stay cheap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
PolyLabel kept its candidate cells in a sorted List, which costs
O(n^2) through Insert and RemoveAt(0). A 48 x 0.002 sliver searched
to 1e-4 took about 27 s.
Use a PriorityQueue keyed by (MaxDist, insertion order). Equal bounds
still pop newest first, and initial cells in sorted order, so every
existing caller gets bit-identical poles. A frozen copy of the
sorted-list implementation checks this on 1800 random shapes and on
symmetric shapes with tied poles. Swapping the tie order fails 25 of
the differential rows.
A new Find(outer, holes, precision, maxCells) overload stops after
about maxCells distance evaluations and returns the best cell so far.
When the starting grid would exceed a quarter of the budget, it is
coarsened so the whole shape is sampled first. The unbounded overload
behaves as before.
Repo-wide sweep with the pinned CSharpier 1.3.0 tool. Whitespace and
line-wrapping only; OpenNest.Engine.Tests (109) and OpenNest.IO.Tests
pass after reformat, full solution builds 0 errors.
Added .csharpierignore so csproj/config XML keeps its existing layout
(CSharpier's XML wrapping churns attributes with zero benefit).
Formatting is now enforceable: dotnet csharpier check . passes.