Skip to content

SLM synthesis: 2D packing

Final math derivation

Typeset derivations: EQUATIONS.md (renders on GitHub and the docs site). Headline: exact feasible region and overlap-free utilization:

\[F(B) = \widehat{\mathrm{IFP}}(S,B) \setminus \bigcup_{k,j} \mathrm{NFP}(A_k \cup H_j,\ B),\qquad U = \frac{\operatorname{area}(\bigcup_k A_k)}{\operatorname{area}(S)}\]

SETUP AND GROUNDED NOTATION. Sheet outer \(S\) (polygon, may be concave/holed), holes \(H_j\), placed parts \(A_k\). Part \(B\) normalized: rotate by \(\theta\) then translate bbox-min to origin (RotateNormalized in IrregularSheetFillNfpBlf.cs:427, RotatePoly in V506.cs:1261). Placement \(p=(o_x,o_y)\): \(B@p = \{v+p\}\). Spacing \(\sigma\) (V506 clamps max(0.1,in) at :132; NfpBlf clamps max(0,in) at :59).

CORE GEOMETRY (already implemented, re-stated to anchor the plan). \(\mathrm{NFP}(A,B) = A \oplus (-B)\); \((B@p) \cap \operatorname{int}(A) \neq \emptyset \iff p \in \operatorname{int}\,\mathrm{NFP}(A,B)\). In IrregularSheetFillNfpBlf this is Clipper2Adapter.MinkowskiSum(pp.AbsScaled, rot.Refl) at :184, with rot.Refl \(= (-x,-y)\) at :450 so the sum is exactly \(A \oplus (-B)\). Clipper MinkowskiSum handles concave-concave inputs exactly (Clipper2Adapter.cs:154-165, self-union under NonZero). \(\mathrm{IFP}\) (containment locus) \(= \{\,p : B@p \subseteq S\,\} = \bigcap_{b \in B} (S - b)\). The shipped ComputeIfp (IrregularSheetFillNfpBlf.cs:225-238) intersects over rot.HullScaled only.

CORRECTED IFP DIRECTION (verifier fix, load-bearing). For CONVEX \(S\), \(S-b\) is a translate of a fixed convex set and the intersection over \(b \in B\) equals the intersection over the extreme points of \(\operatorname{conv}(B)\); reflex (interior) vertices of \(B\) do not bind, so IFP over \(\operatorname{hull}(B)\) = IFP over \(\operatorname{vert}(B)\) = the EXACT IFP. Therefore on a convex sheet the shipped hull-IFP is ALREADY exact, and adding all part vertices changes nothing. For CONCAVE \(S\), \(S-b\) is non-convex and reflex \(b\) DO bind; intersecting over \(\operatorname{hull}(B)\) (or even all \(\operatorname{vert}(B)\)) is an OVER-approximation, not a safe subset: it admits some \(p\) where \(B\) pokes through a concave bay of \(S\). The true erosion is \(S \ominus B = S \setminus (\mathrm{complement}(S) \oplus (-B))\), i.e. subtract from a bounding frame the Minkowski sum of the sheet complement ring with \((-B)\); Clipper2Adapter can do this with MinkowskiSum + DifferenceLoops. CONSEQUENCE: the all-vertex-intersection "exact concave erosion" claim from the candidate proposals is FALSE and is dropped. The W2 benchmark sheet is RectCurve (Validator.cs:773), i.e. convex, so any IFP lever has ZERO effect on W2. Part-part non-overlap is independent of the IFP and is preserved by the NFP difference regardless.

FEASIBLE REGION (hard non-overlap, by construction). \(F(B) = \mathrm{erode}_\sigma(\mathrm{IFP}(S,B)) \setminus \mathrm{inflate}_\sigma(\bigcup_k \mathrm{NFP}(A_k,B) \cup \bigcup_j \mathrm{NFP}(H_j,B))\) (IrregularSheetFillNfpBlf.cs:162-199). Any \(p \in F(B)\) gives \(B@p \subseteq S\), no penetration of any \(A_k\), no entry into any \(H_j\), \(\geq \sigma\) clearance. Overlap is a hard constraint, not a penalty. The bottom-left minimizer \(p^* = \operatorname{argmin}_{p \in F(B)} (y,x)\) is attained at a vertex of \(F(B)\) (linear objective over a polygon), so the candidate set is exactly \(\operatorname{vert}(F(B))\) (BottomLeftVertex at :249-256).

WHY THE BENCHMARK NUMBER IS NOT WHAT IT SEEMS (the decisive grounding correction). The benchmarked 65.2% engine is NfpBottomLeftFillRhino (Validator.cs Bench2D row at :837-839), NOT the Clipper2 IrregularSheetFillNfpBlf (which has NO packbench row). NfpBottomLeftFillRhino packs into a RECTANGLE (GetInnerFitRegionCurve :374-394), exact NFP via NfpCache, first-fit break (:205), and reports strip Utilization = area/(usedLength*sheetWidth) (:942-953). The packbench headline metric is cov = UnionArea2D(placed)/fullSheet (Validator.cs:792-793). On the 24-part fixture the sheet is sized fullSheet = partsArea/0.60 (:771). For ANY pack that places all 24 fixed-area parts with zero overlap, union = partsArea, hence cov = 0.60 EXACTLY, independent of layout. V2/V3/V506 all read 60.0% for this reason. NfpBottomLeftFillRhino reads 65.2% at 24/24 and 0 overlap; that is mathematically only possible if its emitted union area exceeds partsArea, which it does because it is called with simplifyCurves=true (:838) and the curve simplification/polyline reduction (SimplifyPartCurve :576-605, ReducePolyline :627) changes emitted areas, inflating UnionArea2D. So 65.2% on cov is partly a measurement artifact and partly the strip-vs-full-sheet difference, not a genuine higher packing density on this saturated convex fixture. Targeting "exceed 65.2% on cov at 24/24" as written is unreachable and the wrong objective.

CORRECT TARGETS THAT CAN MOVE. (1) The strip metric Utilization = area/(usedLength*width) is NOT rigid-motion invariant: compacting the layout leftward reduces usedLength = max bbox.X (:937-940), raising Utilization. This is the metric to beat for NfpBottomLeftFillRhino. (2) cov = union/fullSheet moves only when placedCount rises, i.e. on an OVERSUBSCRIBED fixture (\(n \gg\) capacity) where reinsertion converts unplaced parts to placed. (3) density = union/used-bbox (covUsed, already emitted at Validator.cs:606,628) moves under compaction even at fixed placedCount. (4) On a CONCAVE or HOLED sheet fixture the IFP/NFP feature path can demonstrate exact-hole containment and concave-bay density that V506's vertex/centroid-in-hole sampling (ContainedInSheet :1616-1632) can miss.

ORDER MULTI-START (free, monotone, already built). NfpBottomLeftFillRhino ALREADY contains a deterministic order multi-start (BuildOptimizationSequences :671-712, optimizationMode 1=add Area/MaxDim/Width/Height/User orders, 2=add reversals, keep argmax by IsBetterResult :741-748) but packbench calls it with optimizationMode=0 (:839). \(U_{\mathrm{best}}\) = max over a fixed order family \(\geq U(\text{area-desc})\) by definition, so enabling it is monotone-safe and deterministic, and it directly lowers usedLength (raises strip Utilization). This is PRISMA Rank 3 (beam/order search) realized with zero new code.

COMPACTION (monotone, deterministic, PRISMA Rank 4). Define \(\mathrm{Redrop}_i\): hold all parts but \(i\) fixed (obstacle set \(Q_{-i}\)); recompute \(F(P_i^\theta \mid Q_{-i})\) over \(\theta \in \Theta\); move \(i\) to the vertex minimizing \(\phi\) only if \(\phi\) strictly drops by \(\geq \varepsilon_{\mathrm{rel}}\). Potential \(\Phi = \sum_i \phi(p_i)\), \(\phi \in \{\mathrm{BL}=(y,x),\ \mathrm{LGC}=c_Y(\theta)+y\}\) (both linear over \(F\) so the min is a vertex). Each accepted Redrop keeps 0-overlap (new \(p \in F\) by construction) and strictly lowers \(\Phi\) by \(\geq \varepsilon_{\mathrm{rel}}\); \(\Phi\) bounded below; so the sweep terminates in \(\leq (\Phi_0 - \Phi_{\min})/\varepsilon_{\mathrm{rel}}\) accepted moves, capped by MaxCompactionPasses. This is the discrete NFP-membership analogue of Li-Milenkovic LP compaction (PRISMA Rank4, translational-only, local optimum), needing NO LP solver. HONEST INVARIANCE LEMMA: on an already-saturated sheet (24/24) with fixed-area parts, union area is rigid-motion invariant, so compaction ALONE does not raise cov; it lowers usedLength (raises strip Utilization and covUsed) and frees contiguous space that reinsertion can fill. cov rises only via reinsertion on oversubscribed instances.

LGC SCORING (PRISMA Rank 5, cheap). \(\phi_{\mathrm{LGC}}(p) = c_Y(\theta) + p_y\) with \(c_Y\) the y-centroid of \(P_i^\theta\) relative to its reference point (constant per rotation), still linear in \(p\), min at a vertex. Flatter boundary than pure BL; ships behind a flag, BL stays default = byte-identical.

EPS/SCALE HYGIENE (T7 + T1). Replace exact == in BottomLeftVertex (IrregularSheetFillNfpBlf.cs:254 uses y==by && x<bx) with eps-aware compare eps2 = _tol*Scale; tie-break by smaller \(\theta\) then sourceIndex. Optionally recenter the sheet to its bbox-min before the x1000 Scale (PrepareSheet path) and fold the offset into WorkToSheet so far-from-origin sheets keep sub-mm fidelity; no behavior change for origin-centred sheets.

Ranked evolutions

[1] Establish the REAL baseline: add IrregularSheetFillNfpBlf to --packbench and add metrics that can move

Expected gain: Truth-criterion (c) prerequisite. Produces the only honest baseline for every later claim. Expected: Clipper2 NfpBlf cov ~60.0% on W2 (saturated \(\Rightarrow\) invariant), real differentiation appears only on the new fixtures below.

Math: cov=union/fullSheet is invariant at 0.60 on the saturated 24/24 fixed-area fixture (Validator.cs:792-793, :771); strip Utilization=area/(usedLength*width) (:942-953) and covUsed=union/used-bbox (:606) are NOT invariant. The Clipper2 engine has no bench row today, so 'strictly dominates the 65.2% leader' is untestable. The 65.2% (NfpBottomLeftFillRhino, simplifyCurves=true :838) is partly a simplify area-inflation artifact, not a density win.

Implementation: In Validator.cs RunPackBench Bench2D block (:825-842) add a row: new IrregularSheetFillNfpBlf(sheetW-as-outline... actually pass the rect Curve outline + empty holes, spacing 0, rots, 0.01, AreaDescending, 0).Pack(parts). Emit covUsed and a strip-Utilization column alongside cov so the comparison is apples-to-apples; re-run NfpBottomLeftFillRhino once with simplifyCurves=false to quantify the artifact. No new dependency.

[2] Add an OVERSUBSCRIBED concave/holed fixture where cov and placedCount can actually rise

Expected gain: Creates the regime where the EVOLUTION.md 51-67% waste-cut figures (Python reference, NOT yet harness-measured) can be validated or refuted in C#. No density number is asserted until this runs.

Math: cov rises iff placedCount rises. On \(n \gg\) capacity (e.g. 60/120/200 parts on the same sheet) greedy leaves many unplaced; reinsertion converts them, so cov is a live signal. A concave sheet + a hole exercises the IFP containment path and the exact-hole NFP obstacle, which the W2 convex rectangle cannot.

Implementation: In Validator.cs add MakeOversubscribed2DParts (reuse the LCG generator at :1061-1072, scale count to 120) and a concave L-shaped sheet outline + a central hole. Bench all 2D engines on it. Report placed/cov/covUsed/overlap/det/time.

[3] Turn ON the existing order multi-start in NfpBottomLeftFillRhino (zero new code, monotone, PRISMA Rank3)

Expected gain: Measurable lift on strip Utilization and covUsed at 0 overlap, 0 new dependencies. Bounded extra runtime (~6x sequences, each a full greedy pass). Provable >= current.

Math: BuildOptimizationSequences (:671-712) already enumerates Area/MaxDim/Width/Height/User + reversals and keeps argmax by IsBetterResult (:741-748). \(U_{\mathrm{best}}\) = max over a fixed family \(\geq U(\text{area-desc})\), deterministic. usedLength is non-increasing \(\Rightarrow\) strip Utilization non-decreasing.

Implementation: In the packbench NfpBottomLeftFillRhino call (Validator.cs:837-839) pass optimizationMode=2, optimizationIterations=0 instead of 0,0. Expose the same in the NFP GH component if not already. This is the cheapest density lever and it already exists.

[4] Gravitational compaction sweep on IrregularSheetFillNfpBlf (monotone, deterministic, PRISMA Rank4)

Expected gain: On saturated convex W2: ZERO cov change (invariance lemma), measurable covUsed/strip-Utilization gain. Real cov gain only when paired with reinsertion on the oversubscribed fixture. Must be measured, not asserted.

Math: \(\mathrm{Redrop}_i\) moves part \(i\) to \(\operatorname{argmin}_\phi\) over \(F(P_i \mid Q_{-i})\); accept only if \(\phi\) drops \(\geq \varepsilon_{\mathrm{rel}}\). \(\Phi = \sum \phi\) non-increasing, bounded below \(\Rightarrow\) terminates. 0-overlap preserved (new \(p \in F\) by construction). Lowers usedLength/covUsed; frees space for reinsertion. Discrete Li-Milenkovic, no LP solver.

Implementation: In IrregularSheetFillNfpBlf.cs after the greedy loop (:142) add CompactAndReinsert: RedropPart reuses the FindBestPlacement body (:155-223) with an explicit obstacle list = placed minus target; generalize BottomLeftVertex to ArgMinVertex(loops,phi). Add ctor params PlacementScore _score=BottomLeft, bool _enableCompaction=false (default off=byte-identical), int _maxCompactionPasses=4. Emit final from post-compaction set.

[5] Guarded reinsertion sweep (the only lever that raises cov at fixed sheet)

Expected gain: This is where the EVOLUTION.md large gains live (Python: ~0.68-0.71 flat-in-N vs greedy collapse). Validate on the oversubscribed fixture via --packbench. Expected-not-measured until run.

Math: For each unplaced \(u\), try place in freed space; else 1-1 evict a smaller placed \(v\), place \(u\), redrop \(v\); accept iff placedCount rises OR (equal AND \(\Phi\) drops). Monotone in placedCount and \(\Phi\), 0-overlap preserved, deterministic (ties by sourceIndex).

Implementation: In IrregularSheetFillNfpBlf.cs add ReinsertionSweep gated by _enableReinsertion (auto-skip when 0 unplaced) and _maxOuterRounds=2; loop Phase1<->Phase2 bounded. Only fires on oversubscribed instances.

[6] Exact-hole + concave-bay feature path fed into V506 modes (net-new value the standalone sibling lacks)

Expected gain: Exact hole honoring (correctness) + concave-sheet containment safety. Density delta only after the true erosion is implemented and measured on the concave/holed fixture. Labeled expected-not-measured until then.

Math: Holes as \(\mathrm{NFP}(H_j,B)\) obstacles (IrregularSheetFillNfpBlf.cs:174-178) honor a hole EXACTLY (Minkowski) vs V506's vertex/centroid-in-hole sampling (ContainedInSheet :1616-1632) which can miss a thin part straddling a hole edge. On a concave SHEET, implement the TRUE complement-Minkowski erosion (\(S \setminus (\mathrm{complement}(S) \oplus (-B))\)) instead of the over-admitting hull/vertex intersection; rely on ContainedInSheet as the safety net for any surplus.

Implementation: Keep V506 feature modes (boundary 0-3, trim, auto-nested holes) untouched in structure. Wire a quality path that routes V506's plain geometric mode (currently delegates to V2 at IrregularSheetFillV506.cs:278-284) to IrregularSheetFillNfpBlf when a Quality flag is on, BYPASSING the V2 delegation gate. Build a thin-part-straddling-a-hole test case proving V506 sampling misses it and the NFP-hole catches it, measured headless.

[7] Determinism + numeric hygiene (T7, T1) across both engines

Expected gain: Reproducible fixed point across mm/m models; required for the det column in --packbench and the no-regression test.

Math: Linear objective tie-break must be eps-aware or the bottom-left vertex pick over \(F(B)\) drifts at coincident coordinates. eps2=_tol*Scale.

Implementation: IrregularSheetFillNfpBlf.cs:254 replace exact == compare with eps2-aware (y<by-eps2 || (|y-by|<=eps2 && x<bx-eps2)); MultiStart/compaction ties by (\(\phi\), \(\theta\), sourceIndex). Optionally recenter sheet to bbox-min before x1000 Scale and fold into WorkToSheet. Add a same-seed-twice determinism assertion in the harness.

Implementation plan

  1. STOP/HITL: this plan touches 5+ files (Validator.cs, IrregularSheetFillNfpBlf.cs, Packing2DModels.cs, a new *Tests.cs, optionally IrregularSheetFill.cs + the V506 component). Per AGENTS.md sec6 get explicit approval before committing, and do NOT change/reuse any GH GUID (V506 GUID D5E7A2B1-... at Pack2DIrregularSheetV506Component.cs:39).
  2. Step 1 (measure first, truth criterion c): in tools/Frahan.StonePack.Harness/Validator.cs RunPackBench, add a Bench2D row for IrregularSheetFillNfpBlf (pass the rect Curve outline + empty holes list, spacing 0, rots, 0.01, AreaDescending, seed 0). Add a covUsed column and a strip-Utilization column to the emitted table. Re-run NfpBottomLeftFillRhino once with simplifyCurves=false to quantify the 65.2% simplify artifact. Build + run --packbench. Record the real Clipper2 baseline.
  3. Step 2: add an oversubscribed fixture (reuse the LCG generator at Validator.cs:1061-1072 scaled to ~120 parts) and a concave L-shaped sheet + central hole. Bench all 2D engines on it. This is the only fixture where cov can move.
  4. Step 3 (free win): change the packbench NfpBottomLeftFillRhino call (Validator.cs:837-839) to optimizationMode=2. Re-run; record strip-Utilization and covUsed deltas. Confirm 0 overlap, deterministic.
  5. Step 4: in IrregularSheetFillNfpBlf.cs add ctor params (PlacementScore _score=BottomLeft default, bool _enableCompaction=false, int _maxCompactionPasses=4, bool _enableReinsertion=false, int _maxOuterRounds=2, double _epsRel) all defaulting to byte-identical-to-today. Add ArgMinVertex(loops, phi), RedropPart (reusing the FindBestPlacement body :155-223 with an explicit obstacle list), CompactionSweep, ReinsertionSweep, and call CompactAndReinsert after the greedy loop (:142). Move EmitPlacement to after compaction.
  6. Step 5 (T7/T1 hygiene): replace the exact == tie-break at IrregularSheetFillNfpBlf.cs:254 with an eps2=_tol*Scale aware compare; add (theta, sourceIndex) secondary tie-breaks. Optionally recenter the sheet before the x1000 Scale and fold into WorkToSheet.
  7. Step 6: add PlacementScore enum {BottomLeft, LowestGravityCenter} to Packing2DModels.cs near PackingSortMode; optionally add CompactionMoves/ReinsertionGains int counters to PackingResult.
  8. Step 7: wire the quality path. In IrregularSheetFillV506.cs, gate the plain-mode V2 delegation (:278-284) on a new _qualityNfp flag so that when quality is on it routes to IrregularSheetFillNfpBlf with compaction/reinsertion ON instead of V2; default false preserves current behavior. Expose _qualityNfp on the V506 component via AppendAdditionalComponentMenuItems (NOT a new input/GUID). Thread it through IrregularSheetFill.ForV506/ForVariant.
  9. Step 8 (headless tests, tests/Frahan.StonePack.Tests): (a) determinism same-seed-twice identical placed+U within eps; (b) 0-overlap exact post-hoc IntersectionArea2D<1e-4 after compaction+reinsertion on the concave/holed fixture; (c) no-regression: compaction=off,reinsertion=off byte-identical to today; (d) monotonicity: covUsed_compacted >= covUsed_initial; (e) exact-hole: a thin-part-straddling-a-hole case the NFP-hole catches and V506 sampling misses.
  10. Step 9: run RUN_TESTS=1 dotnet run for the test gate, run --packbench, capture the numbers, then STOP for Libish visual validation in Rhino per truth criterion (c) before any wiki promotion. Do not claim >baseline until the harness confirms it on a metric that is not invariant.

Benchmark targets

  • NfpBottomLeftFillRhino (the actual 65.2% bench engine, Validator.cs:837): cov(union/fullSheet)=65.2% at 24/24, 0 overlap, optimizationMode=0, simplifyCurves=true (partly a simplify area-inflation artifact); strip Utilization not currently reported by --packbench -> Re-measure with simplifyCurves=false and optimizationMode=2; target a measurable rise in strip Utilization=area/(usedLength*width) and covUsed=union/used-bbox at 0 overlap (cov stays ~60% on the saturated convex fixture by the invariance lemma)
  • IrregularSheetFillNfpBlf (Clipper2 exact NFP-BLF, currently NOT benchmarked): no --packbench row exists; baseline unknown -> Add a --packbench row; establish baseline (expected cov ~60.0% on the saturated W2 fixture); then with _enableCompaction+_enableReinsertion ON on the NEW oversubscribed concave/holed fixture, target higher placedCount and cov than V506-plain and than its own greedy Phase-0, all at 0 overlap, deterministic
  • IrregularSheetFillV506 (feature-rich facade): plain mode delegates to V2 => cov 60.0% at 24/24, 0 overlap (Validator.cs:834, IrregularSheetFillV506.cs:278-284) -> Quality flag routes plain mode to the compacted IrregularSheetFillNfpBlf, bypassing the V2 delegation gate; target >= V506-plain covUsed on the concave/holed fixture and exact hole honoring (no thin-part-straddles-hole misses), measured headless then visually validated

Code change map

  • tools/Frahan.StonePack.Harness/Validator.cs: Add a Bench2D row for IrregularSheetFillNfpBlf (RunPackBench :825-842). Add covUsed + strip-Utilization columns to the emitted table (:756-763). Re-run NfpBottomLeftFillRhino with simplifyCurves=false to quantify the 65.2% artifact (:837-839), and flip its optimizationMode 0->2. Add MakeOversubscribed2DParts + a concave L sheet + central hole fixture (reuse :1061-1072) and bench all 2D engines on it. No new dependency.
  • src/Frahan.StonePack.GH/TwoD/IrregularSheetFillNfpBlf.cs: Add ctor params PlacementScore _score=BottomLeft, bool _enableCompaction=false, int _maxCompactionPasses=4, bool _enableReinsertion=false, int _maxOuterRounds=2, double _epsRel (all default byte-identical). Add ArgMinVertex(loops,phi), RedropPart (reuse FindBestPlacement body :155-223 with explicit obstacle list), CompactionSweep, ReinsertionSweep, CompactAndReinsert called after :142; move EmitPlacement to post-compaction. Fix BottomLeftVertex :254 exact == -> eps2=_tol*Scale aware compare with (theta,sourceIndex) tie-break. If pursuing concave-sheet IFP: implement true erosion S \ (complement(S) (+) -B) via MinkowskiSum+DifferenceLoops, keep hull-vertex ComputeIfp (:225-238) only as the convex fast path; drop the all-vertex 'exact concave' claim.
  • src/Frahan.StonePack.GH/TwoD/Packing2DModels.cs: Add enum PlacementScore {BottomLeft, LowestGravityCenter} near PackingSortMode. Optionally add int CompactionMoves, ReinsertionGains counters to PackingResult (parallels FeasibleRegionCount). Do not change existing PackedCurves/Transforms collections.
  • src/Frahan.StonePack.GH/TwoD/IrregularSheetFillV506.cs: Add private readonly bool _qualityNfp ctor field (default false). Gate the plain-mode V2 delegation (:278-284) on !_qualityNfp; when _qualityNfp is on, route plain geometric mode to new IrregularSheetFillNfpBlf(...) with compaction+reinsertion ON instead of V2. Leave boundary modes 0-3, trim, auto-nested holes, ContainedInSheet, CollidesWithGrid, GenerateCandidates UNTOUCHED.
  • src/Frahan.StonePack.GH/TwoD/IrregularSheetFill.cs: Thread the new _qualityNfp arg through ForV506 (:61-87) and ForVariant (:192-231); default false preserves current dispatch. Optionally add a SolverVariant.NfpBlf + ForNfpBlf factory so the dispatcher can route the density path directly.
  • src/Frahan.StonePack.GH/Pack2DIrregularSheetV506Component.cs: Expose _qualityNfp via AppendAdditionalComponentMenuItems ('Quality (exact NFP)' toggle) flowing into both V506 constructions (:169, :224). Do NOT add a new input param and do NOT change/reuse the GUID (:39).
  • tests/Frahan.StonePack.Tests (new *Tests.cs): Headless tests: (a) determinism same-seed-twice; (b) 0-overlap after compaction+reinsertion on concave/holed fixture (IntersectionArea2D<1e-4); (c) no-regression: compaction/reinsertion off byte-identical to today; (d) monotonicity covUsed_compacted>=covUsed_initial; (e) exact-hole thin-part-straddling case NFP catches and V506 sampling misses.

Open risks

  • BENCHMARK ATTRIBUTION (now resolved, must persist): the 65.2% is NfpBottomLeftFillRhino with simplifyCurves=true, a rectangular-sheet strip engine, NOT the Clipper2 IrregularSheetFillNfpBlf the proposals describe. Any doc that says 'beat the 65.2% NFP-BLF leader on cov' is wrong; cov is invariant at 60% on the saturated 24/24 convex fixture. This correction must enter the wiki or it will propagate.
  • METRIC INVARIANCE: cov=union/fullSheet cannot rise on a saturated fixed-area pack. All real 2D gains must be reported on strip Utilization, covUsed(=union/used-bbox), or placedCount on an oversubscribed fixture. No headline density number should be asserted before --packbench confirms it on a non-invariant metric (truth criterion c).
  • IFP CLAIM DROPPED: all-vertex IFP intersection is NOT exact concave erosion; it equals hull erosion and over-admits on concave SHEETS. The W2 sheet is convex (Validator.cs:773) so the IFP lever does nothing on W2. True concave erosion needs complement-Minkowski (S \ (complement(S) (+) -B)); pursue only if a concave-sheet fixture justifies it, and rely on ContainedInSheet as the safety net.
  • SPACING FLOOR: NfpBlf clamps spacing to max(0,in) (:59) while V506 clamps max(0.1,in) (:132). Do not change either floor inside this evolution; a spacing-floor change is a separate behavior change requiring its own test and HITL.
  • SCOPE/HITL: the change set touches 6+ files (>5) and the V506 GH component; per AGENTS.md sec6 this needs explicit commit approval and the GUID must not change. Visual validation in Rhino by Libish is required before any wiki promotion.
  • 3D NOT COVERED HERE: the task header mentions exceeding the 3D best per feature class, but all three VERIFIED proposals supplied are 2D and there is no verified 3D proposal to consolidate. The W2 3D numbers (TreePackForest 37.2% guillotine-separable, BestFitInventory 65.2%, Ashlar 60.8%, Pack3D V2-skyline 30/30 @23.7%) are measured but a 3D evolution would require its own SLM pass; flagging rather than fabricating one.
  • EVOLUTION.md NUMBERS ARE PYTHON, NOT HARNESS: the 51-67% waste-cut and 0.67-0.71 flat-in-N figures are from the Python reference study, not the C# harness. They must be re-measured in C# via --packbench before being claimed as engine results.
  • sparrow ESICUP densities (SWIM 78.26, TROUSERS 91.73, MARQUES 90.93) are bounding-box density on different instances and require a Rust guided-local-search graft (PRISMA Rank1, blocked on FFI-vs-reimplement). Do not present the proposed net48 levers as narrowing the gap to sparrow without the GLS loop.