76 Commits

Author SHA1 Message Date
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
b1e872577c feat: add Description/ShortName attributes to NestPhase with extension methods
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 19:38:54 -04:00
9903478d3e refactor: simplify BestCombination.FindFrom2 and add tests
Remove redundant early-return branches and unify loop body — Floor(remaining/length2) already returns 0 when remaining < length2, so both branches collapse into one. 14 tests cover all edge cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 17:07:43 -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
0b7697e9c0 feat: add VerticalRemnantEngine and HorizontalRemnantEngine
Two new engine classes subclassing DefaultNestEngine that override
CreateComparer, PreferredDirection, and BuildAngles to optimize for
preserving side remnants. Both registered in NestEngineRegistry and
covered by 6 integration tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 12:57:33 -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
1a41eeb81d feat: add VerticalRemnantComparer and HorizontalRemnantComparer
Implements two IFillComparer strategies that preserve axis-aligned remnants:
VerticalRemnantComparer minimizes X-extent, HorizontalRemnantComparer minimizes
Y-extent, both using a count > extent > density tiebreak chain. Includes 12
unit tests covering all tiebreak levels and null-guard cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 12:38:23 -04:00
f894ffd27c feat: add IFillComparer interface and DefaultFillComparer
Extracts the fill result scoring contract into IFillComparer with a DefaultFillComparer implementation that preserves the existing count-then-density lexicographic ranking via FillScore.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 12:36:04 -04:00
3f3d95a5e4 fix: orient pair short side along primary axis before convergence
The convergence loop now ensures the pair starts with its short side
parallel to the primary axis, maximizing the number of pairs that fit.
Also adds ConvergeStripeAngleShrink to try N+1 narrower pairs, and
evaluates both expand and shrink results to pick the better grid.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 09:22:13 -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
0597a11a23 feat: implement StripeFiller.Fill with pair iteration, stripe tiling, and remnant fill
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 07:44:59 -04:00
2ae1d513cf feat: add StripeFiller.ConvergeStripeAngle iterative convergence
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 07:37:59 -04:00
904d30d05d feat: add StripeFiller.FindAngleForTargetSpan with scan-then-bisect
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 07:36:10 -04:00
7f96d632f3 fix: correct NFP polygon computation and inflation direction
Three bugs fixed in NfpSlideStrategy pipeline:

1. NoFitPolygon.Reflect() incorrectly reversed vertex order. Point
   reflection (negating both axes) is a 180° rotation that preserves
   winding — the Reverse() call was converting CCW to CW, producing
   self-intersecting bowtie NFPs.

2. PolygonHelper inflation used OffsetSide.Left which is inward for
   CCW perimeters. Changed to OffsetSide.Right for outward inflation
   so NFP boundary positions give properly-spaced part placements.

3. Removed incorrect correction vector — same-drawing pairs have
   identical polygon-to-part offsets that cancel out in the NFP
   displacement.

Also refactored NfpSlideStrategy to be immutable (removed mutable
cache fields, single constructor with required data, added Create
factory method). BestFitFinder remains on RotationSlideStrategy
as default.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 23:24:04 -04:00
bc78ddc49c perf: use convex hull NFP to avoid Clipper2 union bottleneck
ConvexMinkowskiSum is O(n+m) with no boolean geometry ops.
The concave Minkowski path was doing triangulation + pairwise
sums + Clipper2 Union, which hung at 100% CPU for complex parts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 20:54:19 -04:00
b7c7cecd75 feat: wire NfpSlideStrategy into BestFitFinder pipeline
Replace RotationSlideStrategy with NfpSlideStrategy in BuildStrategies,
and add integration tests covering the end-to-end FindBestFits pipeline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:09:48 -04:00
5f4288a786 feat: add NfpSlideStrategy for NFP-based best-fit candidate generation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:03:52 -04:00
71f28600d1 refactor: extract PolygonHelper from AutoNester for shared polygon operations
Creates PolygonHelper.cs in OpenNest.Engine.BestFit with ExtractPerimeterPolygon
(returning PolygonExtractionResult with polygon + correction vector) and RotatePolygon.
AutoNester.ExtractPerimeterPolygon and RotatePolygon become thin delegates.
Adds MakeSquareDrawing/MakeLShapeDrawing to TestHelpers and 6 PolygonHelperTests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 19:56:20 -04:00
708d895a04 perf: remove automatic angle sweep in linear fill
Remove NeedsSweep that triggered a 5-degree sweep (36 angles) when
the work area was narrower than the part. Position matters more than
angle for narrow areas, and the base angles (bestRotation + 90deg)
cover the useful cases. ForceFullSweep still works for training.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:43:38 -04:00
3756ea255e fix(test): plate size 2026-03-20 00:32:45 -04:00
6d66636e3d refactor: replace ShrinkFiller shrink loop with TrimToCount
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 00:06:58 -04:00
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
c31ef9f80c test(engine): add multi-item, leftover, unlimited qty, and cancellation tests for IterativeShrinkFiller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 10:34:12 -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
f13443b6b3 feat(api): add NestRunner with multi-plate loop
Stateless orchestrator that takes a NestRequest and returns a NestResponse.
Imports DXFs, builds NestItems, runs the engine in a multi-plate loop until
all parts are placed, computes timing, and returns utilization metrics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 08:37:44 -04:00
a7688f4c9d feat(api): add NestResponse with SaveAsync/LoadAsync
Adds NestResponse type to OpenNest.Api with SaveAsync/LoadAsync for .nestquote format — a ZIP containing request.json, response.json (metrics), and an embedded nest.nest.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 08:33:35 -04:00
84679b40ce feat(api): add NestStrategy, NestRequestPart, NestRequest
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 08:26:51 -04:00
b6bd7eda6e refactor: move CutParameters to OpenNest.Api namespace with new properties
Relocates CutParameters from OpenNest namespace to OpenNest.Api, adds
LeadInLength and PostProcessor properties, and provides a typed Default
factory. Updates Timing.cs, the WinForms project reference, and the three
consuming forms to resolve the type from the new namespace.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 08:25:32 -04:00
cfe8a38620 chore: add OpenNest.Api project skeleton
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 08:23:07 -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
794ef16629 test: add Compactor safety-net tests before refactor
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 20:17:06 -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
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
442501828a test(io): add DXF roundtrip tests
Verifies export-then-reimport preserves geometry for lines,
circles, arcs, mixed entities, and rectangle bounding boxes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 14:53:58 -04:00
202f49f368 test(engine): add FillStrategyRegistry and pipeline tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 13:08:13 -04:00
0ac7b9babd fix(test): rename misleading test method name
Tile_CellLargerThanPlate_ReturnsSingleCell -> ReturnsEmpty

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 09:46:26 -04:00
f336af5d65 feat(engine): add PatternTiler for unit cell tiling across plates 2026-03-18 09:43:45 -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
bfd740c81e feat(core): add FlangeShape with JSON preset loading
FlangeShape generates an outer circle with evenly-spaced bolt holes
on a bolt circle pattern. ShapeDefinition.LoadFromJson<T>() provides
generic JSON loading for any shape — no separate preset classes needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 11:36:26 -04:00
dde07fc256 merge: resolve polylabel conflicts, keep remote version with hole support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 09:02:30 -04:00
6e5471271d feat(core): add RoundedRectangleShape
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:07:22 -04:00
0651f185e3 feat(core): add OctagonShape
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:06:21 -04:00
33377291a6 feat(core): add TShape
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 08:06:08 -04:00