Commit Graph
100 Commits
Author SHA1 Message Date
aj 7f648e6400 build(release): package built-in engines instead of external plug-ins
Rectangles and Irregular now ship inside OpenNest.Engine.dll, so the Windows
package no longer fetches, tests and bundles OpenNest-Engines at a pinned
commit. Removes scripts/external-engines.json and the Engines/ folder,
manifest and license from the package; build-info.json drops enginesCommit.

ReleaseSmoke now checks the packaged registry: every built-in engine must
instantiate from the packaged OpenNest.Engine.dll, the Opus55NestingEngine
name must resolve to Irregular, and an unknown name must be rejected. Linux
check against a published OpenNest.Engine: pass case exit 0; missing engine
DLL exit 1. Full Windows packaging still needs the Windows runner.
2026-09-29 21:18:53 -04:00
aj b688ce896f feat(engine): add built-in Rectangles and Irregular nesting engines
Moves the two production plug-in engines into OpenNest.Engine under names
that describe the jobs they suit:

- Rectangles: plain and near-rectangular plates, maximal-rectangles box
  packing (was the RectanglesNestingEngine plug-in)
- Irregular: irregular profiles, no-fit-polygon frontier packing (was the
  Opus55NestingEngine plug-in)

Their tests and the shared engine contract/layout test kit move into
OpenNest.Engine.Tests/NestingEngines.

The registry maps the old plug-in names to the new engines, so saved desktop
selections, scripts and API requests keep working, and a leftover plug-in DLL
under an old name cannot shadow its replacement. Desktop startup passes the
registry's lookup when restoring the saved Auto Nest engine.
2026-09-29 21:18:37 -04:00
aj 1eb509740e test(export): assert drawing reference identity and dispose form on setup failure 2026-09-29 20:40:03 -04:00
aj ad36101b7f fix(export): report write failures and return truthful outcomes 2026-09-29 20:16:49 -04:00
aj 00ede7c5d0 fix(math): normalize degree interval checks in degrees 2026-09-29 19:27:23 -04:00
aj 314ca2f2a6 feat(desktop): add File -> Export Nest Report command
Capture the report target and reject whole-job nesting, open progress
windows, interactive fill and busy plate actions across every view
sharing the nest; revalidate after the save dialog, capture the snapshot
synchronously on the UI thread and render it through OpenNest.Reporting.
Windows adapter tests cover enablement, guards, cancel, success and a
write failure against an existing destination (compile-only on Linux;
Windows runtime acceptance still owed).
2026-09-29 18:29:05 -04:00
aj 64045be968 feat(ui): wire file and database nest storage 2026-09-29 16:21:17 -04:00
aj 437d659c9d feat(server): add OpenNest.Server SQLite nest storage API
SQLite-backed (Microsoft.Data.Sqlite, WAL) minimal API storing NestRecord
metadata plus the .nest archive as a BLOB. Endpoints: GET/POST /api/nests,
GET /api/nests/{id}[/file], PUT /api/nests/{id}/file, PUT
/api/nests/{id}/metadata, DELETE /api/nests/{id}, GET /healthz. Multipart
upload contract matches RemoteNestRepository (metadata JSON part + file
part). Added to OpenNest.sln, builds standalone on Linux. Dockerfile
publishes to a runtime image listening on :8090 with a /app/data volume
for the SQLite file. docs/nest-storage.md documents the wire contract,
endpoints and deployment.

Full curl round trip verified manually against a running instance:
upload (server-assigned id + computed fileSize), list, get, byte-exact
file download, metadata-only update (archive unchanged), file update
(new archive persisted), 404s for unknown ids, delete, post-delete 404.
2026-09-29 16:21:17 -04:00
aj 10ed3d3a30 feat(data): storage-mode settings and remote nest repository
NestStorageSettings persists the file/database toggle and server URL at
%APPDATA%\OpenNest\storage.json following the EngineSelectionSettings
never-throwing pattern; missing config means File mode. NestRecord carries
the shareable job metadata, INestRepository defines the backend contract,
and RemoteNestRepository implements it against the nest server with
multipart uploads (metadata JSON part + .nest file part).
2026-09-29 16:21:17 -04:00
aj ef2f9aa714 feat(core,io): persist nest status and made-by in nest files
Add NestStatus (Quote/ToBeCut/HasBeenCut) plus MadeBy on Nest, written as
additive camelCase nest.json fields with PascalCase enum strings matching
the units convention. Legacy files and unknown status values fall back to
Quote. The nest info dialog gains a Status dropdown and Made By box.
2026-09-29 16:21:17 -04:00
aj b15c4cf6c6 feat(reporting): add multi-plate pagination and dense-label fallback
Slice 2 of the nest report plan: pagination, overflow and dense-label
coverage on top of the Slice 1 one-plate library.

- NestPdfWriter: general multi-plate pagination. Summary/plate tables
  continue across pages with repeated (HeadingFormat) header rows; notes
  flow as an ordinary paragraph instead of a bounded cell; page headers
  are wrapped and sized into the top margin; drawing areas are located
  per page via DocumentRenderer.GetRenderInfoFromPage.
- ReportText: lossless pre-wrapping (MigraDoc clips an over-tall row and
  lets an unbroken token overflow a narrow cell without warning), capped
  table-cell line counts, plate-range compression ("1-2, 4"), and
  A/B/.../AA map-grid row names.
- NestReportDiagram: label placement centers each part ID on its
  PolyLabel pole (matching PlateView's LayoutPart), computed on a
  placement-independent quantized copy so identical parts share one
  label position. Parts whose ID cannot fit legibly at overview scale
  get a lettered/numbered map grid and a zoomed, framed detail page per
  crowded cell; the writer fails with plate/part/ID when even that
  cannot place a label, or a plate would need more than 24 detail views.
- Tests: NestPdfLayoutTests (multi-plate totals/ranges/same-named
  references, table continuation with no lost rows, long name/notes
  wrapping without column overflow, mm units in all four quadrants,
  dense-label detail views with hole avoidance, save/reload of the
  tabbed/lead-in fixture with stale tab flags, invalid later-plate data,
  late write failures against an existing destination, source
  unchanged on success/failure), ReportPdf test helper (page/word/
  content-stream extraction).
- docs/nest-reports.md: replace the Slice 1 one-plate limits section
  with the general pagination/dense-label contract and the process-wide
  PDFsharp/MigraDoc render lock.

Verification: Reporting filter 70/70; full OpenNest.Tests 2524
passed/21 skipped/0 failed; Engine 351/351; IO 77/77;
EnableWindowsTargeting=true full-solution build 0 errors; scoped
dotnet format --verify-no-changes exit 0. Preview PDFs rendered and
visually inspected (evidence: /home/aj/extracted/2026-09-29/opennest-report-slice2/).
Windows runtime, the desktop adapter and packaged-app font deployment
remain unverified (Slice 3).
2026-09-29 15:43:00 -04:00
aj e4d64a88eb fix(reporting): serialize concurrent nest report exports
PDFsharp/MigraDoc layout and font state is process-wide. Parallel exports
of the same snapshot laid text out differently (for example merged words
like "Total physicalsheets" and shifted table columns). Serialize the
whole render-and-save in NestPdfWriter.Write; reports are rare.

Regression test exports one snapshot 128 times in parallel and compares
page content streams with a sequential export; it failed with dozens of
mismatches per run before the lock.
2026-09-29 14:04:53 -04:00
aj 74d2b6aa68 feat(reporting): add one-plate nest report PDF library slice
Add OpenNest.Reporting (net8.0, PDFsharp-MigraDoc 6.2.4) with a detached
report snapshot and a PDF writer for an empty/demand-only job or a single
plate layout: Letter portrait summary with vector part thumbnails and a
landscape plate page with a vector sheet diagram, both with Page X of Y.

- Snapshot uses reference identity for document-local R### IDs, recounts
  nested quantities from placements x copies with checked wide integers,
  excludes cutoffs from accounting, and retains no live domain objects.
- Geometry keeps Cut/Display material paths, drops rapid/scribe/lead paths,
  preserves native arcs, holes and intentional tab gaps, and rejects
  malformed programs with plate/part identification.
- Open contours and cutoffs are stroked per contour so PDFsharp cannot join
  a tab gap to the next contour.
- Bundled DejaVu Sans via a custom resolver (including MigraDoc's error
  font); unsupported text fails with the field and code point.
- Layouts beyond this slice (multiple plates, page overflow, labels that
  do not fit) fail with NotSupportedException before the destination is
  replaced; output is rendered to a temporary sibling and moved atomically.
2026-09-29 13:13:32 -04:00
aj 64d0e50df3 fix(ui): keep an add-new row in Auto Nest stock options 2026-09-29 12:43:55 -04:00
aj 7d179c696a fix(sequencing): snapshot plates before sequencing the entire nest 2026-09-29 12:23:19 -04:00
aj 662bc133b1 refactor(nesting): unify console MCP and API validation and commit policies 2026-09-29 11:15:05 -04:00
aj b3a4390169 refactor(ui): route every Auto Nest engine through the validated pipeline 2026-09-29 10:56:38 -04:00
aj b8bded1b09 fix(engine): reject unrepresentable nesting output before geometry checks 2026-09-29 10:47:36 -04:00
aj ac901f7ee9 feat(ui): remember the selected Auto Nest engine 2026-09-29 10:43:36 -04:00
aj 43253cd9ab feat(engine): share stock construction across nesting callers 2026-09-29 10:33:16 -04:00
aj 7aeb636ca2 fix(engine): discard cancelled pipeline results from uncooperative engines 2026-09-29 10:31:23 -04:00
aj c914d2bbd9 Add NestPipeline: one validated engine path for all front ends
build NestJob -> resolve engine by name -> Solve -> independent
NestLayoutCheck validation -> bind placements to caller drawings.
The pipeline never commits to plates and never mutates caller items;
validation failures are returned as messages naming real drawings
(unknown-requirement placements are reported, not dropped). Console,
MCP, API and desktop Auto Nest will adopt this path in later phases.

- NestPipeline.Run(request): registry-resolved engine, unknown names
  list the registered engines; cancellation propagates untouched.
- NestPipeline.Run(engine, ...): stub/plug-in engines take the same path.
- NestResultBinder: pose semantics identical to NestResultMaterializer.
- NestLayoutCheck: Violations overload with per-requirement display names.

Tests: overlap stub -> violations w/ drawing names, no throw; ghost
placement -> violation + excluded from further checks; unknown engine
-> NotSupportedException listing engines; cancelled token -> no result;
happy path -> bound by reference, caller quantity untouched.
2026-09-29 10:23:19 -04:00
aj 481c3e5128 docs: trim agent instructions and remove historical performance report 2026-09-29 10:18:50 -04:00
aj 29953ea601 feat(overlap): auto-recheck the active plate after layout edits settle
Nest windows now rerun the overlap check once the layout has been
unchanged for 0.5 s, instead of leaving 'Overlaps: not checked'. Edits
show 'Overlaps: check pending...'; drags are caught by the paint-time
pose stamp, collection edits by their events. The check waits while a
mouse button, modal dialog or fill is active, supersedes a running check
when the layout moves again, and does not retry a canceled or failed
layout until it changes. Rechecks use the incremental analyzer, so only
the moved parts' neighbors are recomputed.

Automatic results update only the canvas label (the status bar keeps the
last command's message) and keep Display > Off. Check Active Plate still
runs immediately. InvalidateOverlapCheck now also drops cached material.
2026-09-29 09:31:51 -04:00
aj 99c31748ba feat(diagnostics): cache overlap material and recheck incrementally
PlateOverlapAnalyzer.Capture(parts, OverlapMaterialCache) reuses each clean
program's converted entities and prepared material across requests, and
Analyze(snapshot, previous) reuses pair results whose two parts kept the
same source, exact pose and relative order, renumbering them. A recheck
after moving one part only clips that part's neighbors again.

On 501 real PEP plates: full check median 1 ms / max 6.4 s; incremental
recheck after one move median 0.1 ms / max 38 ms. Incremental results
matched uncached full analysis exactly across 2505 edits.
2026-09-29 09:31:26 -04:00
aj 2b8a3ce7db fix(leadin): add close button to Place Lead-in side panel
The docked side panel could only be closed with Escape, which was
unreliable: MainForm consumes Escape before ActionLeadIn's KeyDown
handler, so with a part selected ActionManager called the action's
empty CancelAction and nothing happened; with nothing selected the
panel closed but was stored as the previous action, so the next
Escape reopened it.

- EditNestForm side panel gets a header with the panel title and an
  always-visible close button that ends the action.
- ActionLeadIn.CancelAction now steps back (unlock contour, then
  deselect part), replacing the dead KeyDown handler.
- Actions can opt out of Escape-resume (ResumeOnEscape); the lead-in
  action does, so a closed panel stays closed.
2026-09-29 08:38:48 -04:00
aj 36de55f10f style(actions): remove BOM from Action.cs per .editorconfig 2026-09-29 08:38:41 -04:00
aj 55fe0ef228 fix(cnc): extend first-cut edge for outside corner lead-ins
A straight lead-in at a convex outside-perimeter corner now runs along the
extension of the edge cut first, so the torch enters on that line and keeps
cutting it. The result no longer depends on which of the corner's two edges
auto-assign or the manual cursor picked, which made placement flip between
straight and 90 degrees. The approach angle is ignored at such corners.

The straight lead falls back to the first-cut edge normal when its pierce
would be closer than PierceClearance to the contour (very flat or tessellated
corners). Reflex perimeter corners bisect the notch. Line lead-outs run on
straight past a convex corner along the last-cut edge, except on tabbed
perimeters. Program generation and the Place Lead-in preview share
ResolveLeadIn/ResolveLeadOut.
2026-09-29 07:46:20 -04:00
aj 8720580004 fix(sequencing): cut scrap cutoffs before crossed parts 2026-09-29 00:05:28 -04:00
aj c3dd346b7a feat(cutoffs): apply nest-wide with minimum retained tail 2026-09-28 23:43:06 -04:00
aj 4afab63046 fix(cnc): advance rapid display through cutoff cutting moves 2026-09-28 23:19:08 -04:00
aj 2cc06d1dc7 fix(cutoffs): exclude leads from material perimeter chaining 2026-09-28 23:08:47 -04:00
aj 45d2dfced4 feat(posting): require verification and explicit risk acknowledgment 2026-09-28 22:50:03 -04:00
aj a5fddec0e9 fix(cutoffs): account for bounds translation roundoff safely 2026-09-28 22:19:02 -04:00
aj a8204bb51f feat(plateview): annotate overlap centroids 2026-09-28 22:17:49 -04:00
aj e4d07121c8 feat(plateview): show material overlap areas 2026-09-28 21:35:05 -04:00
aj 76044f6bd9 merge: integrate polygon overlap diagnostics 2026-09-28 20:56:46 -04:00
aj 1581260e31 feat(cutoffs): add automatic skeleton cutoffs with tail preservation 2026-09-28 20:50:53 -04:00
aj 19c3a89c32 feat(core): report hole-aware part overlap polygons 2026-09-28 20:44:51 -04:00
aj 4dbdc2678d fix(cutoffs): preserve clearance at ambiguous vertex intersections 2026-09-28 20:37:49 -04:00
aj fb32d508f8 fix(geometry): stabilize triangulation winding at large coordinates 2026-09-28 20:37:38 -04:00
aj 1f637ca43c merge: integrate cutout corner lead-in bisectors 2026-09-28 19:40:02 -04:00
aj 648b0eaca5 fix(cnc): bisect inside cutout corners for straight lead-ins 2026-09-28 19:25:40 -04:00
aj b254a0bd3e feat(io): persist part cutting programs and plate parameters 2026-09-28 19:17:59 -04:00
aj 74302761ce fix(io): restore hole sub-programs with negative IDs 2026-09-28 19:05:11 -04:00
aj 4da48eed68 fix(core): keep cut-offs in their cut sequence through regenerate and save
A cut-off's place in Plate.Parts is its cut sequence number, but
RegenerateCutOffs removed every cut-off part and appended it again, so
any part drag, fill or cut-off move sent the cut-offs to the end. The
nest file didn't store the position either, so reopening did the same.

RegenerateCutOffs now puts each cut-off back at its previous index (new
cut-offs go at the end), and CutOffDto.Sequence saves the index. Older
files without it load the cut-offs at the end, as before.
2026-09-28 18:44:04 -04:00
aj 6803d7519f fix(posts): post CI Fiber cut-offs in their sequence, not last
b0997f6 moved every cut-off after the parts. Plate.Parts order is the
cut sequence, and the user sets each cut-off's place in it (Set
Sequence), so the post must follow it rather than reorder. Cut-offs
still post as uncompensated open lines with no lead-in.
2026-09-28 18:41:06 -04:00
aj 86dbd8819b style(core): apply dotnet format to Plate.cs
Formatter-only: drops the UTF-8 BOM (.editorconfig charset = utf-8).
No behavior change.
2026-09-28 18:40:50 -04:00
aj 20395eed9b fix(core): keep lead-in state and rotation through part copy and rotate
Part.Clone and CloneAtOffset copied the lead-in program but not
HasManualLeadIns, LeadInsLocked, CuttingParameters or the tracked
pre-lead-in rotation. A copy of a rotated lead-in part reported
rotation 0 (the rebuilt program's), so saving it wrote the wrong
rotation and Remove Lead-ins restored an unrotated part.

Part.Rotate on a lead-in part set the tracked rotation from the
lead-in program's own Rotation, which starts at zero when the cutting
strategy rebuilds it, so a further quarter turn left Rotation unchanged.
Lead-in parts now accumulate the applied angle instead.
2026-09-28 18:22:36 -04:00
aj a886735040 fix(cnc): keep hole sub-programs private to each program copy
Program.Clone deep-copied the SubPrograms dictionary but left every
SubProgramCall pointing at the source's sub-program, and
SubProgramCall.Clone went through the Rotation setter, which re-rotated
that shared program to the call's stale angle. Copying a program with
hole lead-ins therefore rotated the source's holes, and rotating the
copy rotated the source again.

Program.Rotate also rotated a shared sub-program once per call, so two
identical holes (one deduplicated sub-program) turned twice.

Clone now binds calls to one private copy per shared sub-program
without re-aligning it, and Rotate turns each distinct sub-program once.
2026-09-28 18:22:36 -04:00
aj 75d41f3bb7 style(cnc): apply dotnet format to Program and SubProgramCall
Formatter-only: re-indents braced switch sections in Program.cs and drops
the UTF-8 BOM from SubProgramCall.cs (.editorconfig charset = utf-8).
No behavior change.
2026-09-28 18:22:36 -04:00
aj 26252b333a docs(posts): record M50 as the confirmed CI Fiber pallet change
The machine owner confirmed M50 swaps pallets (the sample's M50 before
M30 moves the cut sheet out for unloading). Only the single-program
between-sheet sequence remains unconfirmed.
2026-09-28 18:22:36 -04:00
aj c219ec4e66 feat(posts): CI Fiber one program per sheet, configurable pallet change
Multi-sheet nests previously went into one program with a single header
size and one M50 at the end, so sheet 2 would cut into sheet 1's
skeleton. New Sheets settings:

- One program per sheet (default on): JOB.cnc -> JOB-1.cnc, JOB-2.cnc,
  each a full program with its own size and pallet change (CL-series
  batch rule, EM-423 7.4). Single-sheet nests keep the chosen name.
- Off: one program, with /L "L0" + pallet change between sheets;
  mixed sheet sizes are rejected.
- Pallet change code (default M50, unconfirmed for multi-sheet CI Fiber
  runs; documented as a release blocker).

All sheets are validated and rendered before any file is written.
IMultiFilePostProcessor lets the desktop app confirm overwrites of every
target file and list what was saved, and the console print each file.
2026-09-28 18:22:36 -04:00
aj 32586149c6 refactor(posts): remove CI Fiber part comment override
A fixed override wrote the same text on every part, making parts
indistinguishable in the program. Each part's ( PART:... ) comment now
always uses its source file or drawing name. Saved configs that still
contain PartComment load normally; the key is ignored.
2026-09-28 18:21:21 -04:00
aj 9ec28add80 fix(posts): keep CI Fiber material codes case-insensitive after reload
System.Text.Json rebuilds MaterialCodes with the default ordinal comparer,
so a saved config matched 'Mild Steel' but not 'mild steel' and silently
fell back to the default code. The setter now re-keys assigned maps
case-insensitively.
2026-09-28 18:21:07 -04:00
aj 35aa64fe4e feat(posts): sectioned settings editor for CI Fiber post
Replace the generic PropertyGrid for configs that opt in via
PostSettingAttribute: a section list (Machine, Material, Program output,
Macros) with labelled fields, help text, numeric ranges and an editable
material-code table. Edits apply only when OK validates every field.
Unannotated configs (Cincinnati CL, GravographIS) keep the PropertyGrid.
2026-09-28 18:21:07 -04:00
ajandClaude Opus 5.5 c33337cea2 fix(cnc): stop double-counting first incremental rapid in rapid display
RapidEnumerator primed the walk position at the first pierce point, then
the skipped first rapid advanced it again. Raw programs start with a zero
rapid so this was invisible, but lead-in programs start with a real
incremental offset to the pierce, which shifted every later rapid by that
delta and drew rapids off the sheet. Start the walk at the program origin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 14:21:37 -04:00
ajandClaude Opus 5.5 b0997f614b fix(post): post sheet cut-offs in CI Fiber without comp or lead-in
Posting a nest with a sheet cut-off threw because the CI Fiber writer
treated every contour as a compensated part contour and requires a
linear lead-in after G41/G42 (TF5200 13.2.4.1). Cut-offs are open
centreline cuts with no lead-in and no inside/outside, so they now post
without G41/G42 and run after every part on the sheet so the sheet is
not severed before the parts are cut (matching the CL post).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 14:14:07 -04:00
aj 6d7c98ef4a docs: document defaults.json nest-defaults mechanism replacing .nstdot 2026-09-28 08:22:49 -04:00
aj f44c1d465f feat(ui): defaults.json-backed new nests; retire .nstdot templates
New nests now load plate defaults from %APPDATA%/OpenNest/defaults.json
instead of a .nstdot nest template:

- Tools > Nest Defaults... edits the file directly (new dialog).
- Tools > Save Current Plate as Defaults captures the active plate with
  no dialog (visible when a nest is open).
- New_Click no longer unzips a template; a corrupt defaults file warns
  once per session and falls back to built-in values.
- First run converts an existing NestTemplatePath .nstdot to
  defaults.json and clears the legacy setting (kept readable on a
  failed conversion); the setting itself stays, marked legacy.
- Save As no longer offers the .nstdot filter; SaveTemplate removed.
- BomImportForm and OptionsForm template plumbing removed in favor of
  the shared NestDefaults store.
2026-09-28 08:20:49 -04:00
aj bdb78a689d feat(defaults): JSON nest defaults store with per-field fallback
Replaces the .nstdot nest-template mechanism for new-nest plate
defaults. NestDefaults persists units, plate size, quadrant, part
spacing, and edge spacing to a single JSON file (DefaultPath:
%APPDATA%/OpenNest/defaults.json). Load never throws: a missing,
corrupt, or partially valid file degrades field-by-field to the
built-in fallback values, and unknown fields or a future version
number are ignored.
2026-09-28 07:09:52 -04:00
aj 3f89f8f5dd feat(ui): Tools > Expand Spacing for selected parts
Mirrors PushSelected: PlateView/SelectionManager.ExpandSelected runs
Expander.Expand on the selection, marks parts dirty, regenerates cut
offs, and reports achieved spacing plus blocked-pair count in the
status bar. Menu item joins Align Selected's enable surface.
2026-09-28 03:17:55 -04:00
aj 3cab90747a feat(expand): plateview spacing expander
Grows part-to-part spacing of a selected group with the work area and
non-selected parts as hard boundaries. Doubling + bisection search over
the target spacing; Gauss-Seidel straight-line relaxation with anchor
mover policy (later-index selected part moves, first selection never).
Overlapping input is separated along penetration MTVs instead of being
rejected. Cancel/failure never mutates part positions. Clearance gains
BoundaryDistance for ring-pair gaps (part-in-cutout legality).
2026-09-28 03:05:45 -04:00
aj fff3bef4e4 feat(geom): signed clearance kernel between polygons
Omnidirectional minimum distance with separating direction (positive)
and penetration depth with minimum-translation direction (negative),
for the PlateView spacing expander. Overlap verdict defers to
Collision.HasOverlap so kernels never disagree. Basis for the fixed-s
separation solver.
2026-09-27 23:36:26 -04:00
aj 2b78fb3a75 fix(geometry): resolve blocking contacts across directional slide paths 2026-09-27 23:24:31 -04:00
aj a04460c57b refactor(ui): drop duplicate plate preview from nesting progress dialog
The dialog rendered the same best-parts layout as transparent ghost
parts already shown live on the main plate view, and its embedded view
reset zoom on every improvement while lacking placed parts, work-area
and cut-off context. The progress dialog is now a compact stats strip
(266px wide) with the plate counter preserved for multi-plate runs.
Removes the never-called SetStationaryParts stationary-preview path
along with PreviewPlate/UpdatePreview/CreatePreviewPlate.
2026-09-27 20:43:34 -04:00
aj 27948ba8cf docs(posts): replace bundled manual with implementation references 2026-09-27 19:52:25 -04:00
aj 1fdede396b ci(release): bundle and verify external nesting engines 2026-09-27 19:11:14 -04:00
aj 1b9988a1ba ci(release): build and verify Windows desktop packages 2026-09-27 18:57:38 -04:00
aj ca1902e075 fix(bestfit): stop at internal arc tangency 2026-09-27 16:35:53 -04:00
aj bc5fd86996 test(fill): characterize unchanged-row validation reuse 2026-09-27 15:10:01 -04:00
aj 1b23ad79f2 perf(fill): reuse part triangulations within an overlap check
After 1b, triangulating both polygons on every pair was the largest
remaining overlap cost (27% of main-thread samples on the corpus job).
PartOverlapChecker now triangulates each part at most once per check,
lazily after the bounding-box gate, and passes the triangles to a new
internal Collision.HasOverlap overload that runs the unchanged
OverlapRegions body. Triangles are only read by clipping and hole
subtraction, so reuse gives identical verdicts.

Verification:
- 49,000 seeded decisions with reused triangles match LegacyCollision;
  triangles stay bit-identical to a fresh triangulation afterwards.
- Debug PolygonTriangulations: 246 -> 40 and 64 -> 36 per grid check;
  sharing triangles per Program instead fails 23 tests.
- Corpus job (169 parts, --engines Default --parallel 1): median
  13,398 -> 12,702 ms over 4+4 alternating runs vs 1b, identical
  outcomes; serialized layout byte-identical to the base.

Also records the Follow-up B' (Slices 1a, 1b, 2a) measurements in
docs/performance/fill-performance.md.
2026-09-27 13:49:46 -04:00
aj a27290a29c perf(fill): prepare overlap polygons once per check
Both HasOverlappingParts loops rebuilt each part's polygon from its
Program on every pair. PartOverlapChecker prepares each distinct Program
(reference identity) once and each part's world polygon once per call,
then uses the overlap-only Collision.HasOverlap. Loop order, bounding-box
prefilter, early exit and returned indices are unchanged; Part.Intersects
shares the material/polygon recipe and still returns crossing points.

Verification:
- Frozen LegacyPartOverlap differential (original Intersects and both
  loops): verdicts, indices and world polygons bit-identical across fill
  grids, patterns, touching/epsilon gaps, scribe/rapid/empty programs.
- Debug OverlapPolygonPreparations: 246 -> 1 and 64 -> 2 per check.
- Corpus job (169 parts, --engines Default --parallel 1, with 1a):
  median 18,885 -> 13,464 ms over 4+4 alternating runs, identical
  outcomes; serialized layout byte-identical to the base.
2026-09-27 13:34:04 -04:00
aj f44a1a3d2c style(console): consolidate usage text into one multiline write 2026-09-27 13:17:35 -04:00
aj 2b5485f6cf perf(core): skip unused crossing points in overlap-only checks
Collision.HasOverlap only needs the verdict, but it went through Check,
which also collected crossing points. Triangulation, clipping and hole
subtraction now live in one private OverlapRegions method shared by Check
and HasOverlap, so verdict arithmetic stays single-sourced; Check output is
unchanged.

Tests: a frozen copy of the previous Collision is the oracle. 50,000 seeded
HasOverlap verdicts and 2,400 bitwise Check results match it, plus
containment, contact, hole and input-immutability cases. A Debug-only
PerfCounters.CrossingPointScans counter proves HasOverlap no longer scans.
Malformed polygons with null outer vertices still throw when the bounding
boxes overlap (now ArgumentNullException from triangulation rather than
NullReferenceException from ToLines).

Measured (Release, same harness in both trees): about 44% less time per
overlap-only polygon check, allocations 10.0 -> 7.9 MB per 155-pair sweep.
The 169-part serialized corpus layout is byte-identical.
2026-09-27 12:54:20 -04:00
aj a98a49c00a test(fill): add opt-in overlap-check micro benchmark
Times Collision.HasOverlap over the box-overlapping neighbour pairs of two
FillLinear grids, and the FillHelpers.HasOverlappingParts grid checks
themselves. Opt-in via OPENNEST_RUN_FILL_PERF=1 (Category=FillPerformance).
Uses only APIs that predate the overlap-check work, so the same file can be
copied into a before tree for same-harness comparisons.
2026-09-27 12:54:20 -04:00
aj fdbc1fb99f style(console): combine adjacent output with readable multiline strings 2026-09-27 12:48:52 -04:00
aj 82feb78b0f feat(posts): add Cincinnati CI Fiber (TF5200) post-processor
New post-processor plugin OpenNest.Posts.CincinnatiCIFiber for the CI
Fiber laser family (nLight CLX / Beckhoff TF5200 / Precitec ProCutter,
e.g. the CI Fiber 4020 8kW). Named by machine family, not table size.

Emits the machine program contract of the Cincinnati-supplied sample NC
(12992-4SS_NEST.nc): V.E.* header, restart jump, per-part V.E.R4 blocks,
per-contour N labels with V.E.R3, skippable /L macro lines (L0/L2+G41
interior, L4+G42 exterior, L6 cut-on, ZHSOFF cut-end), G162 incremental
arc I/J, trimmed 3-decimal spaceless coordinates, CRLF, M50/M30/%.

Contour classification (interior vs exterior) derives from the material
side of the closed cut path, not hardcoded winding. SubProgramCall holes
are flattened to sheet coordinates (rotation-safe). Arc lead-ins are
rejected per TF5200 13.2.4.1 (first motion block after G41/G42 selection
must be linear). Table envelope (default 160.25 x 81.25 in) validated.

Tests: structure golden on a square-with-hole nest, rotated-hole flatten,
coordinate format, arc-lead-in rejection, table validation, suppressed/
scribe skipping, plus a SkippableFact regression against the real sample
NC (109 parts, 2071 contours, L2=1962, L4=109, perimeter vertices match
within 0.001). Fixtures configure through OpenNest.Tests/test-config.json
and the regression skips when absent.
2026-09-27 04:10:22 -04:00
aj c825f40213 refactor(posts): move post-processor projects under Posts/
Group the Cincinnati and GravographIS plugin projects in a Posts/
folder so new machine posts have one home. Project names, namespaces,
and the runtime Posts/ deploy target are unchanged; only relative
paths in the solution and project references move.
2026-09-26 22:35:23 -04:00
aj 0df2587cf2 perf(fill): reuse offset geometry for translated copies
FillLinear re-prepared offset perimeter geometry (ConvertProgram ->
ShapeProfile -> OffsetOutward) for every part it measured, although
tiled copies share one Program and differ only by Location. A CPU
profile of a 169-part Default job put 62% of wall time there.

Prepare each distinct Program (reference identity) once per public
Fill/FillRow call in local frame, then clone and translate for each
location. The cache is created per call and passed down privately
because FillHelpers.FillPattern calls Fill concurrently on one
instance. PartGeometry gains a local-frame Program overload that the
Part overload now delegates to.

Evaluation order, lazy preparation, fallbacks and tiling are
unchanged. Differential tests against a frozen copy of the previous
FillLinear check bitwise equality, including concurrent calls; Debug
work tests pin preparation counts. With the thread pool capped at one
worker, before/after whole-job layouts are byte-identical. The
Default corpus job median drops from 40,715 to 18,810 ms.
2026-09-26 20:24:34 -04:00
aj 094c4c196b fix(bestfit): never reject pair candidates by utilization
Thin-framed, hollow, or concave parts (e.g. SULLYS-035's frame) have
inherently low part-to-bbox utilization yet nest tightly, so the 30%
MinUtilization floor wrongly dropped every candidate for them. Pair
quality is judged by the rotated pair bounding-box area the results
are already sorted on; utilization now only ever serves as the
high-aspect exception (UtilizationOverride), never as a rejection.

Adds a hollow-frame helper plus regression tests that kept pairs
exist with low utilization and results stay sorted by pair area.
2026-09-26 14:14:44 -04:00
aj 77b729e58c docs(readme): drop opt-in fill performance measurements section
Keeps the README approachable for new users; microbenchmark details
remain in docs/performance/ for maintainers.
2026-09-26 13:00:32 -04:00
aj 1d1c60daa7 docs(fill): record combined initial-batch acceptance 2026-09-26 11:25:17 -04:00
aj 1e8e532063 perf(fill): skip feature extraction without an angle model 2026-09-26 10:02:15 -04:00
aj d70505b7c0 docs(fill): sync shared guidance for scalar-only extraction 2026-09-26 07:36:25 -04:00
aj 8188533d72 perf(ml): support scalar-only angle features 2026-09-26 00:02:28 -04:00
aj 6863c8bdb1 docs(fill): clarify Task 3 evidence retention 2026-09-25 21:46:31 -04:00
aj 4553f8afad perf(fill): avoid redundant bounds recomputation 2026-09-25 21:45:03 -04:00
aj 4ec92c95ec docs: share coding-agent guidance through AGENTS.md 2026-09-25 20:03:42 -04:00
aj b2c864a328 docs(fill): synchronize performance workflow guidance 2026-09-25 19:59:02 -04:00
aj 6efa6b1117 perf(fill): remove discarded extents pitch geometry 2026-09-25 19:52:19 -04:00
aj cec7396da6 perf(fill): avoid unused scores with custom comparers 2026-09-25 17:18:15 -04:00
aj b318950a54 perf(fill): short-circuit default comparisons by count 2026-09-25 16:43:09 -04:00
ajandClaude Opus 5.5 1b862dc1a8 fix(engine): allow 0.0005 spacing slack in layout validation
Layouts placed exactly at the part spacing can land ~1e-4 short once
rotated, rounded (e.g. PEP's 4-decimal exports) and snapped to the
Clipper grid, so both validators rejected layouts that were correct in
practice. NestTolerances.SpacingSlack (0.0005, far below anything a
cutting machine resolves) is now subtracted from the spacing by
NestLayoutCheck's inflation and NestJobPlacementValidator's edge-distance
check. The frozen LegacyNestValidator takes the same rule so the
equivalence tests keep comparing like with like.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:19:04 -04:00
aj d1f3907505 Merge branch 'fix/mcp-config-path' 2026-09-25 14:19:01 -04:00
ajandClaude Opus 5.5 4fb924b03a chore: restore LF line endings in CLAUDE.md
39db4dc committed CLAUDE.md with CRLF endings while the repo stores it
as LF (text=auto), turning a one-line doc addition into a whole-file
diff. Renormalized; the content is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:12:40 -04:00
ajandClaude Opus 5.5 39db4dc10f feat(ui): route Auto Nest through whole-job engines
StockLadder and Engines/ plug-ins only implement INestingEngine.Solve, so
selecting them in Auto Nest had no path to run. MainForm now solves the
whole job through JobEngineNest when the selected engine is not a
built-in fill strategy, feeding NestJobProgress into NestProgressForm and
binding the result poses back onto the nest's own drawings. Whole-job
engines throw on cancel rather than returning a partial layout, so the
progress form hides Accept for these runs. Built-in strategies keep the
existing per-plate fill path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:03:10 -04:00
ajandClaude Opus 5.5 23dd99fa2f feat(benchmark): add --progress logging for engine solves
Long whole-job solves ran silently, so there was no way to tell a slow
engine from a hung one until the timeout fired. --progress hands each
solve a JobProgressLog that prints [job/engine] lines for start, finish
(or failure/timeout), every plate commit, and candidate evaluations
throttled to one line per 2 s so parallel runs stay readable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:03:10 -04:00
695ccc0a3b perf(engine): use cached triangulations in the layout check
The benchmark validator and every engine test re-triangulated both parts
for each nearby pair. NestLayoutCheck now uses TriangulatedRegion, with
Collision.HasOverlap as the fallback when it cannot decide. Verdicts are
unchanged (the frozen-validator equivalence tests still pass); validating
100 discs went from 1,254 ms to 94 ms.

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 10:08:36 -04:00
98d0124172 perf(core): add cached-triangulation collision and an edge-grid prefilter
Collision.HasOverlap re-triangulates both polygons on every call; Qwen
measured that as its dominant cost (over 400 s -> ~110 s on a 219-part
job once cached). TriangulatedRegion (from Qwen's TriSet) triangulates a
part once and takes translation as a parameter; it returns null when it
cannot decide so callers fall back to Collision, which stays the
reference. EdgeGridPolygon (from Qwen's FastPoly) certifies clearly
disjoint shells and never reports Clear for an overlap. A seeded harness
of 100,000 decisions (concave shapes, arcs, holes, touching contacts)
finds 0 mismatches against Collision.HasOverlap.

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 10:08:36 -04:00