Slice 3 of common-line cutting. On costly plate (N26-295: 3/16 RA310S at about
$2,500 a sheet) pairing parts one kerf apart fits the part the spacing cannot:
the last plate's 38 parts fit on one 48x120 with Auto Nest only with pairs.
- Drawing Properties gains "Allow common line" (Drawing.AllowCommonLine, off by
default because customer edge-quality needs vary). The nest's CommonLineUsage
(Off / When it adds parts / Whenever available) is set in Auto Nest and seeded
from Tools > Nest Defaults; both persist (nest.json, defaults.json).
- NestPipeline runs the ordinary solve, then, with a level on, trials where
eligible parts are offered as kerf-gap pair compounds. Placed pairs expand into
parts plus explicit NestJobCommonLine groups, are validated again, and
CommonLineRanking keeps a trial only by the level: count first, then sheets;
"when it adds parts" keeps the plain layout on a tie, "whenever" also takes a
shorter cut or less material, never at the cost of a part. Engines, FillScore
and their comparers are unchanged; Off asks the engine exactly what it did.
- NestLayoutCheck exempts members only through a sheet's explicit group list and
only when the job allows common lines; materializing and committing create
Plate.CommonLines.
- Server nesting writes common-line jobs as version 3 and replays the recorded
trial answers, so the desktop binds the same groups.
- Fill Area honors the level through a PlateFillService overload.
- The benchmark gains --common-lines <level> --kerf <k>.
Benchmark (synthetic nests + N26-295, --parallel 1): with no flag and with
--common-lines off every engine matches the base commit; with a level on no
layout becomes invalid and three runs save a sheet at equal nested area.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The support grid was one shared support-grid.json, off by default, and a nest
had no machine at all, so a plasma table and a fiber laser could not have
different grids, and nothing could pick machine-specific behavior later
(lead-ins differ between laser and plasma; tabbing depends on the grid).
- Nest.Machine (id + name) persists as top-level `machine` in nest.json;
malformed values load as none.
- MachineConfig.SupportGrid holds each machine's grid; a machine without one
still falls back to the legacy support-grid.json.
- New and BOM import assign the Tools > Nest Defaults machine (first by name
when none is chosen) and give the nest that machine's grid.
- Nest > Machine reassigns a nest and swaps in the new machine's grid;
Nest > Support Grid saves machine defaults to the nest's machine.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New only fell back to the legacy DefaultUnit setting when the whole
defaults file was missing or invalid, so a readable file without a
usable unit produced inches. BOM import never assigned units at all,
and the Nest Defaults dialog showed inches for a missing file.
All three now load through MainForm.LoadSavedNestDefaults, which passes
DefaultUnit as the per-field unit fallback. BOM-created nests start from
NestDefaults.ApplyTo, like New, before applying each group's plate size
and spacing.
The ApplyTo test now asserts units; removing the unit assignment from
ApplyTo fails it. WinForms behaviour is compile-verified only on Linux.
Enum.TryParse accepted numeric strings ("7" loaded an undefined unit,
"1" loaded millimeters) and comma-joined names, and a JSON number for
units failed the whole file. Units now load only from a defined unit
name; anything else falls back for that field alone, while the other
valid fields still load.
Add Load(path, fallbackUnits, out status) so callers can keep their
existing unit preference when the file is missing, unusable, or lacks a
usable unit (previously a readable file without units silently became
inches). An undefined fallback is ignored in favour of the built-in
units.
The numeric/comma/JSON-number rows failed against the previous commit;
dropping the fallback assignment or its IsDefined guard fails the new
fallback tests.
NestDefaults.Load caught only JsonException and IOException, so a
defaults.json that File.Exists can see but the process cannot read
threw UnauthorizedAccessException out of New. Report such files as
Invalid and return the fallback values, like a corrupt file.
The regression mode-000s the file on Unix (skipped on Windows and when
running as root); it failed with UnauthorizedAccessException before
this change.
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.
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.