Commit Graph
20 Commits
Author SHA1 Message Date
ajandClaude Opus 5.5 bcbcf1e611 perf(fill): cut a cold Lattice fill from ~300 s to ~160 s with identical layouts
The first fill of a new drawing was blamed on the best-fit pair search, but on the
reported nest that search takes ~3.4 s. The time goes to FillLinear.FillGrid, run
thousands of times by the Pairs, Row/Column and Linear strategies: pattern copy-distance
slides and the grid overlap check. Every change keeps the output bit for bit (part
poses compared on the benchmark nest; best-fit results unchanged).

- ConvexDecomposition.Triangulate: unlink clipped vertices from a circular list instead
  of List.RemoveAt, and remember why a vertex failed the ear test (same neighbors, same
  blocking vertex still present) instead of retesting it after every ear. Compared
  bitwise against a frozen pre-change copy, including the blocker-clipped case.
- Collision: skip triangles whose box misses the other triangulation's union box (exact:
  min/max and monotone subtraction), reuse each triangle's open vertices, and let the
  overlap-only path stop at its first region when there are no holes. Check still
  returns every region.
- SpatialQuery.ExtractEntityVertices presizes its set; FillLinear extracts each entity
  list's vertices once per copy-distance search rather than once per pair.
- StripeFiller converges its stripe angles and tiles its grids in parallel. Dedup claims,
  cached remnant fills, ranking and progress stay in candidate order, and grids are tiled
  only for configurations a claim can still win (GridDedup.Contains), so results and
  reports do not depend on the thread count.
- LinearFillStrategy fills its angles in parallel through
  FillHelpers.BestOverAnglesInParallel; angle results, ranking and progress stay in order.

Benchmark (one drawing, 60x120, 0.25 spacing, cleared plate, interleaved runs):
FillItem Lattice 291/317 s -> 164/153 s, 132 parts both; PairFiller alone 48 s -> 38-42 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 19:34:31 -05:00
ajandClaude Opus 5.5 859d10b31d feat(engine): offer a box layout before best-fit pairs in Fill
Interactive Fill ran the best-fit pair phase first. The first pair
computation for a drawing can take about a minute, and until it finished
the progress dialog showed no layout, Accept stayed disabled and the
status text did not change.

The fill now packs the part boxes (RectBestFit) before the pair phase and
reports that layout, so Accept is available at once. The layout is only a
fallback: strategies still run in their usual order, and a completed fill
picks the same winner as before. While the pairs are computed, the detail
line reads "Computing best-fit pairs (may take a minute)...". Accept or
Stop now stops the wait. The computation keeps running in the background
and is cached for the next fill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:49:48 -04:00
aj 32db086384 perf(irregular): cap surplus rows in private stripe fills 2026-10-05 09:03:49 -04:00
aj 6bcea7919a refactor(engine): remove unused stripe engine hook 2026-09-21 15:43:15 -04:00
aj aec0523062 style: apply CSharpier formatting to all C# sources
Repo-wide sweep with the pinned CSharpier 1.3.0 tool. Whitespace and
line-wrapping only; OpenNest.Engine.Tests (109) and OpenNest.IO.Tests
pass after reformat, full solution builds 0 errors.

Added .csharpierignore so csproj/config XML keeps its existing layout
(CSharpier's XML wrapping churns attributes with zero benefit).

Formatting is now enforceable: dotnet csharpier check . passes.
2026-09-20 16:41:50 -04:00
ajandClaude Opus 4.6 ef15421915 refactor: standardize fill strategy progress reporting via FillContext
Strategies and fillers previously called NestEngineBase.ReportProgress
directly, each constructing ProgressReport structs with phase, plate
number, and work area manually. Some strategies (RectBestFit) reported
nothing at all. This made progress updates inconsistent and flakey.

Add FillContext.ReportProgress(parts, description) as the single
standard method for intermediate progress. RunPipeline sets ActivePhase
before each strategy, and the context handles common fields. Lower-level
fillers (PairFiller, FillExtents, StripeFiller) now accept an
Action<List<Part>, string> callback instead of raw IProgress, removing
their coupling to NestEngineBase and ProgressReport.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:21:48 -04:00
ajandClaude Opus 4.6 c5943e22eb fix: correct Width/Length axis mapping and add spiral center-fill
Box constructor and derived properties (Right, Top, Center, Translate, Offset)
had Width and Length swapped — Length is X axis, Width is Y axis. Corrected
across Core geometry, plate bounding box, rectangle packing, fill algorithms,
tests, and UI renderers.

Added FillSpiral with center remnant detection and recursive FillBest on
the gap between the 4 spiral quadrants. RectFill.FillBest now compares
spiral+center vs full best-fit fairly. BestCombination returns a
CombinationResult record instead of out params.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:22:55 -04:00
ajandClaude Opus 4.6 953429dae9 fix: add overlap safety check and diagnostics to FillGrid Step 2
FillGrid had no overlap check after perpendicular tiling of the row
pattern (Step 2), unlike Step 1 which had one. When geometry-aware
FindPatternCopyDistance underestimated row spacing, overlapping parts
were returned unchecked.

Changes:
- Make FillLinear.HasOverlappingParts shape-aware (bbox pre-filter +
  Part.Intersects) instead of bbox-only, preventing false positives on
  interlocking pairs while catching real overlaps
- Add missing overlap safety check after Step 2 perpendicular tiling
  with bbox fallback
- Add diagnostic Debug.WriteLine logging when overlap fallback triggers,
  including engine label, step, direction, work area, spacing, pattern
  details, and overlapping part locations/rotations for reproduction
- Add FillLinear.Label property set at all callsites for log traceability
- Refactor LinearFillStrategy and ExtentsFillStrategy to use shared
  FillHelpers.BestOverAngles helper for angle-sweep logic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 22:08:38 -04:00
ajandClaude Opus 4.6 3022982f6d refactor: consolidate HasOverlappingParts into FillHelpers
StripeFiller and FillExtents had identical 24-line overlap detection
methods; move to FillHelpers and delegate from both callers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 16:22:00 -04:00
ajandClaude Opus 4.6 80e8693da3 fix: add overlap detection safety net for pair tiling
Shape.OffsetOutward produces inward offsets for certain rotated polygons,
causing geometry-aware copy distances to be too small and placing
overlapping parts. Root cause is in the offset winding direction
detection — this commit adds safety nets while that is investigated.

- FillLinear.FillGrid: detect bbox overlaps after geometry-aware tiling,
  fall back to bbox-based spacing when overlaps found
- FillExtents.RepeatColumns: detect overlaps after Compactor computes
  copy distance, fall back to columnWidth + spacing
- PairFiller/StripeFiller remnant fills: use FillLinear directly instead
  of spawning full engine pipeline (avoids strategies with the bug)
- Add PairOverlapDiagnosticTests reproducing the issue
- MCP config: use shadow-copy wrapper for dev hot-reload

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 23:52:50 -04:00
ajandClaude Opus 4.6 740fd79adc fix: add overlap validation guards to FillExtents and StripeFiller
FillExtents falls back to the unadjusted column when iterative pair
adjustment shifts parts enough to cause genuine overlap. StripeFiller
rejects grid results where bounding boxes overlap, which can occur when
angle convergence produces slightly off-axis rotations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:13:35 -04:00
ajandClaude Opus 4.6 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
ajandClaude Opus 4.6 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
ajandClaude Opus 4.6 560105f952 refactor: extract shared convergence loop and reduce parameter counts in StripeFiller
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>
2026-03-21 14:22:29 -04:00
ajandClaude Sonnet 4.6 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
ajandClaude Opus 4.6 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
ajandClaude Opus 4.6 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
ajandClaude Opus 4.6 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
ajandClaude Opus 4.6 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
ajandClaude Opus 4.6 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