Commit Graph
1273 Commits
Author SHA1 Message Date
aj 3b9f5ca5fa test: remove customer drawing names from tests
GeometrySimplifierTests hardcoded a customer DXF on one user's desktop and
returned early when it was missing, so the test passed without running
anywhere else. Read the path from the optional "SimplifierGapDxfPath"
entry in test-config.json and report a skip when it is not configured,
matching the other external-fixture tests.

Replace the customer part name in a RemnantFinderTests comment with a
neutral description. Customer drawings stay outside the repository.
2026-09-30 21:46:29 -04:00
aj 0209a24008 style(tests): apply dotnet format to RemnantFinderTests 2026-09-30 21:45:12 -04:00
aj 8450ceee40 refactor(geometry): share signed-angle accumulation through ArcFit
EllipseConverter, SplineConverter and GeometrySimplifier each carried an
identical private SumSignedAngles. Move the unchanged body to
ArcFit.SumSignedAngles and call it from all three, keeping the ordered
accumulation, strict half-turn comparisons and empty/single-point result.

ArcFitTests compares the shared method bit-for-bit with a separate
test-local accumulator across half turns, the atan2 seam, multiple
turns, translated centres and NaN inputs, and checks that inputs are not
mutated. The converter winding characterization from 8664656 still
passes unchanged.
2026-09-30 21:20:58 -04:00
aj c55e375cf3 refactor(engine): remove legacy multi-plate auto-nest orchestration
Desktop Auto Nest, Console --autonest, MCP autonest_plate and the API
NestRunner all run through NestPipeline, so nothing calls the old
orchestration any more.

Delete MultiPlateNester (with MultiPlateNestOptions, MultiPlateResult,
PlateResult, PartClass and PartSortOrder), PlateOptimizer and
PlateOptimizerResult, plus their tests. The explicit-strategy contract
those tests checked now lives at PlateFillService.ResolveStrategy, which
keeps its null-means-Default, canonical-name and unknown-name tests.
CreateFiller loses its internal visibility, which only the deleted
orchestrators used.

This breaks source and binary compatibility for external callers of the
removed types; whole-job callers use NestPipeline.Run or INestingEngine.
2026-09-30 21:07:32 -04:00
aj 5e9eaf8a4c perf(fill): reuse validation for an unchanged row
Skip FillGrid's Step 2 overlap check only when Step 1 checked this exact
row clean and the perpendicular tiling appended zero parts, so gridResult
still holds the same Part objects in the same order and poses. The local
rowIsVerified flag is cleared by every Step 1 skip or bbox fallback, and
any nonzero append (including partial-copy parts) keeps the eager check.

Debug work assertions pin the reduced overlap work (horizontal stripe
8->4 exact calls, 4->2 preparations, 16->8 triangulations; vertical
stripe 36->18, 4->2, 38->19) and keep single-seed, Horizontal full-grid,
partial-only and PerpOnly work at base; the invalid overlapping-seed
control pins both fallback stages plus the retained exact-call total.
Three mutants (forced eager check, dropped flag, complete-rows-only)
are caught by their designated controls. Serial whole-job layout is
byte-identical to the frozen 62b5a8d0 oracle before and after.
2026-09-30 17:01:11 -04:00
aj dbcf7dea65 refactor(geometry): share directional part-line preparation 2026-09-30 13:40:22 -04:00
aj 98870e78a7 refactor(cincinnati): share hole subprogram call output 2026-09-30 13:40:22 -04:00
aj c9bb32470c refactor(geometry): share ray and contact-angle primitives 2026-09-30 13:40:22 -04:00
aj e84d7f0e72 fix(ui): traverse nested active controls when restoring focus 2026-09-30 13:40:22 -04:00
aj 6426a98f82 fix(geometry): identify invalid ellipse axes in exceptions 2026-09-30 13:16:00 -04:00
aj 23ffc14f3f fix(controls): dispose owned separator pens 2026-09-30 13:16:00 -04:00
aj b2deed6c5a test(bestfit): observe posted async failures during dispatcher drainage 2026-09-30 13:16:00 -04:00
aj 1cc95c8115 fix(bestfit): own async viewer operations and report failures 2026-09-30 13:16:00 -04:00
aj 038886a757 fix(mcp): bound engine harness execution and output drainage 2026-09-30 13:16:00 -04:00
aj 8664656658 test(geometry): characterize converter winding before ArcFit extraction 2026-09-30 08:57:34 -04:00
aj d7fd2f8468 style(geometry): normalize diagnostic interpolation alignment 2026-09-30 08:56:14 -04:00
aj 5bf3c5f9aa docs(packing): clarify PackingId is caller-supplied and clone-preserved 2026-09-30 07:40:54 -04:00
aj 984d31a391 fix(packing): preserve item type and separate packing identity in clones 2026-09-30 07:24:39 -04:00
aj 4f46fa15ea style(packing): normalize circle-packing file encoding 2026-09-30 07:24:02 -04:00
aj 38324628b0 test(editor): prove per-operation highlighting with a status sentinel
The reorder event snapshot could previously show the reverse pass's
stale timeout label. Set a sentinel before each click so the
ProgramChanged handler can only observe the label applied by the
current operation's highlighting pass.
2026-09-30 01:39:26 -04:00
aj 132bb07bc9 test(editor): make control assertions handle-state independent and order-proving
Quality review of 50884da found the unshown RichTextBox oracle compared
cached CRLF text against the highlighted editor's native LF-normalized
text, which would fail on first Windows execution. Compare generated
text with normalized line endings on both sides instead, capture the
status label and preview state inside the ProgramChanged handler to
prove fallback-before-notify ordering, document the HighlightSpan
UTF-16 and rule-index contract, and pin the null-text argument check.
2026-09-30 01:29:11 -04:00
aj 50884daca1 fix(editor): bound highlighting without interrupting program updates 2026-09-30 00:14:43 -04:00
aj fc53c7732e fix(ui): format stock sizes invariantly and bound validation notices
Address Task 3b quality review:
- FormatSize now emits invariant decimal notation so a comma-decimal
  locale cannot produce stock text its own invariant parser rejects.
- Validation notices embed a bounded single-line preview instead of the
  full cell value.
- Documented GetPlateOptions' all-or-nothing throwing contract.
- STA test helpers run the worker as a background thread with a
  diagnostic timeout message; added a de-DE fractional stock round-trip
  regression.
2026-09-29 23:29:20 -04:00
aj ab1f3a28d1 fix(import): abort failed CAD text extraction 2026-09-29 22:31:26 -04:00
aj 43ebd61fdf fix(ui): reject timed-out stock and shape input 2026-09-29 22:27:05 -04:00
aj bf608fa271 test(math): name fraction culture coverage accurately 2026-09-29 22:02:32 -04:00
aj fa5e5a8e29 fix(io): bound bend-note regex execution without partial imports 2026-09-29 21:41:27 -04:00
aj 388816b015 fix(math): bound fraction regex execution 2026-09-29 21:41:20 -04:00
aj 3bdefb1d1c docs(engines): document built-in engines, renames and change rules
Adds docs/nesting-engines.md: which engine suits which jobs, the old plug-in
names the registry maps, retired engines, and the rule that an engine change
lands only when it beats that engine's current benchmark result with every
layout valid. README, release and automatic-nesting docs drop the bundled
plug-in wording; AGENTS.md now places built-in engines in
OpenNest.Engine/NestingEngines and keeps only external plug-ins out of the
solution.
2026-09-29 21:19:27 -04:00
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