Commit Graph
1961 Commits
Author SHA1 Message Date
ajandClaude Opus 5.5 6e40c2e6ed feat(library): redesign Edit Drawing with drop-downs, color modes and a rotation dial
Typing customer, material, grade and thickness by hand let near-duplicate
spellings into the library, which then split drawings that should match.
The fields are now drop-downs of the values the library and the machine
material catalog already use, while still accepting new values; thicknesses
already used for the material are listed ahead of the standard gauges.

A library color could be set but never cleared back to the color scheme's,
so Automatic and Reset are added. The rotation start/end/step boxes gave no
sense of the range, so a dial shades it, ticks the angles the engines try
(via RotationPolicy) and lets either end be dragged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:46:43 -04:00
ajandClaude Opus 5.5 0ba5539fab fix(display): keep cut-off lines from striking through segment ids
Cut-off lines are painted after the parts, so the dashed line ran straight
through each segment's id, which sits on the cut. Cut-off segment ids are now
held back from the parts pass and drawn after the lines, over a disc of the
plate's fill color.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:46:43 -04:00
ajandClaude Opus 5.5 2e17dd7bc4 fix(desktop): size grid columns before they are first painted
Grids opened at their designer widths and jumped a moment later, because
GridStyle waited for the first paint with rows before measuring them.
Columns now measure their header and first 50 rows directly: at handle
creation when the rows are already there, otherwise in a call posted when
rows arrive, which runs after the filling code and before the repaint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:46:42 -04:00
ajandClaude Opus 5.5 86b0370a8f fix(names): drop S from generated nest names
S reads too much like 5 on labels and screens, so the sequence alphabet is
now base-29. Already-issued names keep their numbers: the server's stored
counter stays authoritative and a base-29 name can never equal an earlier
base-30 one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:46:42 -04:00
ajandClaude Opus 5.5 e5e2c268b8 feat(ui): redesign the Nest Defaults dialog
The flat ten-row list squeezed the quadrant picker to 120x28 and gave no
hint of units or where the defaults are saved. Group the fields (General,
Plate Size, Quadrant, Spacing), lay the edge spacings out around a plate,
show in/mm suffixes, and convert the values when the units switch so a
48 in plate does not silently become 48 mm.

The footer says whether Save writes to this PC or the shared nest server,
and Save is disabled while the shared copy is read-only instead of
failing after the click.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:46:42 -04:00
ajandClaude Opus 5.5 7f1c5a3854 fix(display): label each cut-off segment at its own cut
Every segment of a cut-off is its own part of the one cut-off drawing, but
its sequence label was placed from the drawing's whole program, so all of a
cut-off's segments stacked their numbers on one point. The view culls parts
outside the visible area, so zooming changed which segment drew last and the
shown number appeared to change. Each segment is now labelled at the middle
of its own cutting moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:46:41 -04:00
ajandClaude Opus 5.5 96d8a6874f fix(autonest): show part rotation limits in degrees
The Rot Start, Rot End and Step columns showed the drawing's radian
constraints as-is, so users had to type 1.5708 for 90 degrees. The grid now
shows and accepts degrees; rows keep the radian values, so an unedited row
passes the drawing's limits to the engine without a conversion round trip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 07:10:59 -04:00
ajandClaude Opus 5.5 fecfcc337e build(help): ship help v0.4.0 with the shared nest server, Drawing Library, drawing sets and releases guides
The server and Database-mode pages were written after v0.3.0 was tagged,
so builds pinned to v0.3.0 had no help for setting up or connecting to a
nest server, or for the library, sets and releases that depend on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 07:01:55 -04:00
ajandClaude Opus 5.5 b0697f92b3 feat(cutoffs): keep large horizontal remnants in automatic scrap cut-offs
Strip lines now stop at a full-length horizontal cut-off that clears the
parts instead of slicing the band beyond it. The desktop dialog and Plan
Cutting also propose their own horizontal separator when that band beats
the X tail (opt-in for API callers via KeepHorizontalRemnant); a kept X
tail beside the band becomes a corner piece whose separator stops there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 00:20:34 -04:00
ajandClaude Opus 5.5 98f8e98230 fix(cutting): curved leads meet the cut tangentially; arc lead-in sweep
Curved leads turned with the contour winding, which is backwards on outside
perimeters: arc lead-ins arrived against the cut, and Line + Arc / arc
lead-outs went the long way round on one winding. LeadTurn now gives the
arc's turn direction for emission, the pierce-zone check and the preview,
and the arc geometry mirrors with it.

At convex outside corners, curved lead-ins use the first-cut edge's normal
and arc lead-outs the last-cut edge's, whichever edge was picked.

ArcLeadIn gains SweepAngle (degrees, (0, 180], default 180) in the cutting
panel, settings, nest files and cut pins; older files read as 180.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 23:50:30 -04:00
ajandClaude Opus 5.5 5611d9a663 refactor(engine): one PlateNester and one strategy name table
DefaultPlateNester, StripPlateNester and RemnantPlateNester differed only
in the filler they built. They are replaced by one internal PlateNester
over a Func<Plate, PlateFiller>, which keeps the old Place exactly:
restricted rotation goes to OrderedPlateNester, and each trial gets a fresh
private plate and filler.

PlacementStrategies maps Fill (and its legacy "Default" alias), Strip and
both Remnants to a filler policy and layout. PlateNesterFactory reads it
case-sensitively and PlateFillService ignores case, as before; each keeps
its own error type and message. Nothing outside the Engine and its tests
used the three public nester types, so they are removed.

No layout changes: all 29 fill-stack pins pass with the pin file untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 23:44:51 -04:00
ajandClaude Opus 5.5 5761eefd37 refactor(engine): merge the plate fillers into one PlateFiller
DefaultPlateFiller becomes PlateFiller(plate, policy, layout); PlateFillerBase
and StripPlateFiller are gone. PlateLayout.FillThenPack is the old
orchestrator path (Fill and both Remnants); PlateLayout.Strips is the old
StripPlateFiller.Nest, and Strip is Fill policy plus Strips layout.

Lifetimes are unchanged: a strip-layout filler runs every Fill, group Fill
and PackArea on a fresh inner filler and gives each strip fresh remnant
fillers, so learned angles and PlateNumber never leak into them. The
comparer, preferred direction, trim axis and angle recording now read the
policy directly. The base class's unsorted two-angle BuildAngles is dropped:
only DefaultPlateFiller's pipeline called BuildAngles, and Strip never ran it.

Layout-neutral: all 29 FillStackCharacterizationTests pins pass unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 23:44:51 -04:00
ajandClaude Opus 5.5 9f335ea111 refactor(engine): fold remnant fill settings into one filler policy
Fill, Vertical Remnant and Horizontal Remnant are now three values of an
internal PlateFillPolicy that DefaultPlateFiller takes (Fill by default),
replacing RemnantPlateFiller and RemnantFillPolicy. The policy holds the
comparer factory, preferred direction, trim axis and angle mode; the
remnant angle mode still replaces the angle builder outright, and the
public FillPolicy that strategies read is unchanged. Layout-neutral: the
fill-stack characterization pins pass unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 23:44:51 -04:00
ajandClaude Opus 5.5 0c7ad5aefb refactor(engine): list the built-in fill strategies instead of scanning
FillStrategyRegistry now holds a fixed, ordered list of the seven
built-in strategies (Pairs, Row, Column, RectBestFit, Extents, Linear,
TiltedRemnant, in their Order sequence) instead of reflecting over the
Engine assembly. LoadFrom and LoadPlugins are gone; nothing outside
tests called them, so outside IFillStrategy plug-ins were never loaded
by a shipped host and none are loaded now. IFillStrategy stays public.

The disabled set stays process-wide and keeps its Disable, Enable,
SetEnabled, Strategies, AllStrategies and DisabledNames behavior for the
Options form. DefaultPlateFiller still reads Strategies, so interactive
Fill, the fixed-strategy engines, Irregular's block catalog and private
plate fill, the cutout lattice fill and the brute-force runner all keep
honoring it.

PlateViewFillLifetimeTests drives fills through PlateView, which builds
its own filler, so a filler-level seam can't reach it. The registry gets
an internal UseForTesting hook that swaps the list until disposed, and
the WinForms test project can see Engine internals for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 23:44:50 -04:00
ajandClaude Opus 5.5 73a7c8d6a2 feat(desktop): drop a set's shared name prefix from preview labels
When every drawing in a set starts with the same words, as in
"4980 A111 PT01", the set preview's labels were all cut short to the
same "4980 A111 P…". The preview now drops the leading words all names
share, cutting only at a space, '-', '_' or '.' and never emptying a
name, and notes the dropped prefix above the drawings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 22:39:37 -04:00
ajandClaude Opus 5.5 479883e7ac feat(cutting): tab parts by width when the plate has no support bed
With support tabs on and no support bed, nothing was tabbed and nothing
said so. A new no-bed width range (NoBedTabMinWidth..NoBedTabMaxWidth,
the short side of the part's minimum-area bounding rectangle) now picks
the parts to tab instead. SupportTabSettings.Analyze makes the choice
for Plan Cutting, hand-placed lead-ins and the pre-post review; without
a bed any tab clear of inside corners counts. With no bed and no range,
the plan notes that nothing was tabbed. Cutting Settings > Tabs gains
the range fields, and the values persist with the cutting parameters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 22:02:31 -04:00
ajandClaude Opus 5.5 07499fe1cd fix(engine): judge part spacing alike in the layout check and runner
NestLayoutCheck rejected Fill layouts whose interlocked arcs sat exactly
at the part spacing: its SpacingOutline circumscribes arcs by up to 0.001
each, more than the 0.0005 slack, so facing arcs overlapped. The runner's
placement validator measured inscribed 0.001 polygons instead and so
accepted gaps up to ~0.002 under the slack.

Both now find candidates with SpacingOutline and confirm them on
NativeContours, the native contours flattened at a twentieth of the slack
(moved out of PartSpacingReview, which keeps using them). The runner calls
NestLayoutCheck.KeepsSpacing, the same outline, tie rule and
confirmation, so the two validators accept exactly the same pairs.
Clears stays unconfirmed and stricter for engine certification. No
tolerance changed.

FixedStrategyEngine_IrregularJob pins now assert LayoutAssert.Valid with
unchanged digests; the NativeU 70-part grid's 28 reports were the same
cause and its test now requires none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 21:58:41 -04:00
ajandClaude Opus 5.5 1b1f86085d fix(engine): keep best-fit pair candidates in a stable order
BestFitFinder gathered each rotation strategy's candidates, and PairEvaluator
gathered evaluated results, in ConcurrentBags, so their order depended on which
worker finished first. The stable area sort then broke equal-area ties in that
arbitrary order, and fills, which try candidates in order and keep the first of
equal layouts, could nest the same parts differently between runs.

Both now fill index-addressed slots: candidates keep rotation-angle and
slide-offset order, results keep candidate order, and ties break the same way
every run. The candidates produced and their scores are unchanged.

The Strip whole-job pin in FillStackCharacterizationTests is re-pinned
(A66602F707290A36 -> FD03A4529D37E32C). That job ties between best-fit
pairs; the old pin recorded whichever tied pair the bag happened to return
first. The new layout places the same 26 parts, passes LayoutAssert.Valid
and gave the same digest in five separate test processes. The other 28
pins are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 21:58:34 -04:00
ajandClaude Opus 5.5 36c8de825e fix(engine): route restricted-rotation Strip plates to the ordered nester
StripPlateNester sent every request to the strip fills, which try two
classification angles and ignore rotation bounds. With a fixed or bounded
rotation at quantity above one they proposed forbidden poses, the job
validator threw "Candidate rotation is not allowed for the requirement."
and the whole Strip job failed. Strip now uses the same guard as
DefaultPlateNester and hands such plates to OrderedPlateNester.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 21:13:39 -04:00
ajandClaude Opus 5.5 ccd361dfa9 test(engine): pin Fill-stack layouts on irregular parts
Characterization digests for the interactive PlateFillService surface and
the fixed-strategy whole-job engines on L-shaped, notched, arc, bar and
chevron parts: single-item fills, whole-plate nests (including one where
learned angles change the layout, guarded by a full-sweep comparison),
fills beside clamp zones, and one job per Fill/Strip/remnant engine.
They let the planned filler/nester consolidation be shown layout-neutral.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 21:07:20 -04:00
ajandClaude Opus 5.5 7c4276a457 refactor(engine): retire the StockLadder engine
Every whole-job engine chooses among the offered stock sizes, and on the
synthetic benchmark nests Default matched or beat StockLadder on all 18
runs (saved sizes, a six-size pool, with and without salvage credit),
costing 30-40% less in total with mixed sizes. Its sheet-swap step never
produced a win over Irregular's tail re-plan.

The registry maps "StockLadder" to Default so saved selections, scripts
and API requests keep working. OrderedPlateNester stays: Fill and the
remnant strategies use it for restricted rotations, and its golden layout
is now pinned through NestJobRunner with the parts requested largest
first, as StockLadder requested them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:24:37 -04:00
ajandClaude Opus 5.5 14ba2e653b docs: describe the settings shared through the nest server
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:02:22 -04:00
ajandClaude Opus 5.5 18612e698d feat(desktop): use the server's shared settings in Database mode
Machines, post settings, clamp defaults and nest defaults come from the
nest server once it holds them, and from this PC's files until then; saves
go where the settings were read. The server's copy is re-read when older
than two minutes, before every post and when Machine Configuration opens.
While the server is unreachable the last copy read is used, the user is told
once, and nothing shared can be changed.

Posting re-reads the post's shared settings; saving them is skipped when the
dialog changed nothing, and a conflict loads the other workstation's
settings and stops the post. On first start the desktop offers to upload the
settings the server lacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:02:22 -04:00
ajandClaude Opus 5.5 90e2fd2e6e feat(server): share machines, post settings, clamps and nest defaults
Schema v13 adds shared_settings and shared_setting_revisions behind
/api/settings: one revision-guarded JSON document per kind and key, with
every revision (deletions included) kept with who saved it. Recreating a
deleted document continues its revision count.

OpenNest.Data gains the client (RemoteSharedSettings), a synchronous store
that keeps the last copy read for offline use and refuses writes while the
server is unreachable (SharedSettingsStore), a machine provider over it, and
JSON parse/serialize entry points for clamp settings and nest defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:02:08 -04:00
ajandClaude Opus 5.5 9f3c22dc4f feat(posts): let posts load and save their settings as JSON
IJsonConfigPostProcessor lets a host keep a post's settings somewhere other
than the JSON file beside the post DLL. The CL-707, CI Fiber and Gravograph
posts implement it; LoadConfig keeps the current settings when the JSON is
unusable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:02:08 -04:00
ajandClaude Opus 5.5 84e6adbcf2 fix(data): give the CL-980 default a 0.02 cut-off part clearance
Plates now take cut-off settings from the nest's machine, so the
default's 0.25-0.75 clearances replaced the 0.02 operators expect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:29:59 -04:00
ajandClaude Opus 5.5 b35490addf test(planning): hold problem nests to a cut-off baseline
Nests with a "planning with cut-offs:" line are planned again after clearing
applied work and adding default scrap cut-offs. OPENNEST_PROBLEM_NESTS_CUTOFFS=1
plans every nest that way to record new lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:17:51 -04:00
ajandClaude Opus 5.5 1fbe57a841 fix(data): match machine thicknesses rounded to three decimals
PEP stores 3/16 as 0.188, which missed the machine's 0.1875 at the
0.0005 in tolerance; widen it to 0.001 in, still far below gauge steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:15:03 -04:00
ajandClaude Opus 5.5 1bb58117af fix(desktop): fit the set preview at the largest scale the pane allows
The set preview guessed row widths and settled the scale in four rounds,
which could stick at rows of minimum-width label cells and leave much of
a narrow pane empty. At a given scale, filling each tallest-first row as
far as the pane allows is the best row split, and the packed height only
grows with scale, so the layout now bisects for the largest scale that
fits. A 21-drawing set in a 400x590 pane goes from scale 1.14 with 345 px
used to 2.23 filling the height.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:04:58 -04:00
ajandClaude Opus 5.5 70e8666335 feat(posts): give the Cincinnati CL-707 settings the CI Fiber sectioned editor
The CL-707 config now declares sections, sentence-case labels and ranges,
so its dialog matches the CI Fiber one instead of the generic PropertyGrid.
The sectioned editor gains what the CL-707 config needs: optional (nullable)
numbers behind an enable box, editable tables for lists of simple records,
and a text field that suggests values from a table column (Selected library).
"Lead-in arc line 2 feedrate" is relabeled "Lead-in arc feedrate", which is
what #127 drives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:04:57 -04:00
ajandClaude Opus 5.5 2d8d877482 feat(server): keep a nest's units on its record
A record's thickness is in the nest's units, but nothing on the record said
which, so a list of nests could not tell 9.525 mm from 9.525 inches without
reading each archive. Records now carry units, read from the uploaded
archive like the counts (absent or unknown values are inches) and kept by a
metadata-only save. Schema v12 adds the column and reads every stored
archive, current and historical, so existing millimeter nests report it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:40:43 -04:00
ajandClaude Opus 5.5 610b9dafcb fix(autonest): mark ledger substitutes against the best rank matched
An acceptance rule may list sheet grades first, so at plate thickness its
best match can be rank 3 (A36 for HR). Auto Nest marked every row above
rank 1 a substitute; it now ranks rows against the best rule rank ML
matched at the nest's thickness, as ML labels them, so A36 is preferred
and only A572 is a substitute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:20:11 -04:00
ajandClaude Opus 5.5 079dfaf9ea fix(desktop): count only active MaterialLedger holds and link the nest page
MaterialLedger's Release lets a hold go, so a Released hold no longer keeps
a sheet for the nest; only Proposed, Approved and AwaitingCloseout holds,
the statuses ML counts against free stock, are open. The Has Been Cut
note's link pointed at /Nests/{id}, which ML does not serve; its nest page
is /Nests/Details/{id}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:20:11 -04:00
ajandClaude Opus 5.5 c27188c722 perf(desktop): reuse cached part paths when zooming and dragging
Each zoom step rebuilt every part's display paths from its CNC program,
and each drag step rebuilt the moved parts, which dominates for parts
with hundreds of holes. LayoutPart now keeps its paths in the drawing's
program frame and only maps them to the view on a zoom or move,
rebuilding when IsDirty is set or the program, rotation or manual
lead-ins change. Also caches the label StringFormat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:11:08 -04:00
ajandClaude Opus 5.5 548522775f fix(server): report a nest's units with its sheet sizes
GET /api/nests/{id}/sheets returns sizes as stored, in the nest's units,
but did not say which. MaterialLedger compares them with inches, so a
millimeter nest's sheets never matched inventory. The response now carries
units (inches or millimeters), read the way NestReader reads them: absent
or unknown values are inches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:41:29 -04:00
ajandClaude Opus 5.5 be01124a0f feat(autonest): rank MaterialLedger stock and mark substitutes
MaterialLedger's /api/stock/opennest now resolves an OpenNest material
and grade through ranked acceptance rules. The client reads the new
spec rank, family, grade and finish and the item rank; an older ledger
that sends none of them deserializes as rank 1 with no label.

LedgerStockMapper groups drops only by identical size and spec, keeps
the ledger's order in rows and in each row's ItemCodes, and orders rows
by rank. Auto Nest's Source column shows the row's grade and finish
("A1011 P&O"); substitutes (rank above 1) read "A1011 P&O for HR" in
amber, name the substitution in their tooltip and still load ticked.
The engine still sees no rank.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:45 -04:00
ajandClaude Opus 5.5 f847b216f2 feat(desktop): show a server nest's MaterialLedger holds and closeout note
Server nests in Database mode get a read-only status bar label comparing their
open MaterialLedger holds with the sheets they cut, e.g. "Ledger: 2 of 4 held,
2 short". Nothing writes to the ledger and nothing waits on it.

- MaterialLedgerClient.GetHoldsAsync reads GET /api/stock/holds?openNestNestId=
  with the closed LedgerHoldsResult set; unknown status strings are kept. The
  request plumbing is shared with GetStockAsync.
- LedgerHoldSummary (OpenNest.Data) counts Proposed/Approved/Released/
  AwaitingCloseout holds against the plate quantities, skipping the trailing
  empty plate as /sheets does.
- NestHoldStatus refreshes on server open, after save, after storage or
  Options changes and on label click; it never polls. The tooltip lists holds
  in nest units. File-mode, unsaved and ledger-less nests show nothing.
- Setting Has Been Cut in Nest Info with holds still open shows a non-modal
  note linking to {ledgerUrl}/Nests/{id}. Posting is not gated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:45 -04:00
ajandClaude Opus 5.5 23f093fcc1 feat(nest): plates report the remnant they are expected to leave
Core PlateRemnant.Expected picks a full-span cut-off drop clear of the
parts (less kerf, nominal position for head-locked overcuts), else the
largest salvage edge offcut, else none below the nest's new persisted
MinimumRemnantDimension. NestJobCost's salvage offcut now calls the Core
rule unchanged. NestWriter writes a derived plate expectedRemnant that
readers skip, and /sheets reports it per plate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:44 -04:00
ajandClaude Opus 5.5 15750949dd feat(nest): plates name the ledger item each sheet is cut from
Auto Nest on MaterialLedger stock now records which inventory item each
physical sheet uses, so ML can hold exactly those items instead of matching
by size.

- Core: Plate.StockItemCodes (entry i = sheet i of Quantity; empty = uncoded).
  Core invalidates it: a resize clears it (turning the sheet keeps it), a lower
  Quantity truncates it, a nest material/grade/thickness change clears every
  plate's, ClearAll clears it and new plates never copy it.
- Desktop Auto Nest: after a kept proposal is committed, LedgerStockAssignment
  deals each ledger row's codes to its plates in Nest.Plates order (bundles
  repeat their code; clamp-removal variants use their base row). Rejected,
  discarded or cancelled proposals never reach it. NestPipelineCommit.PairWithStock
  pairs committed plates with their solved stock; the engine stays ledger-free.
- Plate list tooltip: "Stock: CODE (location)"; locations are display-only.
- nest.json: optional plate stockItemCodes, omitted when empty, read leniently.
- /api/nests/{id}/sheets: new plates[] { index, width, length, quantity,
  stockItemCodes }; sheets[] unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:44 -04:00
ajandClaude Opus 5.5 ea1decc097 feat(autonest): load sheet and drop stock from MaterialLedger
Auto Nest's Stock Options gets a Load from ledger button that reads the nest
material's free sheets and drops from MaterialLedger's API-key-protected
GET /api/stock/opennest. Ledger rows are request-local: the engine sees them as
ordinary NestPlateStock rows with ledger:{n} ids and finite quantities, and they
are never written to Nest.PlateOptions.

- OpenNest.Data/MaterialLedger: settings file, read-only client (X-Api-Key,
  5 s timeout, closed LedgerStockResult set) and LedgerStockMapper (bundles and
  sheets one row each, same-size drops grouped with ordered item codes, unknown
  shapes and too-small items skipped, inch/mm conversion).
- Options: MaterialLedger URL and API key (DPAPI, CurrentUser) with Test.
- Auto Nest grid: Use, Qty and Source columns for every row; PlateOption gains
  request-local Quantity and Id, which NestStockBuilder passes to the engine.
  Ledger rows score by area; hand-typed costs stay saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:40:44 -04:00
ajandClaude Opus 5.5 42f1762c56 fix(desktop): keep the cut simulator moving while the plate view pans
Panning invalidates the view on every mouse move, so a paint is always
pending and WM_TIMER never arrives; the torch froze until the pan ended.
Paints now advance the playhead from the playback clock too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 16:46:47 -04:00
ajandClaude Opus 5.5 455c543501 fix(verify): close contours whose cut runs past its start as a seam overcut
Converted part programs keep a source CAM's overcut: a hole's arc or a slot's
last line runs about 0.03 past the pierce along the first cut. The rapid check
only counted a contour closed when it ended exactly at its start, so every
overcut contour became an open fragment, the open-cut proof refused them, and
each part reported "Multiple interrupted/open cutting fragments" and an
unverifiable lead-out instead of being checked (P260810-02: all 50 parts).

ReleasedContourState now closes such a contour at its seam (matched at the
1e-6 seam tolerance, since a hole's two arcs carry centers ~1e-7 apart) and
lets the rapid leaving the overcut end ignore contact within that tolerance of
its departure. The nest now validates fully and reports the real crossings it
was hiding: rapids that cross the hole just cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:25:22 -04:00
ajandClaude Opus 5.5 4b982e8eeb fix(desktop): paint the utilization bar when its fill is a sliver
A one-pixel fill (about 2% on the Nesting Progress bar) asked GDI+ for a
zero-size corner arc, which threw "Parameter is not valid" mid-nest. Clamp
the corner radius to the rectangle and fall back to a plain rectangle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:56:51 -04:00
ajandClaude Opus 5.5 0d369c9a51 feat(desktop): draw numeric units beside the value and accept typed units
Unit suffixes are painted in a reserved right margin instead of being part
of the edit text, so editing the number no longer fails to parse. Length
boxes also convert values typed in other units (mm, cm, m, in, ft,
fractions such as 3/8").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:36:13 -04:00
ajandClaude Opus 5.5 cbf3b728aa feat(desktop): preview the highlighted set beside the Sets list
The set preview moves from its own window into a details pane beside
the list, as the Drawing Library has, with the set's notes below it.
It reads the highlighted set shortly after the selection settles and
caches drawings per revision. Parts are drawn as outlines without fill,
and the info lines sit above the fitted drawings instead of over them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:35:29 -04:00
ajandClaude Opus 5.5 fa70713bfc fix(geometry): treat near-joint contacts as the joint when classifying slides
Drawing data can fix a fillet's center slightly off the tangent normal, so
the arc's end sits ~1e-7 past its neighbouring line's end. The slide contact
classifier only joined entities within 1e-7, so a contact at that tangent
point read as two boundaries, was classified ambiguous and blocked the push.
In N26-243 this stopped a part pushed left along another once the parts
aligned. Contacts within 1e-6 of a joint now take the joint's tangents;
the 1e-7 incidence tolerance is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:52:48 -04:00
ajandClaude Opus 5.5 ba55d5bff0 revert: drop the duplicate near-vertex hole-cut fix
Master already starts such entries exactly at the vertex (45592a81), so the
cherry-picked snap was redundant and its test's off-vertex precondition no
longer holds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:24:57 -04:00
ajandClaude Opus 5.5 f600c5e58b feat(desktop): nest released nests live in the main window
With Nest after release ticked, Make Nests now minimizes the Drawing
Sets window and nests each group's built nest in its own main-window
tab, before it is saved, with Auto Nest's progress window and live plan
strip, one nest at a time in plan order. Stop there (or switching tabs)
stops the whole release, as the panel's Stop does; the layout is kept
by the same rules as the headless renest, without the Keep review.

After upload each tab is bound to its saved record instead of a second
tab opening; a tab whose nest was not saved is closed. The main window
stays in front, and the Drawing Sets window flashes its taskbar button
when a group failed or was refused or the release was not recorded.

NestRenest splits into CreateRequest and Keep so the solve runs off the
UI thread and the commit on it; AutoNestCommand's solve-with-progress
step is shared rather than copied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:21:28 -04:00
ajandClaude Opus 5.5 27796851f3 feat(desktop): build releases in a panel beside the Drawing Sets lists
The modal release dialog is replaced by ReleasePanel, docked beside the
Library and Sets tabs and shared by both. A double-click or Enter adds a
set at one kit or a library drawing on its own at the entered quantity;
adding one already listed adds to its quantity. Lines are edited in
place with a remove button, and the nests it will make preview live.

Each group now merges into a matching Draft nest by default: the only
one, or the most recently saved of several (chosen in the row); a draft
open in a tab blocks Make Nests instead of making a parallel nest.
Nest after release is remembered per user. Edit Set and Edit Drawing
move to their buttons and right-click menus.

The Drawing Sets window is now an unowned top-level window with its own
taskbar entry and remembered bounds, closed with the main window; a
release not yet made survives closing it, and Make Nests brings the
main window forward. ReleaseSetForm remains as a thin dialog host for
releasing from Nest > Add from Drawing Library.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:21:27 -04:00
ajandClaude Opus 5.5 d4269c1f06 refactor(cutting): merge circle lead settings into internal leads
Full circles now take InternalLeadIn/InternalLeadOut like every other
cutout; the ArcCircle lead settings and the cutting panel's Arc / Circle
tab are removed. Circle-specific geometry handling (radius clamp, angle
rounding, pierce zone) is unchanged.

Older nest files and desktop settings still load: their arcCircleLeadIn/
arcCircleLeadOut keys are ignored and no longer written, so re-planned
circles take the internal leads. Saved part programs are untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:20:09 -04:00