21 Commits

Author SHA1 Message Date
92b17b2963 perf: parallelize PairFiller candidates and add GridDedup
- Evaluate pair candidates in parallel batches instead of sequentially
- Add GridDedup to skip duplicate pattern/direction/workArea combos
  across PairFiller and StripeFiller strategies
- Replace crude 30% remnant area estimate with L-shaped geometry
  calculation using actual grid extents and max utilization
- Move FillStrategyRegistry.SetEnabled to outer evaluation loop
  to avoid repeated enable/disable per remnant fill

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:08:55 -04:00
ccd402c50f refactor: simplify NestProgress with computed properties and ProgressReport struct
Replace stored property setters (BestPartCount, BestDensity, NestedWidth,
NestedLength, NestedArea) with computed properties that derive values from
BestParts, with a lazy cache invalidated on setter. Add internal
ProgressReport struct to replace the 7-parameter ReportProgress signature.
Update all 13 callsites and AccumulatingProgress. Delete FormatPhaseName
in favor of NestPhase.ShortName() extension.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 19:44:45 -04:00
93a8981d0a feat: add Disable/Enable API to FillStrategyRegistry
Adds methods to permanently disable/enable strategies by name. Disabled
strategies remain registered but are excluded from the default pipeline.
SetEnabled (used for remnant fills) takes precedence over the disabled
set, so explicit overrides still work.

Pipeline test now checks against active strategy count dynamically.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:19:27 -04:00
00e7866506 feat: add remnant filling to PairFiller for better part density
PairFiller previously only filled the main grid with pair patterns,
leaving narrow waste strips unfilled. Row/Column strategies filled
their remnants, winning on count despite worse base grids.

Now PairFiller evaluates grid+remnant together for each angle/direction
combination, picking the best total. Uses a two-phase approach: fast
grid evaluation first, then remnant filling only for grids within
striking distance of the current best. Remnant results are cached
via FillResultCache.

Constructor now takes Plate (needed to create remnant engine).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:19:19 -04:00
83124eb38d feat: wire IFillComparer through PairFiller and StripeFiller
PairFiller now accepts an optional IFillComparer (defaulting to
DefaultFillComparer) and uses it in EvaluateCandidates and
EvaluateCandidate/FillPattern instead of raw FillScore comparisons.
PairsFillStrategy passes context.Policy?.Comparer through.
StripeFiller derives _comparer from FillContext.Policy in its
constructor and uses it in Fill() instead of FillScore comparisons.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 12:53:28 -04:00
24beb8ada1 feat: wire IFillComparer through FillHelpers, Linear, and Extents strategies
- FillHelpers.FillPattern gains optional IFillComparer parameter; falls back to FillScore when null
- LinearFillStrategy.Fill replaced with FillWithDirectionPreference + comparer from context.Policy
- ExtentsFillStrategy.Fill replaced with comparer.IsBetter, removing FillScore comparison
- DefaultNestEngine group-fill path resolves Task 6 TODO, passing Comparer to FillPattern

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 12:49:59 -04:00
ee83f17afe feat: wire FillPolicy into DefaultNestEngine and FillContext
- FillContext gains a Policy property (init-only) carrying the IFillComparer
- DefaultNestEngine.Fill sets Policy = BuildPolicy() on every context
- RunPipeline now uses context.Policy.Comparer.IsBetter instead of IsBetterFill
- RunPipeline promoted to protected virtual so subclasses can override
- BuildAngles/RecordProductiveAngles overrides delegate to angleBuilder
- RunPipeline calls virtual BuildAngles/RecordProductiveAngles instead of angleBuilder directly
- TODO comment added in group-fill overload for Task 6 Comparer pass-through

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 12:46:30 -04:00
4586a53590 feat: add FillPolicy record and FillHelpers.FillWithDirectionPreference
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 12:41:07 -04:00
0ec22f2207 feat: geometry-aware convergence, both-axis search, remnant engine, fill cache
- Convergence loop now uses FillLinear internally to measure actual
  waste with geometry-aware spacing instead of bounding-box arithmetic
- Each candidate pair is tried in both Row and Column orientations to
  find the shortest perpendicular dimension (more complete stripes)
- CompleteStripesOnly flag drops partial stripes; remnant strip is
  filled by a full engine run (injected via CreateRemnantEngine)
- ConvergeStripeAngleShrink tries N+1 narrower pairs as alternative
- FillResultCache avoids redundant engine runs on same-sized remnants
- CLAUDE.md: note to not commit specs/plans

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 10:12:31 -04:00
811d23510e feat: add RowFillStrategy and ColumnFillStrategy with registry integration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 07:48:00 -04:00
e969260f3d refactor(engine): introduce PairFillResult and remove FillRemainingStrip
PairFiller now returns PairFillResult (Parts + BestFits) instead of
using a mutable BestFits property. Extracted EvaluateCandidates,
TryReduceWorkArea, and BuildTilingAngles for clarity. Simplified the
candidate loop by leveraging FillScore comparison semantics.

Removed FillRemainingStrip and all its helpers (FindPlacedEdge,
BuildRemainingStrip, BuildRotationSet, FindBestFill, TryFewerRows,
RemainderPatterns) from FillLinear — these were a major bottleneck in
strip nesting, running expensive fills on undersized remnant strips.
ShrinkFiller + RemnantFiller already handle space optimization, making
the remainder strip fill redundant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:53:23 -04:00
0472c12113 refactor(fill): extract constants and EvaluateCandidate in PairFiller
Extract magic numbers into named constants (MaxTopCandidates,
EarlyExitMinTried, etc.), extract candidate evaluation into
EvaluateCandidate method, and expose BestFits property so
PairsFillStrategy can reuse without redundant BestFitCache call.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:48:12 -04:00
0a33047ad6 fix(engine): prevent FillExtents overlap and add strategy filter API
FillExtents vertical copy distance was not clamped, allowing rows to be
placed overlapping each other when slide calculations returned large
values. Clamp to pairHeight + partSpacing minimum, matching FillLinear.

Also add FillStrategyRegistry.SetEnabled() to restrict which strategies
run — useful for isolating individual strategies during troubleshooting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 19:53:08 -04:00
0e1e619f0a refactor(engine): move fill and strategy code to dedicated namespaces
Move fill algorithms to OpenNest.Engine.Fill namespace:
FillLinear, FillExtents, PairFiller, ShrinkFiller, Compactor,
RemnantFiller, RemnantFinder, FillScore, Pattern, PatternTiler,
PartBoundary, RotationAnalysis, AngleCandidateBuilder, and
AccumulatingProgress.

Move strategy layer to OpenNest.Engine.Strategies namespace:
IFillStrategy, FillContext, FillStrategyRegistry, FillHelpers,
and all built-in strategy implementations.

Add using directives to all consuming files across Engine, UI,
MCP, and Tests projects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:46:11 -04:00
267254dcae feat(engine): add LinearFillStrategy adapter
Wraps FillLinear in an IFillStrategy, sweeping all AngleCandidates
from SharedState (falling back to 0° and 90°) in both directions and
recording AngleResults for UI inspection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 13:02:16 -04:00
5668748f37 feat(engine): add ExtentsFillStrategy adapter
Wraps FillExtents in an IFillStrategy, trying both bestRotation and
bestRotation+90° angles and picking the better result. Reads
BestFits from SharedState (populated by PairsFillStrategy) to allow
FillExtents to search the best-fit cache for improved pair geometry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 13:02:10 -04:00
b7de61e4d1 feat(engine): add RectBestFitStrategy adapter
Wraps FillBestFit rectangle packer in an IFillStrategy so the rect
best-fit phase participates in the pluggable pipeline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 13:02:05 -04:00
c4d5cfd17b feat(engine): add PairsFillStrategy adapter
Wraps PairFiller in an IFillStrategy so the pairs phase participates
in the pluggable pipeline. Stores BestFitResults in SharedState for
downstream strategies (Extents) to reuse.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 13:02:00 -04:00
1f965897f2 refactor(engine): extract FillHelpers from DefaultNestEngine
Move BuildRotatedPattern and FillPattern static methods into a new
public FillHelpers class in Strategies/. DefaultNestEngine retains
internal static forwarding stubs so existing callsites are unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 12:59:15 -04:00
46fe48870c feat(engine): add FillStrategyRegistry with reflection-based discovery
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 12:57:31 -04:00
c287e3ec32 feat(engine): add IFillStrategy interface and FillContext
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 12:57:27 -04:00