Commit Graph
580 Commits
Author SHA1 Message Date
aj 73e54fbd67 fix(planning): refuse misplaced display perimeters by name
Review found three blanks that slipped past or failed silently. Enclosure
was checked against the outline's bounding box, so a cutout in the notch of
an L-shaped blank reached an unverified plan that warning acceptance could
apply; every cut point is now tested inside the outline itself, and the
material capture names the display perimeter when no ring encloses the
rest. A blank whose only cutouts are tabbed left the search with nothing
to choose and ended without a finding; it is now refused with its reason,
as is a blank with nothing to cut, which used to get only the generic
noncutting message. None of these offers even an unverified plan.

Clean-bounds containment, seam overcut trimming and incremental closure
drift now treat the display perimeter like the other material boundaries.
2026-10-22 19:56:14 -04:00
aj 6e3705eb89 feat(planning): cut holes inside a display perimeter that is never cut
Some jobs cut only cutouts into a supplied blank, such as instrument holes
in a purchased door that is the whole sheet. Its outline has to be known
to the planner (the blank is material that leads, rapids and cut-offs must
respect) but must never be pierced, led or cut.

A new DisplayPerimeter layer marks that outline. It is material, so
nesting, containment, lead clearance and automatic cut-offs all see the
blank; a door that fills its sheet leaves cut-offs nothing to cut. Cutting
preparation keeps it out of the contours entirely: with no cut perimeter
every contour is a hole with internal leads, and the search, best-effort
and safest-route paths stop offering a perimeter. Replay refuses any run
along the display perimeter and completes once every other ring is cut.

A display perimeter must be one whole closed contour enclosing every other
contour; a mixed ring, one inside another contour, or a second one is
refused with that reason. Saving, drawing and posting it are still to do.
2026-10-22 19:32:22 -04:00
ajandClaude Fable 5.1 8f27eac2ca feat(cutting): plan each scrap cut-off segment where the route passes it
Plates with staged automatic scrap cut-offs could not be planned: P260810-01
plate 1 ran out of budget even at 10x effort, while the same plate plans
Ready in 3.6k expansions without the lines. A cut-off was one fixed program
cut whole before every part its line passes through, which dragged the tour
to the middle of the sheet and left the head at the far edge of the line;
and cutting every line first (the rejected alternative) weakens the sheet
and breaks the containment operators rely on when they fast-forward a
changed program.

A cut-off stays one definition, but Plate.RegenerateCutOffs now makes one
part per trimmed segment, each with its own place in the cut sequence.
CutOffSegments gives each segment the span of the nominal line it stands
for (end of the segment before it to start of the one after), and the
dependency rules of the planner and Part Sequencing use that span, so a
segment precedes only the parts beside it. The free-order search plans the
parts as if the segments were not there, so adding lines no longer changes
a plate's route, then CutOffInsertion slides each segment into the latest
gap before its first part where the rapid into its pierce and the rapid
from its end cross nothing cut before; only when one fits nowhere is the
plate searched with the segments among the parts. The search looks through
segments wherever they sit (entries face the next part, hole routes rank
from the pre-segment position, rapids are still checked from the segment's
end), so an applied plan replanned with the order kept replays Ready.
Segments saved out of consecutive order are recorded per segment in the
.nest file (`segments`); older readers load them consecutively.

P260810-01 plate 1 now plans Ready in 3639 expansions with every check
passing; rapid travel 1836 against 1547 without the lines. Plates without
cut-offs keep their PlanningCharacterizationTests digests, and the
problem-nest corpus has no plate below its baseline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-22 11:38:38 -04:00
ajandClaude Opus 5.5 ff46de69df feat(planning): offer the safest route when the strict search runs out
Cut planning should rank routes safest first, then fastest, but a plate whose
strict search hit its expansion budget was simply refused ("no plan found
within the search limit"), even when a route with a few reviewable rapid
crossings existed. Scrap cut-offs make this common: their trimmed segments
can wall off a column of parts, and rapids are straight.

When both the free-order and current-order searches stop at the limit, the
batch now walks a greedy safest route from each input, replays it with fresh
checks, and keeps the one with fewer violations (material first, then rapid
crossings, then the rest), then less rapid travel. A route that passes every
check is an ordinary Ready plan; otherwise it is an unverified BestEffort
that lists every violation and applies only with explicit acceptance. Routes
with any motion into material are never offered, so the strict refusal and
its lead-in advice stand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:37:17 -04:00
ajandClaude Opus 5.5 64eca9775f feat(sequencing): mute parts until a line or click sequences them in Sequence by Path
With every part drawn the same, it was hard to see which parts the drawn
lines had not reached yet. Parts no line reaches are now drawn muted, and
update live while dragging. Clicking a part adds it as a one-point line, so
a stray part can be picked without drawing a line to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:50:12 -04:00
ajandClaude Opus 5.5 d1c77b8ec7 fix(sequencing): cut parts no drawn line crosses last in Sequence by Path
Every part was claimed by its nearest line even when the line never reached
it, so a part beyond a line's start projected to distance 0 along it and was
cut near the front. Parts now belong only to lines crossing their bounding
box; uncrossed parts follow all the others, ordered by their nearest line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:50:11 -04:00
ajandClaude Opus 5.5 35359906fe feat(fill): add tilted parts beside a layout whose leftover strip is too narrow upright
Fill only tried 0 and 90 degrees for parts classified as rectangles, so a long
part filling a 60 x 120 plate four high at 90 degrees left a 55 x 59 strip empty
although two more fit tilted about 33 degrees (a reported nest: 4 placed, 6 fit).
The second tilted part only fits with its corner tucked beside the grid, in the
strip the grid's slack leaves, so a plain rectangular remnant fill finds one.

TiltedRemnant runs after the other strategies. It moves the best layout flush to
one end so leftover room becomes one remnant, and to either end of the other axis
so its slack forms one strip; then tiles the part at the most upright angles that
fit the remnant's height and keeps only copies at least the part spacing from the
layout. It is skipped when the part fits the remnant upright, for circles, for an
explicit rotation range and once the requested quantity is placed.

Fill benchmark (--parallel 1): the synthetic set is unchanged (all valid, same
cost and plates). The reported part needs 1 sheet instead of 2 for 6 parts and
2 instead of 3 for 12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:49:59 -04:00
ajandClaude Opus 5.5 3ca7d6185f feat(desktop): nest released drawing sets with Auto Nest before saving
A release now ends in nests ready to cut rather than empty drafts, and a
merge no longer leaves the draft it cleared waiting for someone to run Auto
Nest. "Nest after release" (on by default) runs Auto Nest headless on every
released group, new or merged, before anything is uploaded.

NestRenest removes every plate and nests each drawing's required quantity
onto sheets from the nest's plate defaults and plate-size options. It keeps
a layout only when it is valid and keepable, so an invalid result never
reaches the server; the nest is saved without sheets and the operator is
told to run Auto Nest. Unplaced copies stay as remaining quantity, and the
status column lists sheets, utilization and what did not fit. A new Nesting
progress stage reports each group, and cancelling while nesting saves
nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:25:55 -04:00
ajandClaude Opus 5.5 8c7dc41ce8 feat(planning): warn about tipping parts and retire legacy lead-in assignment
Plan Cutting is now the only automatic lead-in assignment, and parts the
support grid cannot hold level are reported wherever they lack a
steadying tab.

- Remove Assign Lead-ins (plate and nest), LeadInAssigner, the lead-in
  tool's Auto-Assign button and the Auto Tab size range; old settings
  with autoTab sizes still load
- Manual Place Lead-in tabs the clicked entry when support tabs are on
  and the grid cannot hold the part (SupportTabSettings)
- New PartSupport finding: Plan Cutting replay and best-effort plans
  warn when a drawing pin, a lead-in that does not fit or no single tab
  leaves a part unsteady; the pre-post review checks each placed
  program's outside tab gap. These warnings replace the advisory
  weak-tab notes; only the inside-corner tab note stays advisory, and
  never for a pin
- Pinned perimeters keep their pin but carry the support analysis
- Draw grid points as pixel-snapped squares without anti-aliasing
- Post fixtures build lead-ins through a test-only TestLeadIns helper
- Docs: support tabs, pre-post verification and planner notes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:54:40 -04:00
ajandClaude Opus 5.5 0ca4e64189 fix(bestfit): drop saved pair sets whose canonical frame changed
Saved best-fit pairs are in the drawing's canonical frame, but the canonical
angle is recomputed on load and was not saved with the set. When that
computation changes (RotatingCalipers checking every hull edge, marks no
longer counted as material), old pairs were replayed in a turned frame and
could overlap.

Each saved set now records canonicalAngle, and BestFitCacheStore.Import
skips sets whose angle no longer matches the recomputed one (1e-9 rad,
modulo a full turn). Sets from older files have no angle and are dropped;
their pairs are recomputed when next needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:04:23 -04:00
ajandClaude Opus 5.5 f901840ed0 fix(flatbar): stop the swap optimization trading identical copies
TryImprove kept a swap when the trial run looked fuller than before, but
the trial charges each type's self-nesting gain again even when the run
already holds that type. Trading a placed copy for an identical unplaced
one therefore looked fuller by the gain and repeated forever: FlatBar ran
into the benchmark's 5-minute timeout on tabbed_cog_layout once the
all-edges MBR fix gave one part new rotations (it finished in 7 s before).

The swap is now judged on the merged run, so every kept swap really
leaves the run fuller and the loop terminates. Swaps the old test kept
that do fill the run are still kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:41:27 -04:00
ajandClaude Opus 5.5 96f02c665c feat(desktop): preview sequence numbers live while drawing a path
While a Sequence by Path line is being dragged, the plate view shows the
numbers the finished line would give, without reordering the plate, so
reorder listeners (drawing list, overlap overlay, simulator) are not hit
on every mouse move. The plate is reordered once on release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 20:26:55 -04:00
ajandClaude Opus 5.5 aeb90d8a86 feat(desktop): sequence parts along lines drawn over the plate
Plate > Sequence Parts > Sequence by Path: each left-drag adds a line,
cut in drawing order; the jump between lines is a rapid and claims no
parts. Each part belongs to the line passing closest to its center and
is ordered by where that nearest point falls along the line. Cut-first
parts stay ahead and cutoff dependency rules still apply. A small window
offers Undo Line, Clear (restores the starting order) and Done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 20:12:04 -04:00
ajandClaude Opus 5.5 f51e5aefdd feat(planning): keep support tabs out of inside corners and report weak tabs
The best tab point used to land in inside corners: equally good points
run along a straight edge, vertices were tried first and the first tie
won. Operators cannot grind a nub flush there.

- SupportAnalyzer finds inside corners (inward-turning vertex runs of at
  least 30 degrees, so chorded fillets count) and keeps tab points a
  clearance from them; of equally good points it picks the one farthest
  from an inside corner. Plan Cutting and the overlay use twice the tab
  size; AcceptsTabAt rejects closer points
- When no steadying entry has a lead-in that fits, the search falls back
  to entries clear of inside corners, then to every entry, each ordered
  by how well the tab steadies the part, keeping only the steadiest that
  fit
- Replay notes parts whose planned tab cannot hold them level or sits in
  an inside corner; the notes are advisory and leave a Ready plan Ready
- Console/MCP plate summaries print a ready plan's notes
- docs/support-tabs.md and cutting-planner notes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:18:16 -04:00
ajandClaude Opus 5.5 a7c0747e11 feat(planning): tab parts the laser support grid cannot hold level
Model the machine's support bed as a grid of contact points measured
from the plate origin, and check each part's center of gravity against
the convex hull of the grid points under its material. The bed is
extensible so straight and CAD-imported curved slats can follow.

- Core: SupportBed/SupportGrid, SupportMaterial and SupportAnalyzer
  (margin with registration tolerance, best single tab point)
- Nest-wide support bed that every plate follows, saved in
  plateDefaults.supportBed; machine defaults in support-grid.json
- CuttingParameters.SupportTabs: Plan Cutting tabs only unsupported
  parts and limits their perimeter entries to points where the tab
  steadies the part, falling back to any fitting entry
- Desktop: Nest > Support Grid..., View > Draw Support overlay and a
  "Tab parts the support grid cannot hold level" tab option
- docs/support-tabs.md, nest file format and planner notes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 16:55:24 -04:00
aj 3d4ac5ddf7 fix(planning): drop the desktop-only effort advice from console and MCP findings
CuttingPlanProposal.Describe is shown only by the console and MCP, which have
no Tools > Options; the desktop panel keeps its own effort hint.
2026-10-20 05:26:35 -04:00
ajandClaude Opus 5.5 56eaf4829a feat(planning): add a Cut planning effort setting that scales the search budget
Tools > Options > Cut planning effort (default 1.0, 0.5-10) multiplies the
desktop cut planner's per-plate expansion budget for both the free-order
search and the current-order retry. The budget counts expansions, not time,
so the setting is how faster hardware can be spent on hard plates.

The panel reads the setting on the UI thread and passes it to
CuttingPlanBatch.Capture; the worker never reads settings. The 1,000-
expansion first pass stays fixed: it is a cheap probe for easy plates, so a
higher effort only adds work where the full search runs. A plate refused
at the search limit now says the effort can be raised, without promising a
plan. The default keeps today's budgets and characterization digests.

Console, MCP and API do not run the cut planner, so they get no option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:54:28 -04:00
ajandClaude Opus 5.5 391a27b2f9 feat(console,mcp): add cut planning to the headless front ends
Console --plan-cutting and MCP plan_cutting/apply_cutting_plan run the
desktop's CuttingPlanBatch: capture, plan, then an all-or-nothing apply.
Settings come from an explicit JSON file or the plates' identical saved
settings, never defaults. Unverified plans need per-call acceptance,
which is not posting consent. Console --no-fill plans a loaded nest as
is; a blocked plan exits 2 and saves nothing. Scrap cut-offs stay
desktop-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:54:28 -04:00
ajandClaude Opus 5.5 cf32d1cab5 fix(cutting): scale the reorder stall with the blocked part, not the plate's largest
An attempt along a part order stopped backtracking after 8 x entries x
contours expansions without progress, with contours taken as the most of
any part on the plate. One many-holed part therefore let every blocked
part, even a two-contour tab, replay its predecessors' entries for the
whole of that allowance; a plate whose rounds each stalled that long
ran out of budget before the recovery pass that would have finished it.

Along an order the stall now uses the larger contour count of the blocked
part and of the part cut just before it, whose departure a retry changes,
capped by the old limit. The sequence-free recovery pass keeps the plate
maximum. Planning characterization digests are unchanged.

The optional private-fixture test now also runs RecoveryCuttingNestPath
with default lead-ins, and requires that file's plates to plan within
60% of their budget.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:54:28 -04:00
ajandClaude Opus 5.5 04a6e0eb50 fix(engine): let contour recovery plan scribed and boxed-in holed parts
The cut planner's directed-contour recovery pass gave up on plates the
full search could solve, so they ended "no plan found within the search
limit":

- Each hole was checked as an isolated probe that repeats the part's
  scribes, so every hole-to-hole hop was checked as a detour through
  the scribe start. Scribes and other open cuts are now checked once
  from the arrival, and each hole as a direct hop from the one before.
- Hole entries were ranked only by pierce distance, so the departure
  often ran back across the hole just cut. The nearest hole is still
  tried first, but its entries that leave toward the holes still to
  cut come first.
- The first-ranked outside entry could spend the whole recovery
  allowance. Each outside entry now gets a share of what is left.

The optional private-fixture test now takes its config key, adding
RecoveryCuttingNestPath; it fails on the old search and passes now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:54:27 -04:00
ajandClaude Opus 5.5 a0713289fa feat(planning): tour large aligned grids as a serpentine
A plate of at least 200 parts with no cutoffs or prerequisites, whose
part centres bin into rows and columns within a quarter of the smallest
part extent, is toured line by line in alternating directions. Each row
and column gap must be wider than that tolerance, each cell may hold
only one part, and at least a quarter of the cells must be filled
(interleaved rows fill half). The route runs along whichever axis, from
whichever corner, is shortest from the start point.

This replaces the travelling-salesman 2-opt/Or-opt passes, which took
about 8 s on 5,742 parts. It is still only a proposal: the joint search
checks every rapid and lead, and replans after a learned rule use the
general order. Smaller plates, and the characterization fixtures, keep
their order unchanged.

The washer nest's plate 1 now plans Ready in about 44 s (was 54 s), with
86k expansions against 91k.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:16:03 -04:00
ajandClaude Opus 5.5 40e4e559dd fix(planning): rank hole entries by a clear way out to the next pierce
With no lead-out the tool leaves a hole where it entered it. A tiny corner
arc's midpoint lies on both facing sides of its hole, so the ranker scored it
as an ideal two-side corner, but the tool leaves it along its 45-degree
tangent: a shallow rapid to a nearby next hole re-entered the corner and
crossed the hole just cut. Every outside endpoint's preferred hole chain
then failed the same way, and the reorder search spent its budget on full
backtracking. A 31-part plate of such parts needed 31,959 expansions
against a default budget of 20,000.

Hole entries whose straight rapid to the next pierce clears the hole itself
now rank first, in the preferred chain and in the full search. This is a
stable reorder only: no entry is dropped, and every emitted rapid is still
checked. That plate now plans Ready on its first preferred pass, using 7,781
expansions; the budget is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:16:03 -04:00
ajandClaude Opus 5.5 b9c5240a43 fix(planning): close incremental rounding drift at contour seams
PEP-converted incremental programs round every move, so a long contour
can end 1-1.4e-6 off its start. Material capture refused it as open
("blocked: unsupported geometry"), and a gap right at the 1e-6 boundary
could pass material capture yet fail preparation.

On the planner's owned clean copy only, close incremental contours whose
gap exceeds 1e-8 but stays within what their rounded moves could
accumulate (capped at Tolerance.Epsilon); the next motion absorbs the
correction so nothing later moves. Absolute programs, larger gaps and
the drawing itself are untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:16:02 -04:00
aj 1ed66bbf20 fix(ui): point operator messages at commands that exist
Pinned-lead and locked-lead warnings named a Drawing Pins Off command and a
Clear Lead-ins command that the desktop does not have; name Plate > Remove
Lead-ins and spacing instead. Closing Plan Cutting no longer claims nothing
changed after lead-ins or cut-offs were cleared, the no-stock reason no
longer says the part is larger than every sheet when only the usable area at
the checked rotations is too small, and the console says whole-job engines.
2026-10-19 12:15:40 -04:00
ajandClaude Opus 5.5 a1458a355b fix(cutting): route best-effort holes instead of cutting them in source order
The best-effort fallback (used whenever a part has touching or uncertain
material boundaries, which sends the whole plate there) cut each part's holes
in raw source order with the nearest entry. On hole grids that order
doubles back, so the rapids between holes cross; any crossing means a
shorter uncrossed order exists. On A0228-01 plate 1 this cost ~300 in of
rapid travel and drew crossing rapids on PT08 in the simulator.

Tour the holes with CuttingHoleOrder (the nearest-neighbour + 2-opt/Or-opt
proposal the strict search already uses), as an open path from the arrival,
perimeter still last. Hole-centre selection is shared with the strict search.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:08:51 -04:00
ajandClaude Opus 5.5 baa5f8481a fix(cutting): plan cut-first parts before every other part
Classic sequencing honored Drawing.CutFirst, but the cutting planner built
its own order from the dependency graph and ignored it, so test coupons
interleaved with the rest of the plate. Each cut-first part is now a
prerequisite of every non-cutoff part, except a part nested in its cutout,
where containment wins so the graph stays acyclic. A kept order that cuts
a part before a coupon reports the cut-first conflict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 08:45:47 -04:00
ajandClaude Opus 5.5 5dc13a03ae feat(diagnostics): warn when parts are closer than the part spacing
Validate Nest, the posting review and console --post now report a
"Part spacing" finding for pairs of parts that do not overlap but sit
closer than the plate's part spacing. Before, only Auto Nest's
Keep/Discard review checked spacing, so hand-edited layouts could be
validated and posted with parts too close together.

Candidates are found with Auto Nest's own outline test, moved into Core
as SpacingOutline (NestLayoutCheck and NestTolerances now use it; Engine
tests unchanged). Because that outline circumscribes arcs by more than
the spacing slack, each candidate is confirmed on native contours
flattened 20x finer than the slack, so arcs placed exactly at the
spacing by interactive fills stay clear. The finding reports the
measured gap and marks the middle of the closest approach. Overlapping
pairs, cut-offs, zero spacing, the plate edge and clamps are not
reported as spacing findings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 01:56:27 -04:00
ajandClaude Opus 5.5 93cf8007bc feat(cutting): pierce perimeters in open scrap when no lead-in keeps the pierce zone
With a pierce diameter set, a perimeter none of whose configured lead-ins keeps its pierce
clear (a lead shorter than the zone radius, or neighbours closer than it) was only rejected.
Plan Cutting now looks for a pierce in the open scrap around the part instead.

- ScrapPierceSearch (Engine): the window four zone radii around the part, minus every nearby
  part's clean material grown by the zone radius and a margin (ClipperBridge, conservative
  flattening); the clear point nearest each ranked perimeter entry is a candidate. Other
  parts' cutouts count as scrap, the part's own holes do not. With no clear scrap, the
  PolyLabel pole of the free scrap is the best effort, and its shortfall is reported.
- ContourChoice.Pierce / PreparedContours.ScrapPierceEntry: the planner's choice carries the
  pierce; the emitter cuts a straight lead from it to the nearest perimeter point and still
  reads nothing but the contour and the choice.
- Every candidate passes the lead validator like any planner-chosen entry; pinned perimeters
  keep their pins and warn. Without a pierce diameter nothing changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 01:56:27 -04:00
ajandClaude Opus 5.5 6676f9f6f1 feat(cutting): keep lead-in pierces clear of material with an opt-in pierce diameter
Thick plate pierces leave a crater wider than the kerf. CuttingParameters.PierceDiameter
(Cutting Settings > Pierce Diameter, 0 = off) requires every lead-in pierce to keep half
of it from all part material: its own contour, its holes and every other part.

- LeadPathValidator checks the zone during Plan Cutting candidate checks and final
  replay; fixed emissions warn as usual. No-fit findings explain the zone.
- PierceZoneReview reports Pierce zone warnings before posting for leads from any source.
- Holes whose configured pierce is inside the zone move it toward the hole's PolyLabel
  pole (round holes: the centre), as close to the contour as the zone allows; a hole
  too small for the zone is pierced at its pole and the shortfall is still reported.
- Straight corner leads and round-hole clamping use the larger of PierceClearance and
  the zone radius. The manual lead-in preview shares the Core rules.
- Persisted through the DTO, owned copies and state fingerprint.

Perimeter fallback is not included; see docs/geometry/pierce-zone.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 01:56:27 -04:00
ajandClaude Opus 5.5 00acaeaaf1 feat(engine): report best whole-job plans from Rectangles, Circles and Default
Desktop Auto Nest's "Best plan so far" strip now fills in for every built-in
whole-job engine except FlatBar, not only Irregular.

- Rectangles makes one sheet-by-sheet run, so it reports the finished job as
  its one plan, then replays its plate commits (token checked as before).
- Circles reports when a stock-preference run or a tail re-plan beats its
  best result so far.
- Default scores each candidate plan as it ranks results (valid, fewest
  unplaced, lowest cost, candidate order) and reports it only when it beats
  every plan already shown, so a later candidate never shows a worse plan.
  The chosen result is always the last plan reported.
- Shared ReportPlan/SameLayout helpers on CandidateProgress; Irregular uses
  them too. Reporting never changes the search.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:56:38 -04:00
ajandClaude Opus 5.5 cd2bea054b feat(desktop): show Auto Nest as a sheet strip with a Keep/Discard review
Auto Nest no longer draws parts on the plate view before they are kept:
its result is a set of sheets, possibly of several sizes, not parts on the
current plate. Ghost parts stay with interactive Fill and remnant tools.

- NestJobStage.PlanImproved carries an engine's best whole-job plan so far;
  Irregular reports one whenever a variant or the tail re-plan beats it.
- NestPipeline adds PlanSheets: the plan on detached drawings built from
  the job geometry (with the caller's names and colors), so previews never
  touch caller drawings or quantities.
- SheetStripControl draws every sheet side by side at one scale, repeats
  once with their count, captioned with size, parts and utilization.
- The progress window shows "Best plan so far"; a review window shows the
  final sheets, totals, cost summary and unplaced parts before commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:56:38 -04:00
aj a2fb945669 Revert "feat(engine): live sheet preview while Irregular packs"
This reverts commit 9f0e536eeed85e4417f285b94913e03f594400f7.
2026-10-18 23:56:38 -04:00
aj b11225135e Revert "feat(desktop): draw Auto Nest previews on the trial sheet"
This reverts commit bf42cdc63d5e475fa6921a3ddac06d7a39152bec.
2026-10-18 23:56:38 -04:00
ajandClaude Opus 5.5 d384e63068 feat(desktop): draw Auto Nest previews on the trial sheet
NestProgress gains PreviewSheet, a detached plate for the stock the preview
parts lie on; Irregular builds one per stock. PlateView draws it (size, edge
spacing, clamps) in place of the plate while the preview is shown and drops
it whenever the preview is cleared or the plate changes. Auto Nest zooms out
when a trial sheet falls outside what it has already shown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:56:38 -04:00
ajandClaude Opus 5.5 c997b2e588 feat(engine): live sheet preview while Irregular packs
The frontier packer reports its placements after each choice; the engine
turns them into preview parts at most four times a second and sends them as
NestProgress.BestParts with the stock work area, which desktop Auto Nest
already draws on the plate view. Reporting only reads finished placements,
so layouts are unchanged with or without a progress sink.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 23:56:38 -04:00
ajandClaude Fable 5.1 e6c87df372 perf(engine): pool best-fit pair searches by part geometry across jobs
Every Auto Nest run, and every Default-engine run, recomputed each part's
best-fit pairs: job snapshots are fresh objects and BestFitCache keyed
its entries on Drawing reference, so the Irregular engine paid
PairCatalog.Build (about half of a 118-part solve) on every run of the
same drawings.

BestFitCache now keeps a process-wide, bounded (512 sets, least recently
used first) pool of unfiltered candidate sets keyed by the exact content
of a PartGeometrySnapshot (mode and every motion value, bit for bit;
PartGeometrySnapshot.ContentComparer) and the spacing. Every drawing
DrawingJobMapper.CreateDrawing reconstructs from a snapshot declares
that geometry (BestFitCache.ShareByGeometry), so the Irregular pairs and
blocks, the cutout lattice and the Fill/Default job paths all read and
fill the pool. Candidates depend on the program geometry and spacing
alone, so a pooled set is the set the search would produce: benchmark
layouts, costs and placements are unchanged on the synthetic nests (all
engines) and on the 67-drawing, 118-part job. Failed searches are not
pooled, Invalidate leaves the pool alone, Clear empties it, and a
drawing whose Program is replaced stops vouching for its geometry.

Same 118-part job solved twice in one benchmark process (--parallel 1):
the second solve now takes 13.1 s after a 33.0 s first; before, 35.6 s
after 41.8 s.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 22:17:53 -04:00
ajandClaude Opus 5.5 13ab8e02f8 fix(cutting): plan with staged scrap cut-offs within budget
Staged cut-offs were appended after every part, so a kept order (the
"Keep the current part order" option, or the fallback after a free
search runs out of budget) always conflicted with any cut-off crossing
a part. PlateSequencing.PlaceCutOffs moves each new cut-off to just
before the first part it crosses, separators last, without moving
anything else; the panel applies it after staging.

The free search also learned "cut the cut-off first" only after a full
stall, re-planning the many-holed parts before it. A cut-off is a fixed
program that never gets harder to cut earlier, so a crossing approach
into a non-separating cut-off now teaches that rule at once. On the
reported 35-part plate this drops the plan from 23,930 expansions (over
the 20,000 budget) to 15,605.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:14:28 -04:00
ajandClaude Opus 5.5 fb505ef0ff fix(cutting): plan outlines with a seam gap before an arc, and memoize lead checks
A converted outline whose seam closes within the admitted 1e-6 gap could
only be entered at its seam: entered anywhere else, the arc after the
seam started off its own circle and every emission was refused as an
inconsistent radius. The search then spent its whole budget on that
part and the plate never planned. Emission now bridges the gap with a
straight move and cuts the arc as drawn; the verifier already admits
that slack. An entry on the seam vertex is left as before.

Each prefix of a part re-checked its earlier contours' leads against
every other part's material. One search now remembers each verdict by
the lead curve's exact bits and the material's reference, charging the
same native query budget; replay never uses it. On the reported plate
the memo alone gives a byte-identical result in 57 s instead of 12 min 47 s;
with the seam fix the plate plans Ready in about 17 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 21:21:20 -04:00
ajandClaude Fable 5.1 d317a90f1e perf(engine): cut Auto Nest time on many-part jobs
A 67-drawing, 118-part job took about six minutes through the desktop
Auto Nest path and was cancelled before any layout appeared. Almost all
of it was the cutout pre-pass, the rest the Irregular engine.

Cutout pre-pass (317 s -> 8 s on that job, same bundles and poses):
- The lattice and NFP searches prepare the frame's and the occupants'
  validation outlines once per search (NestLayoutCheck.Prepare) instead
  of re-outlining and re-triangulating the frame at every candidate, and
  test candidates against the small occupants before the frame. Answers
  match the pairwise check exactly, including its leftmost-inflated rule.
- A hole's proposal is replayed for the same frame, hole and offered
  inserts: automatic-rotation inserts ask the same question at every
  frame angle.

Irregular engine (40 s -> 15 s, benchmark 42 s -> 19 s, same layouts):
- Concave no-fit polygons are built as the union of pairwise convex sums
  of each footprint's convex pieces (ear clipping plus Hertel-Mehlhorn
  merging on the Clipper lattice) instead of n*m edge-pair quads; a
  footprint that cannot be decomposed keeps the old sweep.
- Pair and block catalogs share one private drawing per part geometry,
  owned by the engine instance, so each part's best-fit search runs once
  per instance and across a pipeline's trial and ordinary solves.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 21:13:15 -04:00
ajandClaude Opus 5.5 dd2c00aed4 perf(engine): replay cutout pre-pass clearance checks and trim hole subtraction
Profiling the cutout pre-pass on real round-part jobs put ~97% of its time in
the frame-wall NestLayoutCheck.Clears inside CutoutCirclePacker.Fill. Ear-clipped
round frames make every perimeter/insert triangle pair overlap, so a clear pair
runs hole subtraction for every pair. The same question repeats for every frame
angle and repeated frame instance, because Automatic inserts get identical
frame-local poses.

- CutoutMemo, one per Prepare, replays geometry reads, circle items and Clears
  answers. Keys hold geometry by reference, pose bit patterns, argument order
  and spacing. It is threaded through CutoutCirclePacker.Fill and the router's
  round-only path.
- TriangulatedRegion.SubtractAllHoles measures each piece's bounds once, skips
  holes and hole triangles that no piece reaches, and ping-pongs the remainder
  between scratch buffers instead of copying it into pooled ones. Clip
  arithmetic and order are unchanged.

Bundles are byte-identical on 14 real and 10 synthetic ring jobs. Pre-pass time
over the real set drops from 99.6 s to 10.1 s (N0414-02 35.8 s -> 0.6 s).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 15:36:02 -04:00
ajandClaude Opus 5.5 f01b2cec67 perf(engine): speed up round cutouts and re-plan Circles' last sheets
- The cutout router uses the circle packer alone for a round hole whose
  inserts are all round. Lattice and NFP never placed more there, and when
  demand exceeded what fit they spent 20+ minutes on one job.
- The cutout pre-pass skips a hole whose area is smaller than an insert's
  outline, so bolt holes no longer run the searches.
- Circles re-packs its last sheet, and its last two sheets together, onto
  each single stock and keeps the result when the whole job gets cheaper.

On 14 real round-part jobs costs are unchanged and pipeline time drops from
262 s to 144 s for Circles and 355 s to 245 s for Default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 14:54:01 -04:00
ajandClaude Opus 5.5 278252fe79 feat(console): allow multi-sheet autonest with --max-sheets and --stock
--autonest can now use more than one physical sheet. The first layout
replaces the target plate's parts, further layouts are inserted as new
plates after it, and --stock offers explicit sheet sizes (with optional
costs) built from the target's spacing, quadrant and clamp rail.
NestPipelineCommit gains ApplyToPlates for callers without a
PlateManager. Per-sheet results and overlap checks cover every
committed plate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 13:27:14 -04:00
ajandClaude Opus 5.5 9f2e3d5291 feat(engine): compare clamp-hit dropping with filling past the clamp strip
Dropping the parts that land on a clamp suits small parts, which fill the
gaps between clamps. Large parts often do better if the fill area stops
short of the clamps. The rows still fit, and no part is lost on a clamp.

When every clamp in the fill area lies on one edge, interactive Fill now
also fills the area with that edge moved just past the clamp keep-outs,
as a larger edge spacing would. It keeps whichever layout the strategy's
comparer ranks better, and a tie keeps the dropped-parts layout. Clamps
on more than one edge skip the comparison. The second fill reuses the
cached best-fit pairs. While it runs, a preview counts as the new best
only when it beats the first layout, so the preview never gets worse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:49:48 -04:00
ajandClaude Opus 5.5 05aac952ba feat(engine): fill clamped plates ignoring clamps, then drop clamp hits
Interactive Fill on a plate with clamps used to split the plate into
clamp-free rectangles and fill each one separately. It showed no progress,
so Accept did nothing until the end, and Stop discarded the result. Packing
around the clamps also tends to give a worse layout than losing the few
parts that land on them.

FillItem and FillGroup now fill the area as if there were no clamps. They
then drop each part that enters a clamp keep-out or sits too close to an
existing part, using the same check as before. For a multi-part group, the
whole copy is dropped when any member fails. Progress previews are filtered
by the same check, so a layout accepted early is clamp-clear. A quantity
request that loses copies asks for that many more, up to two more times.
Pack and whole-plate Nest still pack only into the clamp-free areas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:49:48 -04: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
ajandClaude Opus 5.5 de845a5818 feat(engine): add Circles engine for round parts
Circles packs every part as its minimum enclosing circle at centers tangent
to two of the work-area walls, placed discs and clamp keep-outs, bottom-left
and left-bottom, with hexagonal and square lattice seeds. It bids only when
every part fills at least 0.8 of its enclosing circle and otherwise returns
everything unplaced at once. Default runs it as a third candidate.

Rectangles' sheet-by-sheet stock loop moves to NestingEngines/SheetByStock
(Rectangles output unchanged) and Default's candidate scoring and commit
forwarding become shared helpers. Circles also runs the whole job preferring
each offered stock and keeps the best result. Contract tests take shapes an
engine can override.

Synthetic round jobs, salvage-credited: Circles wins or ties all six with
valid layouts; Default cost falls about 4% versus Irregular alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:49:48 -04:00
ajandClaude Opus 5.5 a430da89d0 feat(engine): nest round frames inside larger round frames
The cutout prepass now bundles frames smallest first, so a larger frame's
round cutout can take a ring part or an earlier bundle as one insert through
the circle packer: a flange holds a washer that holds a smaller washer that
holds a slug. Chains stop at four parts deep. Lattice and NFP proposals never
take these inserts, since they could seat a part in a ring's bore.

Expansion and released demand recurse, so placed order cuts innermost first
at every level and an unplaced outer frame returns its whole chain to demand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:49:36 -04:00
ajandClaude Opus 5.5 f3a6af7f09 feat(engine): pack round inserts into round cutouts as circles
Add CutoutCirclePacker, a circle-in-circle proposal for the cutout
router. When a cutout is one circle and every insert fills at least 0.8
of its enclosing circle, it packs the inserts largest first at centers
tangent to the wall or placed discs, trying a center-out pass (a single
insert sits concentric) and a wall-first pass (optimal counts for up to
nine equal discs). Every pose passes NestLayoutCheck.

The router runs it first and skips the lattice and NFP searches when it
places all demand; otherwise the higher count wins and existing
proposals keep ties. On synthetic disc jobs the prepass drops from
minutes to seconds and places as many or more inserts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:49:36 -04:00
ajandClaude Opus 5.5 2174416dcc feat(engine): model any part as a circle for circle packing
Item.Create builds the circle packer's view of a part: the minimum
enclosing circle of its outward-flattened perimeter (padded by the
layout check's flattening allowance), its fill ratio, the offset back
to the part origin, and the largest disc clear of material inside its
cutouts. Gears and sprockets pack by their tip circle around their
bore; the fill ratio lets a future engine decide which jobs to bid on
instead of a yes/no round test.

Adds MinimumEnclosingCircle (Welzl, repeatable order) to Core geometry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:49:36 -04:00
ajandClaude Opus 5.5 6e2e27d6fc fix(engine): stop staggered circle fills overlapping
Staggered stripes were spaced only so adjacent stripes touched
diagonally. When the in-stripe pitch stretched past sqrt(3)*D, stripes
two apart, which sit directly over each other, came closer than a
diameter. Clamp the stripe spacing to at least a radius in both fills.

FillEndOdd also divided by zero when a single circle fit per stripe;
it now stacks those stripes a diameter apart. FillEndEven now clones
the template item, so placed circles keep its radius instead of zero.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:49:36 -04:00