Clamps could only sit on the origin-side edge and be edited as a list in Nest > Clamps. Setting them up against a real sheet is easier by dragging them where they are, so Plate > Edit Clamps adds an edit mode: drag a clamp along its edge or onto another edge, click near an edge to add one, click a clamp's locating dimension to type its distance from the origin, Delete to remove. Dimensions measure to the clamp center or its near edge (remembered per user). Edits replace the nest's rail so every sheet follows, like the dialog. Rail clamps now carry a ClampEdge (origin side, far side, origin end, far end), positioned from the origin along that edge. Origin-side clamps keep their ids and files; other edges persist in optional per-edge arrays in .nest files and clamps.json, omitted when empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
18 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-Nis drawing N's clean G-code program; optionalprograms/program-N-subscontains its hole sub-programs.- Each serialized plate has a one-based
id. Itspartsarray excludes cut-offs; each placement retainsdrawingId,x,y, androtation(radians). - A placement with
HasManualLeadInsalso writesparts/plate-P/part-K, andparts/plate-P/part-K-subswhen 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
programfield 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. drawingHashprotects 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.
Nests converted from PEP before their drawings carried etch marks keep those
marks only in the saved placed programs. When a drawing has no etch (scribe)
moves and every placement with a saved program carries the same marks, the
reader adds those marks to the drawing in its own frame (after its existing
moves) and refreshes that drawing's clean placements, so Remove Lead-ins and
Clear Lead-ins & Cut-offs keep the marks. Saved programs are never changed. If
the saved programs disagree, the drawing is left without marks and no warning
is raised. The drawing hash is checked against the file text before this
repair, so saved programs still load; the next save writes the marks into
programs/program-N.
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). Clamps on the other
edges are listed in optional farSideCenters (X, along the edge across the
sheet's width from the origin), originEndCenters (Y, along the edge through
the origin) and farEndCenters (Y, along the edge across the sheet's length);
each is omitted when empty, so origin-side-only rails are written exactly as
before. Rail clamp ids (clamp-N) number the origin-side clamps first, then
far side, origin end and far end, each by ascending center. Older readers
ignore the extra arrays; a plate's saved clamps then no longer match their
rail and load as explicit zones. 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
clamps were removed (by Auto Nest as a last resort, or by hand) 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. A plate that follows a rail other than the nest's default
saves it in its own optional clampRail (same shape), so its removal state and
time survive a reload; plates following the default rail omit it.
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, quadrant, edge and part spacing, 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, quadrant or spacing 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.
Saved best-fit pairs (cache only)
Optional bestfits/bestfit-N entries hold drawing N's cached best-fit pair
results: a JSON array of sets, each with plateWidth, plateHeight and
spacing (the plate size and part spacing it was filtered for) and results.
Each result has part1Rotation, part2Rotation, part2OffsetX,
part2OffsetY, strategyType, testNumber, candidateSpacing,
rotatedArea, boundingWidth, boundingHeight, optimalRotation, keep,
reason, trueArea and hullAngles. Angles are radians; pair geometry is in
the drawing's canonical frame.
The entries are only a cache: readers may ignore them and writers may omit
them, at the cost of recomputing pairs. Writers save only sets whose
(width, length, spacing) matches a plate in the nest, and only kept results; a
drawing without such a set gets no entry. NestReader and NestWriter touch
these entries only when given an IBestFitStore through BestFitStore;
without one, a reader skips them unparsed and a writer writes none. The front
ends pass the engine's BestFitCacheStore.Shared, which adds loaded sets to
the process-wide BestFitCache without replacing sets already computed.
Drawing library links
A drawing copied from a server drawing library carries a library object:
serverKey (the normalized server URL), id, revision, and a snapshot of that
revision's cutType, thickness, materialName, materialGrade, units, notes
and packageSha256. It is omitted for other drawings. Read leniently: a value that
is not an object, has the wrong types, a blank server key, an empty id, a revision
below 1, a cut type or units that are not one supported name, or a negative
thickness loads the drawing without a link and adds a reader warning. Converter
edits keep the link as provenance; split pieces get none. Drawing packages leave
the link out, so linking a drawing does not change its package bytes.
Drawing packages
A library drawing's geometry travels as a canonical drawing package
(OpenNest.IO.DrawingPackage): a .nest archive with one drawing and no plates,
fixed archive timestamps and dates, and no best-fit entries, so equal content has
equal bytes and SHA-256. The server stores each distinct package once by that hash.
A package holds geometry and geometry-bound data only: program, source entities and offset, suppressed entity IDs, entity roles, bends, cut pins, breakoffs, tabbed cutouts and material density. Fields the server's library record owns are written as their defaults, so editing them never changes the hash:
- nest and drawing
name(blank), drawingcustomer, materialnameandgrade(blank); color,customColor,priority,cutFirstandconstraints(omitted);quantity.required(zero) andlibrary(omitted).
Adding a library drawing to a nest ignores whatever a package holds for these fields and applies the record's values.
Drawing colors
color is the drawing's display fill. A drawing writes customColor: true when
someone picked that color on purpose (Drawing Properties, or a library record with a
stored color); it is omitted when false. Opening a nest in the desktop app, or
changing the color scheme, reassigns every non-custom, non-cutoff drawing a color
from the active scheme's palette, so the view follows the application's scheme
rather than whichever scheme the file was saved under. Files written before this
flag have no custom colors and are recolored on open. Split pieces keep the flag.
Drawing cut-first flag
A drawing may write cutFirst: true to have automatic part sequencing cut its parts
before every other part (see part sequencing);
it is omitted when false, so older readers ignore it. Split pieces keep the flag.
Drawing cut pins
A drawing may carry a cutPins array, omitted when empty. Each pin has x/y
in the drawing's source frame (the frame of its source entities, which does not
move when the program is re-origined at its first rapid; the program frame is the
source frame minus source.offset), a leadIn and leadOut in the same shape as
plate cutting parameters, and an optional tab omitted for a closed contour.
Pins are part of the drawing's content, so drawing packages carry them and they
change the package hash. Pins are read strictly per entry but leniently per file:
an entry that is not an object, has wrong types, an unknown lead or tab type, an
invalid dimension, or a tab together with a lead-out is skipped with a reader
warning naming the drawing and pin; the other pins and the drawing load. A
cutPins value that is not an array loads no pins, with a warning. Converter
edits keep pins (they stay in the source frame); split pieces and canonical
engine copies get none.
A placement may write drawingPinsOff: true to ignore its drawing's pins on that
part only; it is omitted when false. Any value other than a boolean loads as
false (the drawing's pins apply) with a warning naming the plate and part.
Drawing breakoffs
A drawing may carry a breakoffs array, omitted when empty. Each entry has
edgeIds (source entity IDs of the joining line), flip (omitted when false),
tabWidth and tabCenters (distances along the joining line), all in the source
frame; see breakoffs. The stored program already has them applied,
so readers never rebuild geometry from them. An entry with no edges, a tab width that
is not positive and finite, or a nonfinite tab center is dropped with a reader
warning; the drawing and its program load unchanged, only the breakoff stops being
editable.
Break cuts are saved as motions tagged :BREAK. In a drawing program each break cut
travels with its scrap on the left; placed programs may cut it either way. Versions
without breakoff support cannot read the tag.
Drawing tabbed cutouts
A drawing may carry a tabbedCutouts array, omitted when empty. Each entry has
edgeIds (source entity IDs of the hole's contour), tabWidth and tabCenters
(distances counterclockwise around the hole from its lowest-left vertex), all in the
source frame; see tabbed cutouts. The stored program already has
them applied, so readers never rebuild geometry from them. An entry with no edges, a tab
width that is not positive and finite, or a nonfinite tab center is dropped with a reader
warning; the drawing and its program load unchanged, only the cutout stops being editable.
Tab spans are saved as motions tagged :TAB. They bound material but are never cut;
versions without tabbed cutout support read them as ordinary cut motions.
Drawing entity roles
entities/entities-N holds drawing N's source entities, the IDs of suppressed
(excluded) ones in suppressed, and an optional roles object, omitted when empty,
mapping source entity IDs to "cut", "etch" or "display". An entity without an
entry keeps the role its layer implies: ETCH, SCRIBE and ENGRAVE etch, every
other layer cuts. The stored program already has the roles applied: etched entities
are :SCRIBE motions and display-only entities are not in the program at all, so
posts, nesting, area and verification never see them; the desktop draws them with
each placed part. Roles are geometry-bound data, so drawing packages carry them and
they change the package hash. Unknown role names and malformed IDs are skipped (that
entity keeps its layer role). Load-time etch repair only uses entities whose role is
etch, so an etch-layer entity set to cut stays cut. Split pieces get no roles.
Versions without role support ignore roles; they keep the program as saved but
lose the overrides, and display-only geometry, on the next converter edit.
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.