mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-17 13:51:40 -04:00
Plates the engine built from stock (DrawingJobMapper.CreatePlate, used by the public result materializer, the layout check and the placement fillers) stored placement-frame zones, while live plates store them relative to the sheet's lower-left corner. The same Plate.ClampZones therefore meant two frames: the new clamp cut checks double-translated materialized plates in quadrants 2-4, and interactive fill and push on live plates read sheet-frame zones as placement coordinates, so they could put parts on clamps there. Plates built from stock now keep the stock's input zones (sheet frame, no lossy translate-and-back), and the layout check and interactive clamp obstacles map through ClampZoneFrame.ToPlacement, which reproduces the stock's placement zones bit for bit, so engine placements and validation are unchanged. Project Memory: opennest 14f93b26-2e41-41f1-b09f-87dc895bb4da
241 lines
17 KiB
Markdown
241 lines
17 KiB
Markdown
# Nesting engines
|
|
|
|
Whole-job engines implement `INestingEngine.Solve(NestJob)` and are selected by name through
|
|
`NestingEngineRegistry`. Every automatic nesting front end validates engine output the same way;
|
|
see [automatic nesting and validation](automatic-nesting.md).
|
|
|
|
## Built-in engines
|
|
|
|
Engines are named for the jobs they suit, not for how or by whom they were built. Engine code lives
|
|
in `OpenNest.Engine/NestingEngines/<Name>/`, its tests in `OpenNest.Engine.Tests/NestingEngines/`.
|
|
|
|
| Engine | Best for | Method |
|
|
|---|---|---|
|
|
| Default | Any job; used when no engine is named | Runs Irregular, then Rectangles, checks both layouts with the layout check and keeps the best: valid first, then fewest unplaced parts, then lowest salvage-credited cost; ties keep Irregular |
|
|
| Rectangles | Plain and near-rectangular plates | Each part packed as the box of its material at its minimum-area rotation, using a maximal-rectangles free list; stock chosen sheet by sheet by salvage-credited look-ahead cost |
|
|
| Irregular | Irregular profiles | No-fit-polygon frontier packing with gap filling and best-fit pairs, six whole-job strategy variants and a tail re-plan |
|
|
| StockLadder | Caller-supplied stock ladders | Constrained-first fill with equivalent-demand area repacking |
|
|
| Fill, Strip, Vertical Remnant, Horizontal Remnant | Single-strategy fills | The fixed placement strategies behind interactive fill; Fill is the multi-phase lattice fill (linear, pairs, rectangle best-fit, remainder) |
|
|
|
|
Neither Irregular nor Rectangles wins every job, even within its own lane, so Default runs both
|
|
rather than choosing by part shape; Rectangles adds little time and is also the fallback when an
|
|
Irregular layout fails the check. A candidate that throws is skipped. Default routes the whole job:
|
|
engines cannot share a sheet, so a job mixing plain and irregular parts goes to both engines whole.
|
|
A future circle/ring engine joins Default as another candidate.
|
|
|
|
Rectangles places irregular parts validly, but only as their bounding boxes; it never nests into a
|
|
notch or hole. Box sides account for how the layout check flattens arcs, so round-edged parts stay
|
|
valid at box contact.
|
|
|
|
Interactive/full-area box packing uses the same 90% work-area slack allowance as Rectangles.
|
|
A free box may absorb a slightly oversized side only at its right/top edge when that edge
|
|
coincides with the plate work-area boundary. Internal leftover edges keep the strict packing
|
|
tolerance, and actual part dimensions still determine spacing away from the plate boundary.
|
|
|
|
Irregular keeps nominal orientation bounds for line-only outlines, so an allowed rotation can
|
|
fit exactly between the configured plate-edge gaps. These bounds use original rotated line
|
|
endpoints, retaining material extents even when polygon cleanup discards short-edge chains.
|
|
Curved outlines retain conservative flattening-error padding. This does not relax part-spacing
|
|
footprints, no-fit polygons or layout validation tolerances; a part extending beyond the accepted
|
|
work-area bounds remains invalid.
|
|
|
|
Irregular fills gaps and open notches using outer profiles; it does not yet place parts inside
|
|
enclosed cutouts. For a part with two or more copies it also offers its best-fit pairs (two copies
|
|
interlocked, as the Best Fit viewer shows them) alongside the single copies, and places a pair
|
|
where both members' free regions allow it. Each pair's internal spacing is re-checked with the
|
|
layout check before it is offered, and only rotations the part's policy allows are used. A pair
|
|
may introduce legal rotations beyond the sampled single poses; these remain eligible even when
|
|
none of the sampled singles fits the stock. Both members block space separately, leaving their
|
|
notches and intervening gaps available for later parts. Concave no-fit polygons are prepared
|
|
with a single boundary/containment union.
|
|
Any remaining numerical hole is filled only when its entire ring is certified to lie in forbidden
|
|
space, preserving genuine enclosed placement pockets without changing spacing tolerances.
|
|
|
|
When remaining demand exceeds two, Irregular also offers Fill patterns as optional
|
|
multi-member candidates, not as solid bounding boxes or a whole-job Fill fallback. It searches
|
|
the empty work area and physical leftover space for up to two high-area rectangles. Occupied
|
|
outlines are expanded by part spacing before rectangle search. Each sheet prepares blocks initially
|
|
and after its first placement, for up to four high-demand-area types; each type has at most eight
|
|
new Fill preparations per spacing per solve. Repeated rectangles reuse private drawing/candidate
|
|
caches. Quantity-one and quantity-two requests never run block Fill.
|
|
|
|
Block members are trimmed to remaining demand, mapped back to source-frame rotations, and checked
|
|
for legal rotations and internal material clearance before competing with singles and pairs.
|
|
Group-only rotations do not expand the single-part rotation choices. Placement intersects all
|
|
member free regions and subtracts each placed member separately, preserving usable gaps. A failed
|
|
or invalid Fill proposal leaves singles and pairs available. Large enclosed-pocket blocks remain
|
|
pending the hole-geometry integration; containment cutting order and shop-use safety acceptance
|
|
remain separate sequencer/verification work.
|
|
|
|
## Rectangular clamp zones
|
|
|
|
Whole-job Rectangles and Irregular exclude each stock's rectangular clamp zones,
|
|
expanded on all sides by its bottom edge spacing. Part spacing is not added to clamp
|
|
clearance. Default runs and validates both lanes as usual. Empty clamp collections
|
|
leave the existing packing path unchanged; clamps never become requested or placed parts.
|
|
|
|
Every `Plate`, including plates the engine builds from stock, stores clamp zones relative
|
|
to the sheet's lower-left corner; `NestPlateStock.ClampZones` carries them in the signed
|
|
placement frame of part coordinates. Code that compares a plate's zones with parts, cuts
|
|
or motions maps them through `ClampZoneFrame.ToPlacement`, which reproduces the stock's
|
|
zones exactly; reading `Plate.ClampZones` bounds directly is wrong in quadrants 2-4.
|
|
|
|
Irregular prepares static rectangle obstacles and their cached no-fit polygons once
|
|
per stock per solve, separately from the part-spacing cache. Every orientation's initial
|
|
free region excludes these obstacles, including extra orientations introduced by pairs
|
|
or Fill blocks. Blocks still size their proposals from work-area/placed-part rectangles;
|
|
member free regions determine whether the whole proposal can actually fit around clamps.
|
|
Open notches and material between clamps remain available. Inflated synthetic rectangles
|
|
are enclosed on the existing clipping grid before NFP preparation, retaining at least one
|
|
grid interval per axis so positive sub-grid clamps cannot disappear. Existing arc/grid
|
|
safety margins, covered-hole certification and cancellation apply without relaxed tolerances.
|
|
|
|
Known clamp edge cases remain in the earlier shared/Rectangles implementation:
|
|
large part spacing can make Rectangles overlap a clamp; rectangles below the clipping
|
|
grid can make layout validation (including Default's candidate scoring) throw. Irregular's
|
|
static-obstacle preparation does not repair these separate paths. The ordinary clamp
|
|
fixtures pass for both lanes and Default; this is not an all-input clamp-safety guarantee.
|
|
|
|
This is whole-job engine support, not interactive fill/push or cutoff/post-verification
|
|
support; those consumers are separate work. Desktop clamp editing and the stock-size
|
|
resolution of a nest's clamp rail are described in
|
|
[automatic nesting](automatic-nesting.md#machine-edge-clamps). Other engines and
|
|
plug-ins have not gained clamp-aware placement from this change.
|
|
|
|
## Repeating completed layouts
|
|
|
|
Rectangles and Irregular can skip repeated sheet searches after selecting one nonempty layout
|
|
whose normal sheet search has completed. The repeat count includes the first physical copy and
|
|
is limited by the scarcest requirement on that layout, remaining finite stock and the remaining
|
|
physical `MaxPlates` allowance. Parts absent from the layout keep their demand. A salvage-worthy
|
|
offcut does not prevent repetition: completion means the engine found no further supported
|
|
requested placement, not that no geometric packing exists.
|
|
|
|
Fast-forwarding applies only while exactly one stock entry remains available. Different stock
|
|
IDs count as alternatives even at identical dimensions. Irregular's forced-first-stock tail trial
|
|
still counts **all** remaining stock entries, not just that trial's restricted shortlist; it commits
|
|
the forced first sheet once when alternatives remain and resumes ordinary stock selection.
|
|
Candidates that cannot pass the existing candidate validator retain ordinary one-sheet allocation.
|
|
Cancellation never certifies a layout and is checked during physical-copy expansion.
|
|
|
|
Both engines continue automatically with residual demand in the same solve, including when a
|
|
layout's repeat count is one. There is no per-layout stop, confirmation or opt-in mode. Rectangles
|
|
retains both Global/Ordered packing modes and its priority guard. Irregular retains demand-dependent
|
|
singles, pairs and blocks, variant ordering and its existing work-budget semantics; saved work can
|
|
allow additional variants to run, so this is not a promise of historical pose equality.
|
|
|
|
Raw `NestJobResult` output remains one entry per physical sheet, with fresh placements and unique
|
|
instance indices, physical stock usage and physical progress counts. Irregular also retains each
|
|
physical copy in its working plan: its last-one-to-three-sheet improvement can return the removed
|
|
tail's finite stock, repack that demand, and replace just those copies while keeping the prefix.
|
|
Default inherits these fast paths by running the two engines; other engines and external plug-ins
|
|
keep their existing search behavior. Quantity-bearing presentation is a separate validated-pipeline
|
|
representation; repeating does not bypass whole-job validation or change cutting programs.
|
|
|
|
## Filling cutouts (not yet in production)
|
|
|
|
Placing parts inside another part's enclosed cutout is being built as a step that runs before any
|
|
engine, so every engine benefits. Only an internal test-only pipeline preview calls it: a part
|
|
inside a cutout must be cut before the cutout's contour, and the cutting/post safety gates are
|
|
not complete. Ordinary whole-job calls do not enable this path.
|
|
|
|
`CutoutLatticeFill` (`OpenNest.Engine/Jobs/Cutouts/`) fills one closed cutout with copies of one
|
|
part. It runs Fill over the cutout's bounds plus one part step on every side, then shifts
|
|
that lattice across a grid of offsets of up to half a step each way. At each offset it keeps the
|
|
copies whose spacing-grown outline lies inside the cutout, using the part's inner-fit region of
|
|
the inscribed, flattened cutout, and the offset keeping the most copies wins. Every returned pose
|
|
is then checked against the frame and the other copies with `NestLayoutCheck.Clears`, the test the
|
|
layout check uses. The method suits many small copies in a large cutout; a few large inserts are
|
|
meant for no-fit-polygon placement. Fill can return different, equally scored lattices on repeated
|
|
calls for some parts, so results are not yet guaranteed identical between runs.
|
|
|
|
The internal `CutoutRouter` can propose copies from the shifted lattice, then search bounded
|
|
inner-fit/NFP sample points for remaining copies and other insert requirements. It keeps
|
|
original requirement IDs, reindexes accepted copies and checks clearance against the frame
|
|
and every previously accepted insert. Lattice shifts account for occupied poses before
|
|
quantity trimming; a pre-fill work limit declines giant grids and lets bounded NFP sampling
|
|
try instead. A null NFP proposal is not proof of geometric impossibility. No material-area
|
|
ratio cutoff rejects a possible placement: the measured 0.10 and 0.35 ratios guide
|
|
search order only. Below 0.10 it starts with Fill for three or more copies; between
|
|
0.10 and 0.35 it compares Fill-plus-NFP with NFP-only counts; above 0.35 it tries
|
|
NFP first, then Fill if demand remains, taking the higher-count valid proposal.
|
|
An 0.20 NFP-only rule would lose a second 4-inch square in a 10-inch round hole.
|
|
Six geometry-only, anonymized real-job probes and neutral ring fixtures informed
|
|
the search-order hints; these limited cases are not a global density guarantee.
|
|
The internal `NestPipeline.RunCutoutPreview` builds quantity-one proxy instances for eligible
|
|
closed-cutout frames. It considers legal frame rotations without outer-perimeter symmetry
|
|
reduction, shifts insert rotation policies into the local frame, and reserves certified router
|
|
poses by original requirement ID. It can fill multiple holes and repeated/different frame
|
|
requirements; inserts with a different priority or their own cutouts stay independent, so
|
|
bundles remain flat. It uses the largest offered spacing for every bundle because the current
|
|
job contract cannot condition insert demand on the stock selected later. Remaining demand is
|
|
offered to the selected engine once. Only actually placed proxies expand to original frame and
|
|
insert identities. An unplaced proxy's reservations return to the original unplaced demand;
|
|
they cannot be packed retroactively in that same solve. Engine indices and metadata are checked,
|
|
then every physical pose, stock, rotation, quantity and fulfillment is independently validated
|
|
before binding caller drawings. Incomplete, forged or geometrically invalid composites return
|
|
no bindable plates even with invalid-result consent. Candidate evaluation progress is forwarded
|
|
with unverified proxy commitment counts zeroed; transformed proxy commits are hidden. Physical
|
|
commits are reported only after physical validation. Preparation is bounded to 32 bundles
|
|
and 16 base sweep-angle samples per frame (automatic quarter-turns are all retained);
|
|
remaining copies stay ordinary requirements. Fixed legal proxy orientations are a
|
|
conservative search choice, not full engine choice among all legal orientations; unsupported
|
|
bundles remain ordinary independent requirements. This is an internal pipeline path, not a
|
|
production setting or cut-ready nest. Cutting/post safety remains pending before enablement.
|
|
|
|
## Renamed engines
|
|
|
|
The registry maps names used by earlier releases (the first two shipped as plug-ins) so saved
|
|
desktop selections, scripts and API requests keep working:
|
|
|
|
| Old name | Now |
|
|
|---|---|
|
|
| `Opus55NestingEngine` | Irregular |
|
|
| `RectanglesNestingEngine` | Rectangles |
|
|
|
|
In v0.3.0 and earlier, `Default` was the multi-phase fill engine now named Fill. `Default` now names the
|
|
choosing engine above, so saved selections, scripts and API requests that name it get that
|
|
engine. Fill-strategy calls (interactive fill, `PlateFillService`, console fill without
|
|
`--autonest`, MCP fill tools) still read `Default` as Fill.
|
|
|
|
Gpt6Astra and Qwen38FlashNext are no longer shipped and have no alias. A saved selection of either
|
|
falls back to the default engine with the usual status-bar warning.
|
|
|
|
## Changing an engine
|
|
|
|
Use the [engine benchmarking guide](performance/engine-benchmarking.md) for the command, candidate-sheet controls, and comparison procedure.
|
|
|
|
- A change lands only when it beats the engine's current result on `OpenNest.Benchmark` for the
|
|
jobs that engine targets, with every layout valid. Report cost, validity, unplaced parts and time.
|
|
- Placement must be deterministic: no clocks, unseeded randomness or environment variables. Budget
|
|
work by counting it; wall time may stop work only through the cancellation token.
|
|
- Keep each engine's tests passing, including `EngineContractTests<TEngine>` (quadrants, overflow,
|
|
priority, cancellation, stock and plate limits, determinism). Every layout in those tests is
|
|
checked with `NestLayoutCheck`, the benchmark's validator.
|
|
- Engines may share code. Move a helper into shared Engine code when a second engine needs it,
|
|
rather than copying it.
|
|
|
|
## Plug-ins
|
|
|
|
External engines still load from an `Engines/` folder beside the desktop, console, MCP or benchmark
|
|
executable. A plug-in implements `INestingEngine` with a public parameterless constructor and
|
|
registers under its CLR type name. A plug-in whose name matches a built-in engine, or a renamed
|
|
engine's old name, is skipped: a leftover `OpenNest.Engine.Opus55.dll` cannot shadow Irregular.
|
|
Leftover Gpt6Astra or Qwen38FlashNext DLLs still load as ordinary plug-ins until deleted.
|
|
|
|
## Cost basis and heuristic limits
|
|
|
|
`NestPlateStock.Cost` is nullable, immutable and per physical sheet. Every available
|
|
stock must have a finite positive cost, or all must omit it. Omitted costs preserve
|
|
area scoring. The original constructor and area-valued `NetSheetArea` and obsolete
|
|
`EstimateNetArea` APIs retain their units. `NetSheetCost` applies proportional salvage
|
|
to explicit costs; geometric area still controls fit, utilization and material ordering.
|
|
|
|
Rectangles uses net cost in sheet trials and remaining-material estimates. Irregular
|
|
uses it in look-ahead, strategy comparison and equivalent-demand tail replacement.
|
|
StockLadder ranks net cost per placed material area and uses total net cost for
|
|
consolidation. Default compares valid equally fulfilled results by generic job score.
|
|
Fixed Fill/Strip/remnant strategies rank priority fulfillment first, then net cost
|
|
for priced stock (gross area for legacy unpriced stock), then existing stable ties.
|
|
None guarantees globally optimal purchasing or explores every combination of patterns.
|