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>
Parts created by fill operations share a Program instance via
CloneAtOffset. RotateSelectedParts called Part.Rotate on each,
compounding Program.Rotate on the shared object (1x, 2x, 3x…)
and producing inconsistent bounding boxes. Fix tracks rotated
programs with a HashSet, rotates locations around the group center,
and anchors the bounding box corner to prevent drift.
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>
Active remnant shown as dashed orange rectangle on PlateView during
iterative fill workflow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extracts remnant detection from the nesting engine into a standalone
RemnantFinder class using edge projection algorithm, enabling an
iterative nest-area -> get-remnants workflow.
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>
The sequencer returns parts ordered from exit point inward. Reverse
so part 1 is nearest the origin and cutting works outward.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Advanced sequencer with default 0.25 MinDistanceBetweenRowsColumns
puts every part in its own row, degenerating to a Y-sort. Switch to
LeastCode (nearest-neighbor + 2-opt) for visible results.
Also replace AddRange(linq) with foreach+Add to avoid ObservableList
AddRange re-enumerating a deferred LINQ query for event firing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace old SequenceByNearest with PartSequencerFactory using default
SequenceParameters (Advanced method with serpentine row grouping).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Renamed OpenNest.Engine.Tests → OpenNest.Tests (directory, .csproj,
namespaces in all .cs files). Added OpenNest.IO project reference.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
16 tasks covering test infrastructure, core model changes, part sequencing
(6 strategies + factory), rapid planning (2 strategies), and the PlateProcessor
orchestrator. TDD approach with xUnit tests for each component.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix coordinate transforms (translate-only, no rotation), make orchestrator
non-destructive (ProcessedPart holds result instead of mutating Part.Program),
use readonly structs consistently, add factory mapping and known limitations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Visual Studio re-serialized the designer — removes `this.` prefixes,
modernizes event handler syntax, trims trailing whitespace in resx.
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>
When Strip is selected in the engine dropdown, RunAutoNest_Click
calls StripNestEngine.Nest() instead of sequential FillExact+Pack.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Runs strip and sequential strategies in competition, picks the
denser result. Reports scores for both strategies in output.
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>
Document NestEngineBase hierarchy, NestEngineRegistry, and plugin
loading in the Engine section.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
StripNester becomes StripNestEngine extending NestEngineBase.
Uses DefaultNestEngine internally via composition.
Registered in NestEngineRegistry.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pluggable engine architecture with NestEngineBase, DefaultNestEngine,
registry with plugin loading, and global engine switching.
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>
Moving BasePart locations via Compactor.Push bypassed LayoutPart.Offset
which sets IsDirty. Without it, graphics paths were stale until a zoom
triggered a full rebuild.
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>
ComputeRemainderStrip used the bounding box of ALL plate parts against
the full plate, missing large interior gaps between drawing groups.
Now computes remainder within the current work area based on only the
parts that were just placed. This lets subsequent drawings fill the
gap between previous drawing groups instead of being forced into a
tiny strip at the plate edge.
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>