Drawing Sets > More > Import BOM reads a BOM and its drawing folder, matches each drawing to the library by its D4 identity and geometry (reuse, new revision, or new drawing) and opens the set editor with a new set prefilled with the BOM quantities. Reading only queries the library; Create Set lists the uploads and revisions and asks before storing them, and the set is stored only by the editor's Save. Parts with no material or thickness still import and are flagged for Edit Drawing. DrawingPackage.GeometryKey compares geometry independent of the fresh entity ids each DXF import assigns. In Database mode File > Import BOM opens this flow instead of creating nests directly; File mode is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
21 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 ("Draft", "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. Builds
before Draft therefore open a Draft nest as Quote, and saving it there keeps Quote.
machine optionally names the machine the nest is cut on: id (its machine
configuration's GUID) and name (the configuration's name when assigned, so the nest
still says what it was made for on a PC without that configuration). The key is omitted
when no machine is assigned. A non-object, a missing, non-string, unparseable or empty
id loads as no machine; a missing or non-string name loads as empty.
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.
Cut-offs
Each serialized plate's cutOffs array holds its cut-off definitions: x, y,
axis (vertical or horizontal), optional startLimit and endLimit, and the
places in the plate's cut sequence (Plate.Parts order, cut-offs included) of the
parts the definition materializes. Loading regenerates each definition with the
reader's default cut-off settings and makes one part per trimmed segment. sequence
is the zero-based place of the first segment (by position along the line); null or
absent in older files, which load the cut-off at the end. segments lists the place
of every segment in ascending order along the line and is written only when the
segments are not consecutive, as after Plan Cutting cuts a strip line in pieces where
the route passes them. Readers without segments place the segments consecutively
from sequence, which is still a dependency-safe order. A segments list whose length
differs from the regenerated segment count (nondefault cut-off settings, a moved part)
places the segments consecutively from its smallest entry. headLockOvercut
(model units) and farEdgeFirst record a head-locked overcut past the far sheet
edge and whether its segment starts at that edge (see
automatic cutoffs);
both are omitted when zero or false, and older files load without them.
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.
Support grid
plateDefaults.supportBed optionally stores the machine support bed every sheet of
the nest rests on (see support tabs): type, then for grid
pitchX, pitchY, offsetX, offsetY (the first grid point, measured from the
plate origin into the sheet), or for slats axis (x or y, the axis the slats
run along), pitch, thickness and offset (the first slat's center line); both
end with registrationTolerance and stabilityMargin. All lengths are in the
nest's units. The key is omitted when the nest has none, so such files are
unchanged. A missing pitch or thickness, a non-numeric value, an unknown type or
axis, or an invalid value (a non-positive pitch, a negative thickness, tolerance
or margin) loads as no bed; the rest of the nest loads. Versions without slat
support read a slats bed as none. Every plate follows the nest's bed.
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),
canonicalAngle 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 canonical angle is not saved with the drawing: readers recompute it from
the program, so a change to that computation turns the frame of drawings saved
before it. canonicalAngle records the drawing's canonical angle when the set
was saved, and BestFitCacheStore drops a set on load unless it matches the
recomputed angle (within 1e-9 radians, modulo a full turn), since pairs replayed
in a turned frame can overlap. Sets from files saved before the field existed
have no angle and are dropped the same way; their pairs are recomputed when
next needed.
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.
An unchanged DXF file imports with the same entity ids, but from another folder its
package records another source.path, and packages saved before entity ids were stable
carry random ones, so the packages' hashes differ. DrawingPackage.GeometryKey
compares geometry instead: the SHA-256 of the package entries with source.path left
out and every GUID replaced by its order of first appearance. BOM import to a set uses
it to reuse a library drawing whose geometry has not changed.
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.