This repository has been archived on 2026-09-29. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OpenNest-Engines/OpenNest.Engine.Rectangles/README.md
T
aj 13478f5102 feat(rectangles): add rectangle-lane maximal-rectangles engine
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
2026-09-29 20:49:00 -04:00

75 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).