Two bugs caused the remnant finder to miss valid empty regions:
1. RemoveDominated used an 80% overlap-area threshold that incorrectly
removed L-shaped remnants. A tall strip to one side would "dominate"
wide strips above/below it even though they represent different usable
space. Replaced with geometric containment check — only remove a box
if it's fully inside a larger one.
2. FindTieredRemnants split remnants at the obstacle envelope boundary,
and both pieces could fall below minDimension even though the original
remnant passed the filter (e.g., 6.6" remnant split into 5.35" + 1.25"
with minDim=5.38"). Added fallback to keep the original unsplit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add plate-free Push overload and CompactIndividual method that pushes
each part individually against all others as obstacles. Disabled in
StripNestEngine pending investigation — compaction opens irregular gaps
that the remnant finder scatters parts into.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Skip remnants that are too small to fit any remaining part, avoiding
wasted fill attempts. Recalculated each iteration as quantities deplete.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remainder items were being filled into the full remnant box without
compaction. Added ShrinkFill helper that fills then shrinks the box
horizontally and vertically while maintaining the same part count.
This matches the strip item's shrink behavior and produces tighter
layouts that leave more usable space for subsequent items.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The iterative remnant fill was mutating shared NestItem.Quantity objects,
causing the second TryOrientation call (left) to see depleted quantities
from the first call (bottom). Use a local dictionary instead so both
orientations start with the full quantities.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the single-pass guillotine split approach with RemnantFinder-based
iteration. After each fill, re-discover all free rectangles and try all
remaining items again until no more progress is made. This fills gaps that
were previously left empty after the initial strip + remainder layout.
Also change ActiveWorkArea border color from orange to red.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ComputeRemainderWithin only returned the larger of two possible free
rectangles, permanently losing usable area on the other axis after each
remainder item was placed. Replace the single shrinking box with a list
of free rectangles using guillotine cuts so both sub-areas remain
available for subsequent items.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
StripNestEngine only overrode Fill(NestItem), so ActionClone.Fill
and Pack operations fell through to the empty base class defaults.
Now all virtual methods delegate to DefaultNestEngine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wrap IProgress with AccumulatingProgress so remnant fills prepend
previously placed strip parts to each report. The UI now shows the
full picture (red + purple) instead of replacing strip parts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nest() now deducts placed counts from input NestItem.Quantity so the
UI loop doesn't create extra plates. All inner DefaultNestEngine.Fill
calls forward the IProgress parameter for live progress updates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The auto-nest code paths (MainForm, MCP, Console) now call
engine.Nest(items, progress, token) instead of manually orchestrating
sequential fill+pack. The default implementation in NestEngineBase
does sequential FillExact+PackArea. StripNestEngine overrides with
its strip strategy. This makes the engine dropdown actually work.
Also consolidates ComputeRemainderWithin into NestEngineBase,
removing duplicates from MainForm and StripNestEngine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New NestEngineBase subclass that dedicates a tight strip to the
largest-area drawing and fills the remnant with remaining drawings.
Tries both bottom and left orientations, uses a shrink loop to find
the tightest strip, and picks the denser result.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The forward bounding-box gap check (gap < 0) incorrectly skipped obstacles
for irregular shapes like SULLYS-003 whose narrow handle extends past an
adjacent part's BB edge while the wide body still needs contact detection.
Replaced with a reverse-direction gap check that only skips obstacles the
moving part has entirely cleared. Also fixed edge distance check to prevent
overshooting the work area boundary when already at the limit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PushSelected now calls Compactor.Push instead of duplicating the push
logic. Compactor.Push moves parts as a group (single min distance) to
preserve grid layouts. Compact tries both left-first and down-first
orderings, iterating up to 20 times until movement drops below
threshold, and keeps whichever ordering traveled further.
Also includes a cancellation check in FillWithProgress to avoid
accepting parts after the user stops a nest.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Strip mode was adding thousands of candidates (7600+) when the work area
was narrow. Now caps at 100 total, sorted by utilization descending so
the best candidates are tried first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The binary search was passing null for progress, so the NestProgressForm
showed all dashes during the entire search (potentially minutes). Now
each iteration reports progress — the user sees phases, part counts, and
density updating as the search runs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Track productive angles across Fill calls; subsequent fills skip
angles that never produced results (knownGoodAngles)
- Binary search uses utilization-based range estimates (70%-25%)
instead of starting from the full work area dimension
- Quick bounding-box capacity check skips binary search entirely
when the plate can't fit more than the requested quantity
- Use full Fill (not rect-only) for binary search iterations so
the search benefits from pairs/linear strategies
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously each moving part only checked against the original stationary
set. Parts pushed earlier in the loop were invisible to later parts,
causing overlaps (utilization > 100%). Now each pushed part is added to
the obstacle set so subsequent parts collide correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>