Files
OpenNest/docs/nest-file-format.md
T
aj 6fc1a0ebdc fix(core): move an untouched trailing sheet to new nest defaults
Changing the nest defaults (Nest Info, Set as default, Edit Plate on an
empty sheet) left the editor's trailing empty sheet on the old size and
quadrant, so the writer's clamp-preserving empty-sheet rule saved it as a
stale extra sheet. PlateManager.ChangePlateDefaults wraps a defaults edit:
a trailing empty, cutoff-free sheet that exactly matched the old defaults
(and followed their rail with no removed clamps or explicit zones) takes the
new defaults; every other empty sheet keeps its configuration and is still
written.

PM cade6901-5f4a-4503-85bf-0ea681d9ca7f
2026-10-13 18:15:50 -04:00

8.6 KiB

Nest file format and saved cutting state

.nest is a ZIP archive with additive version-2 JSON metadata in nest.json. Old files remain readable. Older applications ignore the new cutting-state fields; opening and saving with an older application loses that state.

Job metadata

nest.json carries the workflow fields shown in the nest info dialog: name, customer, dateCreated, dateLastModified, notes, material, thickness, plus status ("Quote", "ToBeCut", "HasBeenCut") and madeBy. Status and made-by are additive: files written before them load as Quote with an empty maker, and an unrecognized status value likewise falls back to Quote.

Programs and placements

  • programs/program-N is drawing N's clean G-code program; optional programs/program-N-subs contains its hole sub-programs.
  • Each serialized plate has a one-based id. Its parts array excludes cut-offs; each placement retains drawingId, x, y, and rotation (radians).
  • A placement with HasManualLeadIns also writes parts/plate-P/part-K, and parts/plate-P/part-K-subs when it has hole sub-programs. P is the serialized plate ID; K is the zero-based index in that plate's serialized parts array. Empty plates and cut-offs do not consume these part indices.
  • The placement's program field references that ZIP entry. Its G-code is saved in the already-rotated local part frame, with the placed origin at (x, y). Existing :LEADIN, :LEADOUT, and other layer tags, tab gaps, and G65 calls are retained rather than regenerated by a cutting strategy during load. Hole IDs may be negative: they are identifiers, not validity sentinels.
  • drawingHash protects against restoring obsolete cutting paths after an externally edited drawing. It is uppercase SHA-256 of UTF-8 text composed of the main text's decimal UTF-16 character count, :, main text, and sub-program text (empty when absent). The hash includes both drawing entries so changing a hole alone invalidates the placed cutting program. Text is read without a BOM; otherwise whitespace and line endings participate in the hash.

The reader constructs and places a clean part first, then installs its saved program without rotating it again. The drawing reference remains authoritative for Remove Lead-ins: removing them restores the clean drawing at the saved pose. Successful restoration marks the part as carrying lead-ins and restores its leadInsLocked flag. The per-part CuttingParameters reference remains null; the saved program, not strategy regeneration, determines the cutting geometry.

Missing program references in legacy files load clean and silently, even when old transient flags were true. A missing or mismatched drawing hash likewise loads clean silently. A referenced but missing, unparseable, or motionless part program, or an unresolved/empty hole sub-program, leaves that part clean and unlocked without discarding the rest of the nest. NestReader.Warnings identifies the plate, zero-based part index, and entry; File > Open displays these warnings. The program reader is not a general G-code or geometric safety validator; the saved cutting geometry is not validated against the drawing.

Plate cutting parameters

Each plate's optional cuttingParameters stores the settings used by Assign Lead-ins and Place Lead-in. Missing settings stay null, preserving the old default selection behavior. OpenNest.IO.CuttingParametersSerializer owns the shared cross-platform DTO mapping; the desktop settings serializer delegates to it so existing saved settings keep their JSON contract. Settings are independent of the saved part programs: restoring settings never regenerates those programs.

Clamp zones

Each plate may carry an optional clamps array of fixed rectangular keep-out regions in plate coordinates: id, x, y, length (X extent) and width (Y extent, the depth intrusion from the y = 0 operator-side edge for machine clamps). Zones are exclusion and display data only - never material, quantity, utilization, cut sequence or CNC output. Files without the key load with no zones, and an individually malformed entry (blank id, non-positive size) is dropped while the rest of the nest loads.

plateDefaults.clampRail optionally stores the nest's machine clamp rail: width (along the edge), depth (into the sheet) and centers (X positions measured from the plate origin along the origin-side edge). New plates follow the rail and resolve it for their own size and quadrant; clamps wholly beyond a sheet are skipped and one still reaching onto it keeps its whole footprint, so a zone may extend past the sheet edge. The key is omitted when a nest has no rail, so such files are unchanged; a missing, empty or malformed rail loads as none. On load a plate follows the rail again only when its saved clamps are exactly the rail's zones for that sheet; any other saved zones are kept as written.

The rail's optional removalMinutes is the time to take one clamp off and put it back; a missing or invalid value loads as five minutes. A plate on which Auto Nest removed clamps as a last resort lists their rail ids in an optional removedClamps array (omitted when none were removed); its clamps already exclude them, and it follows the rail minus those ids. A malformed removedClamps value reads as none removed (non-string entries are skipped); the saved clamps load either way.

An empty plate (no parts, no cut-offs) is omitted from the file unless that would lose clamp zones: it is omitted like any other empty plate only when a new plate from the saved defaults would recreate it (same size and quadrant, zones the saved rail recreates), which keeps the editor's trailing empty sheet out of the file while it matches the defaults. Nest-defaults edits move only an untouched trailing empty, cutoff-free sheet to the new size, quadrant, edge and part spacing, re-resolving its rail. It must have exactly matched the old defaults and followed their rail with no removed clamps or explicit zones (or have had no rail and no zones when the defaults had none). Edited sheets, middle empty sheets, and sheets with parts, cut-offs, removed clamps or explicit zones are not changed. An empty plate with other zones, or a rail-following one whose size or quadrant differs from the saved defaults, is still written.

Drawing edits

DrawingProgramSnapshot captures serialized main and hole-program text before the converter is loaded. After an accepted edit, only parts referencing drawings whose program text changed are rebuilt (lead-ins, tabs, and locks cleared); their location and rotation remain unchanged. Reference identity, not editable names, keys this comparison. The desktop invalidates the affected layout graphics. Name, quantity, and color edits leave placed programs alone. The properties editor previously called LayoutPart.Update, a color refresh, not Part.Update; it now explicitly refreshes only the edited drawing's layout colors.

Verification and limits

The automated tests cover save/reopen/re-save, rotated/manual/locked parts, lead-outs and tab gaps, hole binding, clean removal, stale drawings, damaged entries isolated from other parts, plate settings, and CI Fiber posting before and after reload. Drawing-change decision logic runs on Linux; desktop rendering, dialog values, and clicks require Windows acceptance.

Known separate issue: the cutting strategy currently labels contour cuts Display. CI Fiber includes them, but GravographIS skips Display. Saving restores the existing layers; this change does not alter the strategy or either post. Future hardening: make numeric G-code serialization/parsing culture-invariant as a coordinated compatibility change; do not change just one side. Program content deduplication is optional and must not change per-part ownership.

Stock-option prices

The existing plateOptions entries retain width, length and cost; no new .nest format version is required. Legacy zero cost means unspecified, not a free sheet. Positive costs are generic per-sheet values in caller-consistent units. Solving requires all available options to be priced or all to be unspecified.

Desktop and accepted MCP/API jobs retain offered options (API rows with zero available quantity are excluded because .nest options do not retain inventory). MCP save also preserves loaded job metadata, plate defaults and salvage settings, and accepted whole-job requirements are stored on drawings. Options do not persist finite inventory or associate a historical purchase price with each physical sheet. API quote archives additionally retain request costs and validated proposal cost summaries; old archives without summaries load with Costs == null.