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>
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>
Extract ConvergeFromAngle to deduplicate ~40 lines shared between
ConvergeStripeAngle and ConvergeStripeAngleShrink. Reduce BuildGrid
from 7 to 4 params and FillRemnant from 6 to 2 by reading context
fields directly. Remove unused angle parameter from FillRemnant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
- 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>
Add virtual comparer, direction, and angle-building hooks to NestEngineBase
so subclasses can override scoring and direction policy. Rewire IsBetterFill
to delegate to the comparer instead of calling FillScore directly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
- 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>
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>
Remove implemented plan/spec docs from docs/superpowers/ (recoverable
from git history). Add .superpowers/ and launchSettings.json to gitignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Display current plate utilization percentage in the status bar,
updating live when parts are added or removed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire IDistanceComputer into RotationSlideStrategy, replacing inline
CPU/GPU branching. BestFitFinder constructs the appropriate implementation.
Replace PushDirection enum with direction vectors in BuildOffsets.
Rename IBestFitStrategy.Type and PairCandidate.StrategyType to StrategyIndex
for clarity (JSON field name unchanged for backward compatibility).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract distance computation from RotationSlideStrategy into a pluggable
IDistanceComputer interface. CpuDistanceComputer adds leading-face vertex
culling (~50% fewer rays per direction) with early exit on overlap.
GpuDistanceComputer wraps ISlideComputer with Line-to-flat-array conversion.
SlideOffset struct uses direction vectors (DirX/DirY) instead of PushDirection.
SpatialQuery.RayEdgeDistance(dirX,dirY) made public for CPU path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
NFP strategy has coordinate correction issues causing overlaps.
The slide-based approach is fast and accurate — keeping it as default.
NfpSlideStrategy and PolygonHelper remain in the codebase for future use.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Calling ConvexMinkowskiSum directly with manual reflection produced
wrong winding/reference-point handling, causing all pairs to overlap.
Route through Compute which handles reflection correctly. Hull inputs
keep it fast — few triangles means trivial Clipper union.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
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>
Size(width, length) maps Width to vertical and Length to horizontal in
PlateView, but BoundingWidth (the longer dimension) was being passed as
Width (vertical) instead of Length (horizontal), causing the bounding
box to appear portrait instead of landscape.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevent InvalidOperationException when the timer fires before or
after the control handle is available.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In strip mode, build candidate list entirely from pairs whose
ShortestSide fits the narrow work area dimension, sorted by
estimated tile count. Previously, the top-50 utilization cut
ran first, excluding good strip candidates like #183.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Normalize pair bounding box to landscape (width >= height) in
PairEvaluator for consistent display and filtering. Fix
BestFitViewerForm where BoundingWidth/BoundingHeight were passed
in the wrong order to the plate Size constructor.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Compactor.Settle and AutoNester.Optimize post-passes to
NestEngineBase.Nest, StripNestEngine, and PlateView.FillWithProgress
so all fill paths benefit from geometry-aware compaction.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Optimize method that re-places parts using NFP-based BLF, keeping
the result only if it improves density without losing parts. Fix
perimeter inflation to use correct offset side. Add NfpNestEngine
that wraps AutoNester for the registry. Register NFP engine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Settle method that repeatedly pushes parts left then down until
total movement falls below a threshold. Replaces manual single-pass
push calls for more consistent gap closure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refactor BLF to compute NFP paths as Clipper PathsD with offsets
instead of translating full polygons. Add spatial pruning to skip
NFPs that don't intersect the IFP bounds. Clamp placement points
to IFP bounds to correct Clipper2 floating-point drift. Add
progress reporting to simulated annealing. Add debug logging.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move PlacedPart to its own file. Replace tuple-based sequences with
SequenceEntry struct for clarity. Add IProgress parameter to
INestOptimizer. Add IFP caching to NfpCache to avoid recomputing
inner fit polygons for the same drawing/rotation/workArea.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add immutable Translate methods to Box. Make NoFitPolygon
ToClipperPath/FromClipperPath public with optional offset parameter.
Refactor InnerFitPolygon.ComputeFeasibleRegion to accept PathsD
directly, letting Clipper2 handle implicit union. Add UpdateBounds
calls after polygon construction.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add SelectionChanged event to PlateView and display the selected part's
location and size in a new status bar label. Shows combined bounding box
when multiple parts are selected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>