docs: add lead item rotation design spec for strip nesting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# Lead Item Rotation for Strip Nesting
|
||||
|
||||
## Problem
|
||||
|
||||
`StripNestEngine.Nest()` sorts multi-quantity items by priority then area descending, always placing the largest-area drawing first. This fixed ordering can produce suboptimal layouts — a different starting drawing may create a tighter shrink region that leaves more usable remnant space for subsequent items.
|
||||
|
||||
## Solution
|
||||
|
||||
Try multiple candidate orderings by promoting each of the top N largest drawings to the front of the fill list. Run the full pipeline for each ordering, score the results, and keep the best.
|
||||
|
||||
## Candidate Generation
|
||||
|
||||
- Take the multi-quantity fill items (already filtered from singles)
|
||||
- Identify the top `MaxLeadCandidates` (default 3) unique drawings by `Drawing.Area`, deduplicated by `Drawing` reference equality
|
||||
- If there is only one unique drawing, skip the multi-ordering loop entirely (no-op — only one possible ordering)
|
||||
- For each candidate drawing, create a reordered copy of the fill list where that drawing's items move to the front, preserving the original relative order for the remaining items
|
||||
- The default ordering (largest area first) is always the first candidate, so the feature never regresses
|
||||
- Lead promotion intentionally overrides the existing priority-then-area sort — the purpose is to explore whether a different lead item produces a better overall layout regardless of the default priority ordering
|
||||
|
||||
## Scoring
|
||||
|
||||
Use `FillScore` semantics for cross-ordering comparison: total placed part count as the primary metric, plate utilization (`sum(part.BaseDrawing.Area) / plate.WorkArea().Area()`) as tiebreaker. This is consistent with how `FillScore` works elsewhere in the codebase (count > density). Keep the first (default) result unless a later candidate is strictly better, so ties preserve the default ordering.
|
||||
|
||||
## Execution
|
||||
|
||||
- Run each candidate ordering sequentially through the existing pipeline: `IterativeShrinkFiller` → compaction → packing
|
||||
- No added parallelism — each run already uses `Parallel.Invoke` internally for shrink axes
|
||||
- `IterativeShrinkFiller.Fill` is a static method that creates fresh internal state (`RemnantFiller`, `placedSoFar` list) on each call, so the same input item list can be passed to multiple runs without interference. Neither `IterativeShrinkFiller` nor `RemnantFiller` mutate `NestItem.Quantity`. Each run also produces independent `Part` instances (created by `DefaultNestEngine.Fill`), so compaction mutations on one run's parts don't affect another.
|
||||
- Only the winning result gets applied to the quantity deduction at the end of `Nest()`
|
||||
|
||||
## Progress Reporting
|
||||
|
||||
- Each candidate run reports progress normally (user sees live updates during shrink iterations)
|
||||
- Between candidates, report a status message like "Lead item 2/3: [drawing name]"
|
||||
- Only the final winning result is reported with `isOverallBest: true` to avoid the UI flashing between intermediate results
|
||||
|
||||
## Early Exit
|
||||
|
||||
- If a candidate meets all requested quantities **and** plate utilization exceeds 50%, skip remaining candidates
|
||||
- Unlimited-quantity items (`Quantity <= 0`) never satisfy the quantity condition, so all candidates are always tried
|
||||
- Cancellation token is respected — if cancelled mid-run, return the best result across all completed candidates
|
||||
- The 50% threshold is a constant (`MinEarlyExitUtilization`) that can be tuned if typical nesting utilization proves higher or lower
|
||||
|
||||
## Scope
|
||||
|
||||
Changes are confined to `StripNestEngine.Nest()`. No modifications to `IterativeShrinkFiller`, `ShrinkFiller`, `DefaultNestEngine`, fill strategies, or the UI.
|
||||
|
||||
## Files
|
||||
|
||||
- Modify: `OpenNest.Engine/StripNestEngine.cs`
|
||||
- Add test: `OpenNest.Tests/StripNestEngineTests.cs` (verify multiple orderings are tried, early exit works)
|
||||
Reference in New Issue
Block a user