Commit Graph

199 Commits

Author SHA1 Message Date
85278bbb75 feat: add ShrinkFiller.TrimToCount for axis-aware edge trimming
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 00:03:57 -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
8bfc13d529 fix(engine): move progress reporting from inner fills to ShrinkFiller
StripNestEngine was passing progress directly to DefaultNestEngine.Fill
inside the ShrinkFiller loop, causing every per-angle/per-strategy report
to update the UI with overlapping layouts in the same work area.

Now inner fills are silent (null progress) and ShrinkFiller reports its
own progress when the best layout improves. IterativeShrinkFiller tracks
placed parts across items and includes them in reports. The trial box is
reported before the fill starts so the work area border updates immediately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:43:35 -04:00
fab2214149 perf(engine): reduce PairFiller work area when count exceeds target
When the first pair candidate places more parts than needed (e.g., 17
when target is 10), sort by BoundingBox.Top, trim from the top until
exactly targetCount remain, and use that Top as the new work area
height. All subsequent candidates fill this smaller area, dramatically
reducing fill time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 11:35:15 -04:00
e3b89f2660 perf(engine): add target count to ShrinkFiller with FillBestFit estimate
When a target count is known, ShrinkFiller now uses FillBestFit (fast
rectangle packing) to estimate how many parts fit on the full area,
then scales the shrink axis proportionally to avoid an expensive
full-area fill. Falls back to full box if estimate is too aggressive.

Also shrinks to targetCount (not full count) to produce tighter boxes
when fewer parts are needed than the area can hold.

IterativeShrinkFiller passes NestItem.Quantity as the target count.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:55:01 -04:00
1e9640d4fc feat(engine): include rotating calipers angle in pair nesting tiling
PairEvaluator already computes OptimalRotation via RotatingCalipers on
the pair's convex hull, but PairFiller.EvaluateCandidate only passed
hull edge angles to FillPattern. Now includes the optimal rotation
angle (and +90°) so tiling can use the mathematically tightest fit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:42:29 -04:00
116a386152 refactor(engine): delete obsolete StripNestResult and StripDirection
Both types were only used internally by the old StripNestEngine.Nest
strip-orientation logic, which has been replaced by IterativeShrinkFiller.
No references remain outside of these files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 10:38:12 -04:00
8957b20bac feat(engine): rewrite StripNestEngine.Nest with iterative shrink-fill
Replaces the old orientation-based strip nesting (TryOrientation,
SelectStripItemIndex, EstimateStripDimension, ShrinkFill helpers) with
a call to IterativeShrinkFiller.Fill for multi-quantity items, plus a
RemnantFinder-based PackArea pass for singles and leftovers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 10:37:21 -04:00
3b6e4bdd3a fix(engine): remove dead unlimitedDrawings set, fix comment accuracy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 10:32:46 -04:00
ef737ffa6d feat(engine): add IterativeShrinkFiller with dual-direction shrink selection
Introduces IterativeShrinkFiller.Fill, which composes RemnantFiller and
ShrinkFiller by wrapping the caller's fill function in a closure that tries
both ShrinkAxis.Height and ShrinkAxis.Width and picks the better FillScore.
Adds IterativeShrinkResult (Parts + Leftovers). Covers null/empty inputs and
single-item placement with three passing xUnit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 10:30:10 -04:00
9a58782c46 merge: resolve conflicts from remote nesting progress changes
Kept using OpenNest.Api in Timing.cs and EditNestForm.cs alongside
remote's reorganized usings and namespace changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 09:35:25 -04:00
e656956c1c fix(api): set plate Material from request, add null guards in LoadAsync
- NestRunner now assigns Material to plates from request.Material
- NestResponse.LoadAsync uses descriptive exceptions instead of null-forgiving operators
- Fix pre-existing FillExtents.Fill signature mismatch (add bestFits parameter)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 08:48:30 -04:00
1c8b35bcfb refactor(engine): rename NestResult to OptimizationResult
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 08:28:12 -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
76e30d91c0 feat(engine): flag overall-best progress reports in DefaultNestEngine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 22:12:20 -04:00
e789fe312d feat(engine): add IsOverallBest flag to NestProgress
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 22:11:29 -04:00
f73bb2bc2f refactor(fill): simplify FindPatternCopyDistance — extract pair loop, remove redundant span calculation
The pattern bounding box already computes max(upper) - min(lower), so the
manual loop was redundant. Extract the N×N pair distance loop into a static
FindMaxPairDistance helper. Drop pre-cached edge arrays since GetEdges()
returns stored references with zero allocation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 21:04:35 -04:00
0da970ec9a fix: revert FillExtents/FillLinear FillHelpers.Tile calls (not yet available)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 20:29:38 -04:00
62f00055b7 Reapply "refactor(compactor): deduplicate Push — PushDirection delegates to Vector overload"
This reverts commit e695e29355.
2026-03-18 20:26:14 -04:00
e695e29355 Revert "refactor(compactor): deduplicate Push — PushDirection delegates to Vector overload"
This reverts commit 9012a9fc1c.
2026-03-18 20:24:33 -04:00
9012a9fc1c refactor(compactor): deduplicate Push — PushDirection delegates to Vector overload
Also fix missing using for FillHelpers in FillLinear and FillExtents,
and update callers (CompactorTests, PatternTileForm) for the new
Vector parameter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 20:23:50 -04:00
b009f195be refactor(compactor): remove dead code — Compact, CompactIndividual, and helpers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 20:19:55 -04:00
dddc890a96 Revert "refactor(engine): simplify FillExtents logic using Compactor.Push"
This reverts commit d1d47b5223.
2026-03-18 20:17:57 -04:00
d1d47b5223 refactor(engine): simplify FillExtents logic using Compactor.Push
Simplify geometry-aware positioning by replacing manual slide calculations with higher-level Compactor.Push utility. Extract pair creation into CreatePair helper, remove redundant UpdateBounds calls, and clean up column/horizontal repetition logic.
2026-03-18 20:13:55 -04:00
c2b8400986 refactor(engine): extract AngleCandidateBuilder.Build into focused helpers
Move known-good pruning check before sweep/ML to avoid wasted work,
extract ContainsAngle, NeedsSweep, AddSweepAngles, ApplyMlPrediction,
and BuildPrunedList so Build reads as a clear pipeline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 20:02:30 -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
1d9bcc63d2 chore: sort using directives
Auto-formatter reordering of using statements across the solution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:47:42 -04:00
6102dd5b85 refactor(engine): migrate Fill(List<Part>) to strategy pipeline
Single-part group fills now delegate to Fill(NestItem) which runs
the full strategy pipeline, eliminating ~70 lines of duplicated
manual phase logic. Multi-part group fills retain the linear
pattern fill (unique to multi-part groups).

PairFiller now references FillHelpers directly instead of
bouncing through DefaultNestEngine helper methods.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:46:24 -04:00
495ee6f0c3 refactor(engine): move NFP code to OpenNest.Engine.Nfp namespace
Move AutoNester, BottomLeftFill, NfpCache, SimulatedAnnealing,
and INestOptimizer/NestResult to OpenNest.Engine.Nfp. These are
not yet integrated into the engine registry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:46:17 -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
0cba528591 docs: update README with accurate features and add roadmap
Remove NFP pair fitting claim from features (not yet integrated).
Qualify lead-in/lead-out as engine-only (UI coming soon).
Mark --autonest CLI option as experimental. Add Roadmap section
with planned work: NFP nesting, lead-in UI, sheet cut-offs,
post-processors, and shape library UI.

Add documentation maintenance instruction to CLAUDE.md requiring
README.md and CLAUDE.md updates when project structure changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 16:45:50 -04:00
7bbfe06494 refactor(engine): replace FindBestFill with strategy pipeline
DefaultNestEngine.Fill(NestItem, ...) now delegates to RunPipeline
which iterates FillStrategyRegistry.Strategies in order.

Removed: FindBestFill, FillRectangleBestFit, QuickFillCount.
Kept: AngleCandidateBuilder, ForceFullAngleSweep, group-fill overload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 13:05:24 -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
4348e5c427 feat(engine): add NestPhase.Custom for plugin fill strategies
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 12:57:23 -04:00
76a338f3d0 refactor(engine): remove dead import and add spacing comment in PatternTiler
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:48:14 -04:00
f336af5d65 feat(engine): add PatternTiler for unit cell tiling across plates 2026-03-18 09:43:45 -04:00
3d6be3900e feat(engine): generalize Compactor.Push to support arbitrary angles and BB-only mode
Add Vector-based overloads to SpatialQuery (ray casting, edge distance,
directional gap, perpendicular overlap) and PartGeometry (directional
line filtering) to support pushing parts along any angle, not just
cardinal directions.

Add Compactor.PushBoundingBox for fast coarse positioning using only
bounding box gaps. ActionClone shift+click now uses a two-phase strategy:
BB push first to skip past irregular geometry snags, then geometry push
to settle against actual contours.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:41:09 -04:00
6229e5e49d fix(engine): fix FillExtents competition and vertical gap bugs
- FillExtents.Fill reported progress internally which overwrote the UI's
  temporary parts even when a better result (e.g. Pairs with 70 parts)
  won the competition. Added final ReportProgress call in FindBestFill
  and Fill(groupParts) to ensure the UI always shows the actual winner.

- FillExtents vertical copy distance clamp (Math.Max with pairHeight +
  spacing) prevented geometry-aware compaction from ever occurring,
  causing visible gaps between rows. Boundaries are already inflated by
  halfSpacing so the calculated distance is correct; only fall back to
  bounding-box distance on non-positive results.

- PairFiller now sets RemainderPatterns on FillLinear so remainder strips
  get pair-based filling instead of only individual parts (+1 part in
  tight layouts).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:59:28 -04:00
ccd230568e feat(engine): integrate FillExtents phase into DefaultNestEngine
Adds FillExtents as a fourth nesting phase in both FindBestFill and
Fill(groupParts, Box), running at bestRotation and bestRotation+90°.
Updates Description to reflect the new phase.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 00:51:51 -04:00
7a19b78d31 feat(engine): implement FillExtents horizontal column repetition
Replace RepeatColumns stub with real implementation: compacts a test column
left against column 1 to derive the copy distance, tiles further columns at
that interval, and clips partial columns to the work area bounds. Adds 4 new
FillExtentsTests covering multi-column fill, rect shapes, non-zero-origin
work areas, and cancellation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 00:50:16 -04:00
31b293324d feat(engine): implement FillExtents iterative height adjustment
Replace AdjustColumn stub with a convergence loop that distributes the
remaining gap between the topmost part and the work area top edge across
all pairs. TryAdjustPair/TryShiftDirection try shifting part2 up (or down
as fallback) and compact left, rejecting moves that widen the pair.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 00:48:41 -04:00
7bc9f134f6 feat(engine): add FillExtents scaffold with pair construction and column tiling
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 00:45:55 -04:00
6e30e24957 feat(engine): add NestPhase.Extents enum value 2026-03-18 00:42:48 -04:00
42f2475f3c fix: correct Size width/length axis mapping throughout codebase
The Size fix (d4222db) changed Size.Width to Y axis and Size.Length to
X axis but only updated DrawPlate/LayoutViewGL. BoundingBox, WorkArea,
rotations, DXF export, and engine code still used the old Width=X
convention, causing the fill engine to get a swapped work area (60x120
instead of 120x60) and parts to fill in the wrong direction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:20:24 -04:00