Archived
Nests every part as the axis-aligned box of its material at its minimum-area rotation and packs boxes with a maximal-rectangles free list (6 placement rules x global/ordered pick). Sheet choice uses the salvage-credited look-ahead cost. Curved extremes read the way the layout check sees them (circumscribed arcs on the Clipper grid), so discs, rings and obrounds stay valid at box contact; an 80-job sweep failed 16-50 jobs before that rule. Local 2026 production lanes (--min-salvage-dimension 12, --parallel 3): - 91 all-rectangular jobs: 91/91 valid, cost 333081 vs best general 332153 (Gpt6Astra, 89/91 valid), 5.9 s vs 13.7 s (Opus55) and 169.5 s (Gpt6Astra) - 77 box-filling (>= 90%) jobs: 77/77 valid, lowest total cost 641637
75 lines
4.2 KiB
Markdown
75 lines
4.2 KiB
Markdown
# OpenNest.Engine.Rectangles
|
||
|
||
A whole-job `INestingEngine` for the **rectangle lane**: jobs whose parts are plain or
|
||
near-rectangular plates. Every part is nested as the axis-aligned box of its material at its
|
||
minimum-area rotation(s), packed with a **maximal-rectangles** free list
|
||
(Jylänki, *A Thousand Ways to Pack the Bin*, 2010).
|
||
|
||
For these parts the box wastes almost nothing, and exact box packing is both denser and far
|
||
faster than contour-sliding engines. Irregular parts are still placed validly, but only as their
|
||
bounding boxes: nothing is nested into another part's notch or hole. Use a general engine
|
||
(Opus55, Gpt6Astra) for irregular work.
|
||
|
||
## Algorithm
|
||
|
||
**1. Boxes (`BoxCatalog`)**
|
||
- Rotations come from the host's `RotationCandidates.ForShape` (the policy angles plus, for
|
||
`Automatic`, the minimum-bounding-rectangle alignment). Only the rotations whose box area is
|
||
within 1e-6 of the minimum are kept, with duplicate box shapes removed. A 10 x 4 plate drawn at
|
||
30° is squared up; a fixed-rotation part keeps its one angle.
|
||
- Bounds use material contours only (rapids and scribe/etch excluded), like the layout check.
|
||
- **Curved extremes:** the layout check circumscribes arcs and snaps to a 1e-4 grid, so a disc
|
||
or obround reads slightly larger than its true arc. Each box side takes the check's own
|
||
outline (`ClipperBridge.OffsetForValidation`) plus one grid unit wherever that outline sticks
|
||
out by more than a quarter of `NestTolerances.SpacingSlack`. Straight-edged parts are unchanged
|
||
and still pack at exactly the part spacing.
|
||
|
||
**2. One sheet (`MaxRectsSheet`, `SheetPacker`)**
|
||
- Boxes and the work area are grown by the part spacing on their right/top sides, so touching
|
||
grown boxes are exactly one spacing apart and the last box can touch the work-area edge.
|
||
- The free list keeps every maximal empty rectangle; placing a box splits each free rectangle it
|
||
hits and prunes the contained ones.
|
||
- Six placement rules: best short side, best long side, best area, bottom-left, left-bottom and
|
||
contact point. Two pick modes: *global* (each step places whichever remaining box scores best
|
||
anywhere) and *ordered* (priority, then largest box first, each type filled until it stops
|
||
fitting). Lower priority numbers are always served first.
|
||
|
||
**3. Whole job (`RectanglesNestingEngine`)**
|
||
- Sheet by sheet, every available stock is packed under all 12 rule/mode pairs. The candidate with
|
||
the lowest estimated whole-job cost wins: its salvage-credited `NestJobCost.NetSheetArea`, plus
|
||
the remaining demand priced at the best net-area-per-part-area ratio any candidate reached. A
|
||
candidate that leaves more of the most urgent priority tier unplaced never wins.
|
||
- Deterministic: no clocks or randomness; the only stop besides completion is the host token.
|
||
|
||
## Results
|
||
|
||
`OpenNest.Benchmark`, `--min-salvage-dimension 12`, each file's own sheet sizes, `--parallel 3`
|
||
(times are under that load). Synthetic results only here; the real-part corpus stays local.
|
||
|
||
Local corpus of converted 2026 production nests (file names and parts stay out of this repo):
|
||
|
||
| Lane | Jobs | Engine | Valid | Total cost | Time |
|
||
|---|---|---|---|---|---|
|
||
| Every part plain or near-rectangular | 91 | **Rectangles** | 91 | 333,081 | 5.9 s |
|
||
| | | Gpt6Astra | 89 | 332,153 | 169.5 s |
|
||
| | | Opus55 | 91 | 332,925 | 13.7 s |
|
||
| | | StockLadder | 89 | 337,641 | 36.0 s |
|
||
| Every part fills ≥ 90% of its box | 77 | **Rectangles** | 77 | 641,637 | 19.1 s |
|
||
| | | Gpt6Astra | 76 | 651,514 | 195.6 s |
|
||
| | | StockLadder | 76 | 671,590 | 93.4 s |
|
||
| | | Opus55 | 76 | 693,972 | 31.8 s |
|
||
|
||
Rectangles never leaves a part unplaced that another engine placed. Its losses are a handful of
|
||
multi-sheet jobs where the sheet-by-sheet greedy choice commits to a worse sheet mix (see Next).
|
||
|
||
`tests/` (27 tests): contract, starter and engine tests, including an 80-job sweep of discs,
|
||
rings, obrounds and triangles at four spacings that failed 16–50 jobs before the curved-extreme
|
||
rule.
|
||
|
||
## Next
|
||
|
||
- Multi-sheet jobs: the sheet-by-sheet greedy choice loses to whole-plan search on a few jobs.
|
||
Re-plan the last sheets and try whole-job variants, as Opus55 does.
|
||
- Guillotine-only mode for shears and for cut-off-friendly layouts.
|
||
- Exhaustive search for small jobs (one or two part types).
|