Commit Graph
2284 Commits
Author SHA1 Message Date
aj b92d079e77 feat(data): read and write the desktop user settings outside WinForms
UserSettingsFile in OpenNest.Data reads and writes
%APPDATA%\OpenNest\user-settings.json (~/.config/OpenNest on Linux) so the
Avalonia app can share the settings the WinForms app keeps there.
AppDataSettingsProvider now delegates its file reads and writes to it, so
there is one implementation of the format.

A missing, unreadable or corrupt file reads as empty without touching it,
and Update keeps entries the caller does not change. One difference for
WinForms: an entry whose value is not text is now skipped instead of the
whole file reading as empty.
2026-11-09 22:05:09 -05:00
aj 3beaf53942 docs(agents): call PortableMath for trig in layout projects 2026-11-09 20:25:14 -05:00
aj da13801d25 ci: run the Linux unit tests in Release
The unit-test matrix was the only test job still built in Debug, and the
Engine suite was taking 26-27 of its 30 minutes. In Debug the JIT does not
optimize PortableMath (64 ns per Sin against 17 ns for the runtime's), so
after 49603edd the suite reached the 30-minute limit and was cancelled
(run 42728099456). Every other test job (windows-desktop, the Windows
release build, the server image) already runs Release, which is what
ships; no production project has Debug-only code. The Windows release
workflow still runs OpenNest.Tests in Debug.
2026-11-09 19:58:50 -05:00
aj 9627bb9625 perf(math): fully optimize PortableMath from its first call
Without tiering, PortableMath's Sin took 25.5 ns against the runtime's
15.0 ns in Release; marked AggressiveOptimization it takes 17.2 ns (Atan2
18.1 against 17.0). Results are unchanged: the cross-platform digest test
passes in Release and Debug on Linux and in Release on Windows.
2026-11-09 19:58:50 -05:00
aj 8e9946aa47 docs(engines): keep layouts the same across platforms with PortableMath 2026-11-09 19:24:25 -05:00
aj 49603eddf9 fix(geometry): nest the same on Windows and Linux
Geometry, nesting and import code now call PortableMath instead of
System.Math for Sin, Cos, Tan, Atan, Atan2, Asin and Acos (Core, Engine,
IO, Data; 269 calls in 64 files). A test fails if those projects call the
runtime's trig again.

With the runtime's trig, Windows and Linux disagreed in the last bit at
non-quarter angles and the same job could nest differently: the
learned-angle nests (a cross fixture) placed 23 parts in Strip on Windows
and 28 on Linux. Now every Engine.Tests pin gives the same layout on both
(3600 passing on Linux and on DesktopPC), Linux layouts are unchanged, and
the learned-angle rows are re-pinned to the shared layouts, which pass the
overlap and work-area checks, and run on every platform again.

Fixes Project Memory 518f3b58.
2026-11-09 19:18:00 -05:00
aj 921d100aa0 feat(math): add trig that returns the same bits on Windows and Linux
System.Math's Sin, Cos and Atan2 call the C runtime, and Windows' UCRT and
Linux's glibc differ in the last bit at many angles that are not quarter
turns. Nesting turns those bits into different tie and tolerance decisions,
so the same job nests differently on the desktop and on Linux (Project
Memory 518f3b58).

PortableMath ports fdlibm's Sin, Cos, Atan and Atan2, which use only IEEE
exact operations, and builds Tan, Asin and Acos on them with Sqrt. Results
stay within one ulp of the runtime (a few for the derived functions) and
hash to the same digest on Linux and on Windows (DesktopPC). Nothing calls
it yet.
2026-11-09 19:08:07 -05:00
aj 071531dbfc test(engine): run the learned-angle fill pins only on Windows
Nest_LearnedAngleItems_PinnedLayout has failed on every Linux CI run since
735c076a re-pinned it: the pins were captured on Windows, where they still
pass, and Linux lays the same nest out differently (Strip 28 instead of 23;
the other rows keep their count with different poses).

The split comes from the platforms' trig differing in the last bit at
non-quarter angles, which the fill stack turns into different decisions:
FillLinear starts a copy's slide exactly at the contact snap tolerance, and
equal-score candidates are ordered by raw doubles. Fixing the start gap moves
about 190 bit-exact FillLinear and layout pins and still leaves Strip and both
Remnant rows split, so the rows run on Windows only until the cause is fixed
(Project Memory 518f3b58). Nest_LearnedAngleItems_DependsOnLearnedAngles
still runs everywhere.
2026-11-09 18:40:26 -05:00
ajandClaude Opus 5.5 5668952e6b fix(import): read source drawings that are open in another program
AutoCAD keeps an open DWG/DXF locked for writing, and ACadSharp's path
constructors (and File.OpenRead / XmlReader.Create) deny write sharing,
so the CAD Converter could not import a part while it was open in
AutoCAD. Open source files read-only with read/write sharing instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 14:01:56 -05:00
ajandClaude Opus 5.5 e58e8b85c4 feat(report): add sheet weight to the plates table
The shop needs each sheet's weight for handling, so the summary's plates
table gains a Sheet weight column: one full sheet from its area, the nest
thickness and the nest (or catalog) density. A drawing's own material is
not used, since it doesn't say what the stock is; with no density the
column is left out.

Plates with copies now put the total cut time (and total weight) on a
second line instead of after a comma, which wrapped mid-phrase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 12:59:31 -05:00
ajandClaude Opus 5.5 ae8e7c6e42 feat(report): lay the summary header out in two key/value columns
The one-column list took nine rows before the Plates table, pushing part
cards onto later pages. Job and material now sit on the left and the
report's totals on the right, five rows at most.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 12:46:15 -05:00
ajandClaude Opus 5.5 9c3a8a5063 test(irregular): run the seeded-search test on a job the seeds still improve
Irregular builds its blocks with Fill, which now nests the ten ells as
herringbone pairs; the normal search's plan rose from 550 to 684 compactness
offcut, and the seeded search no longer beats it within twelve plans' sheets.
Twelve ells nest identically with or without herringbone pairs, and the
seeded search still improves on them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:41:23 -05:00
ajandClaude Opus 5.5 735c076afb feat(fill): tile herringbone pairs at the part's hull angles
Fill paired a 9" angle bracket at 90/270 degrees (75 on a 48 x 96 plate)
while a hand-laid herringbone at 315/135 holds 84. Its pair touches only at
the legs' ends, so it ranked ~888th by pair box area, and pairs are tiled
at the pair's own hull angles, never the part's 45-degree edge.

The pairs phase now also builds herringbone pairs: the part turned so a
hull edge lies along a sheet axis, its 180-degree copy slid onto the row
the part makes along that axis, tiled and remnant-filled like best-fit
pairs. Fill now finds the 84.

The chevron in the learned-angle fixture became a herringbone whatever
angles were learned, so a cross replaces it; the learned-angle and Strip
mixed nests are re-pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 23:39:11 -05:00
ajandClaude Opus 5.5 f62f73533e feat(fill): tile pair grids into narrow leftover strips
Fill tiled 8" gussets on a 16.85 x 60 strip as seven pairs in a 10.35"
column (14) and left a strip too narrow for a gusset square on. Sliding
each pair along its hypotenuse so six span the height narrows the column
to 8.75" and leaves a strip that holds eight more turned 45 degrees (20),
as laid out by hand.

- The pair remnant filler tries strips down to the part's narrowest hull
  width and tiles single best-fit pairs as a grid there, taking the grid
  only when it places more than the rows of pairs.
- After the pair filler's early exit it also tries skipped candidates
  whose grid plus an optimistic leftover-strip estimate could still win,
  keeping one only when it places more parts.
- The optimistic remnant bound counts parts by true area, not bounding
  box, so interlocking parts no longer prune winning candidates.

The mixed Fill nest pin now places 34 instead of 33; the mixed Strip nest
keeps 35 with its L-shapes laid out differently. Both pass the layout check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 22:24:49 -05:00
ajandClaude Opus 5.5 5adcddc748 feat(fill): refill the outer row's end to fit one more part
After the fill strategies, the winning layout's outermost row on each side
is pushed into the edge slack, slid toward one end, and copies of its end
part are slid in from the freed end. Lifting a staggered hexagon row straight
over the one below opens a notch where one more nests: a 7" hex cover on a
60 x 120 sheet now fills 181 instead of 180. The policy's comparer must
prefer the result and the requested quantity caps it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 21:46:03 -05:00
ajandClaude Opus 5.5 3c561566cd fix(clamps): drop the origin rapid from the keep-out outline
The exact clamp check built each keep-out from a program whose opening
rapid from (0, 0) joined the outline, stretching every keep-out into a
wedge back to the origin. Parts clear of a clamp but inside that wedge
were dropped from fills (two L's on a clamped sheet). Build the
rectangle from four lines instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 20:11:35 -05:00
ajandClaude Opus 5.5 74937b7952 feat(cutting): start the owner's outline on its common line
The part cut first no longer makes a separate uncompensated centerline
cut with its own lead-in and lead-out, then re-pierces for its
remainder. It cuts its outside compensated, which puts its shared edge
on the kerf centerline, starting on the common line: a straight lead-in
from the scrap along the shared edge, then the line and on round its
perimeter with no lead-out. A member with spans already cut stops in
that kerf and cuts its last stretch from it, ending beside the part it
is cut before. Partners still cut only their remainders.

Replay's tabbed-ring accounting now accepts pieces that tile each span
exactly once, and the pre-post review counts a span as cut by a
member's compensated edge (or an older centerline cut).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 16:57:35 -05:00
ajandClaude Opus 5.5 bff11dbc3c feat(cutting): cut Z and L common lines once as one shared path
Straight spans of one pair that meet at a corner chain into one shared
path (CommonLineGeometry.Paths). Plan Cutting cuts it as one open
centerline cut through its corners, pierced and left in scrap along its
end legs; each member's spans run on to its own vertex at a corner, so
the path is one tab run. Join Common Line slides along the main edge so
the step of a Z or L also lands one kerf apart, and the plate draws the
path through its corners.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 15:15:29 -05:00
ajandClaude Opus 5.5 757d366e4a feat(ui): pan the Shape Library preview and pin slid labels between the arrows
The preview's middle-drag pan was switched off by the form; turn it on and
let a left drag on empty space pan too. A panned view is kept while editing.

A label slid along its dimension line kept a fixed pixel offset, so zooming
out drifted it off center and past the arrows. Scale that slide with the
drawing; the standoff from the drawing stays in pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 14:34:32 -05:00
ajandClaude Opus 5.5 aa8616ea84 feat(ui): run Shape Library extension lines to 6px from the part; call out the bolt circle from outside
Extension lines now walk in from the dimension line until they come 6px from
the drawing's outline, as on the CAD Converter, passing measured points that
are off the outline (a rounded rectangle's sharp corners). The flange bolt
circle is a leader landing on the dotted circle from outside, between holes,
instead of a linear dimension whose extension lines crossed the flange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 21:59:11 -05:00
ajandClaude Opus 5.5 d7f9f25265 feat(ui): aim the flange hole callout through its center and dot the bolt circle
The Shape Library's hole diameter leader now crosses its hole through the
center to the far edge, arrows pointing out onto both edges, wherever the
label is dragged. The hole pattern dimension measures a dotted bolt circle
through the hole centers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 21:26:30 -05:00
ajandClaude Opus 5.5 ea540253bb feat(ui): delete plates from the Plates list's right-click menu
Add Delete Plate to the plate row menu beside Duplicate Plate, enabled
when Remove Selected Plate is. Drop the Plates tab's add-plate button
and the Plate menu's Add and Remove items: the list's trailing empty
plate already provides new plates, and removal stays on the tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:22:08 -05:00
ajandClaude Opus 5.5 c8f070ea64 feat(ui): list open windows under the Window menu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 20:19:48 -05:00
ajandClaude Opus 5.5 a4417af763 test(ci): track renamed Shape Library invalid-entry test
The dimension-editing rewrite replaced the invalid-input test with
InvalidEntryStaysOpenInRedAndKeepsTheLastShape, so the Windows
acceptance gate found zero cases for the old name and failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 19:58:45 -05:00
ajandClaude Opus 5.5 bcbcf1e611 perf(fill): cut a cold Lattice fill from ~300 s to ~160 s with identical layouts
The first fill of a new drawing was blamed on the best-fit pair search, but on the
reported nest that search takes ~3.4 s. The time goes to FillLinear.FillGrid, run
thousands of times by the Pairs, Row/Column and Linear strategies: pattern copy-distance
slides and the grid overlap check. Every change keeps the output bit for bit (part
poses compared on the benchmark nest; best-fit results unchanged).

- ConvexDecomposition.Triangulate: unlink clipped vertices from a circular list instead
  of List.RemoveAt, and remember why a vertex failed the ear test (same neighbors, same
  blocking vertex still present) instead of retesting it after every ear. Compared
  bitwise against a frozen pre-change copy, including the blocker-clipped case.
- Collision: skip triangles whose box misses the other triangulation's union box (exact:
  min/max and monotone subtraction), reuse each triangle's open vertices, and let the
  overlap-only path stop at its first region when there are no holes. Check still
  returns every region.
- SpatialQuery.ExtractEntityVertices presizes its set; FillLinear extracts each entity
  list's vertices once per copy-distance search rather than once per pair.
- StripeFiller converges its stripe angles and tiles its grids in parallel. Dedup claims,
  cached remnant fills, ranking and progress stay in candidate order, and grids are tiled
  only for configurations a claim can still win (GridDedup.Contains), so results and
  reports do not depend on the thread count.
- LinearFillStrategy fills its angles in parallel through
  FillHelpers.BestOverAnglesInParallel; angle results, ranking and progress stay in order.

Benchmark (one drawing, 60x120, 0.25 spacing, cleared plate, interleaved runs):
FillItem Lattice 291/317 s -> 164/153 s, 132 parts both; PairFiller alone 48 s -> 38-42 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 19:34:31 -05:00
ajandClaude Opus 5.5 331234d6f3 feat(ui): drag Shape Library dimensions and zoom the preview
Dragging a dimension's label moves the dimension: a linear one slides
out from the drawing (never onto it) and its label along the line, an
inline one slides its label, a leader's label goes anywhere and the
leader follows. A click without dragging still opens the editor.

The preview zooms with the wheel, pans with the middle button and fits
again with F or a middle double-click. Once zoomed or panned by hand,
parameter edits keep the view; a new shape or configuration fits it and
puts dragged dimensions back. An open editor follows its label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 19:25:43 -05:00
ajandClaude Opus 5.5 6be8a7b41c feat(ui): edit Shape Library parameters as dimensions on the drawing
The Shape Library no longer has a side panel of inputs. Each length is a
dimension line or leader on the preview; click its label to type a new
value (Enter applies, Esc cancels, Tab moves to the next dimension), as
Edit Clamps does on the plate. Invalid values keep the editor open in
red and the last good shape on screen. Settings with no place on the
drawing (sides, pipe size, clearance, blind) are clickable notes along
the bottom. The flange hole leader carries the count beside the size,
such as "Ø7/8" x 8 holes".

Shapes declare their dimensions through ShapeDefinition.GetDimensions;
lengths show as fractions when exact to 1/64 (Fraction.Format). Edit
Clamps' inline text box moves to a shared DimensionBox.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 19:25:43 -05:00
ajandClaude Opus 5.5 9b643aaec9 feat(engine): refill the pair grid's leftover strip with rows of pairs
The pair filler topped its grid off with single parts at 0 or 90 degrees,
so a narrow strip beside the grid held half of what it could: on a 60x120
sheet of N26-29X parts the plate fill gave 132, while deleting the last
column and filling the strip gave 136. The strip is now also laid out the
way the Row strategy lays one, rows of best-fit pairs turned so whole pairs
span it, and that layout replaces the singles when it places more.

Each strip size is laid out once per fill and skipped when the singles
already fill it by area. On the benchmark nest the plate fill now reaches
136 with the hand-made layout's rotations, about 17% slower (156 s vs
133 s with best fits cached). The pinned L-shape fills gain a part (52 to
53). The auto-nest problem-nest corpus keeps its baseline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 19:15:36 -05:00
ajandClaude Opus 5.5 a9fc9f0afb feat(ui): duplicate a plate by Ctrl+dragging it or from its right-click menu
Copying a layout meant recreating the plate and copying its parts by hand.
Ctrl+drag a row in the Plates list drops a copy at that slot; the row's
right-click menu has Duplicate Plate, which inserts the copy after it.

Plate.CopyLayout copies the sheet (size, outline, spacing, quadrant, grain,
clamps, support bed), the parts with their programs and lead-in state,
common-line groups and cut-off definitions, regenerated in their places in
the sequence, as a save and reload would. The copy is one sheet with no
stock item codes, since those name particular physical sheets. The desktop
gives it its own cutting parameters and records it as one undo step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 17:07:52 -05:00
ajandClaude Opus 5.5 872cd8e4b8 fix(ui): show the plate size on the new-plate header
The empty new-plate header read only "New plate (empty)", so the sheet
size of the plate about to be nested was hidden until parts were added.
FormatHeader now appends the size (and quantity above one) there too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 16:48:52 -05:00
ajandClaude Opus 5.5 e6e2e5e0ad test(corpus): guard Auto Nest problems in the problem-nest corpus
The corpus is meant to hold every nest that ever caused a problem, tagged with
what it guards, but it could only plan cutting paths. Nests tagged
`checks: auto-nest` now run the Default engine on their demand and fail when the
layout is invalid, leaves parts unplaced, or uses more sheets than their
`auto-nest: N sheets` line; planner runs skip them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 15:17:57 -05:00
ajandClaude Opus 5.5 863ce4afde feat(engine): drop a sheet by packing edge parts first and turning the key part by its contacts
The Irregular engine grows each sheet from one side, so parts that fit only
when the outer ones hug opposite edges never met: ten one-off brackets
(four tapered 112-inch bars, four gussets, two small plates) stacked three bars
on one sheet and took three 60 x 120 sheets where two hold them.

After the search, a sheet-reduction pass splits a complete single-stock plan
over one sheet fewer and packs each sheet edge first: large parts to the
corners (flush, most outline on the edges, thinnest across the sheet), small
parts into the gaps last, and one key part into the pocket between. Clamped
sides move in past the keep-outs so edge parts line up on one straight edge.
When no sampled orientation fits the key, ContactFit turns it by its contacts
(each overlap a linear constraint on dx, dy and rotation) instead of sampling
more angles; the exact no-fit free region then confirms the angle, so spacing
and validation are unchanged. That job now nests on two sheets, the middle bar
at 4.4 degrees; the repository benchmark jobs are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 15:17:56 -05:00
ajandClaude Opus 5.5 b1430bf5ba fix(ui): keep the Drawing Sets window in front of the main window
File > Drawing Library opened the window unowned, so clicking the main
window buried it. It is now owned by the main window: still modeless,
so nests stay editable, but it always stays on top of it. It keeps its
taskbar button and minimize, which nesting during a release relies on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 20:44:23 -05:00
ajandClaude Opus 5.5 535219b073 fix(release): keep released tabs saved so a later merge release records
Make Nests bound each tab it nested to its saved record but left the
tab's history off its save point, so the tab showed " *" and closing it
asked to save. Yes uploaded a second revision, and a release that had
already looked up that draft (the panel asks the operator to save and
close an open draft) then merged with If-Match on the old revision: the
server refused it, that group failed, no nest after it was saved and the
release was not recorded. End() then closed the leftover tabs through
the unsaved prompt, whose Yes the release lockout refused.

- Binding a released tab marks its save point (EditNestForm.MarkSaved).
- Make Nests re-reads each merge target and merges at the revision now on
  the server; one saved while the release runs is still refused.
- End() closes the tabs it did not save without the prompt
  (CloseDiscardingChanges), after taking them off the release list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:50:22 -05:00
ajandClaude Opus 5.5 a437162039 chore(help): pin help v0.7.2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:34:26 -05:00
ajandClaude Opus 5.5 1caf9b68bb fix(ui): fit the Allow common line hint on one line
The hint wrapped to a second line that Drawing Properties clipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 16:27:14 -05:00
ajandClaude Opus 5.5 4aa035af2f feat(diagnostics): mark plate-edge findings on the plate and say "outside the work area"
The Layout label counted parts off the plate or inside the edge spacing, but
nothing showed which part or where, so the user had to hunt for it. Each edge
finding now gets a canvas marker like the overlap and spacing ones: a red
crosshair where a part leaves the sheet and an orange ring at the gap to the
edge, in every visible display mode, with hover text that carries the finding's
message. Markers are retained across edits like the other overlay findings
(only a changed part's marker drops), and a sheet size, edge spacing or outline
change clears them. Validate Nest already located these findings through
PostVerificationFinding.Location.

"Near edge" / "inside the edge spacing" read as a spacing rule rather than a
placement problem, so the label, overlay messages, PlateEdgeReview findings
and the Validate Nest category now say the part is outside the work area. Off
the plate keeps its wording. The PostVerificationKind.EdgeSpacing name stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:28:52 -05:00
ajandClaude Opus 5.5 7cc0e6f126 fix(ui): count parts off the plate in the Layout status label
The status bar's "Layout: OK" ran its own snapshot check of overlaps and
part spacing, separate from Validate Nest and posting, so a part dragged
off the sheet still read OK.

The plate capture now owns the sheet frame (sheet box, work area,
outline), and the worker runs PlateEdgeReview's material check on it:
"N off plate" and "N near edge" join the label. Snapshots hold no leads,
so leads past the sheet stay a Validate Nest/posting finding. The
geometry stamp now includes edge spacing and outline so changing them
marks the result out of date. Bare-part captures (Plan Cutting) have no
frame and are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 10:38:02 -05:00
ajandClaude Opus 5.5 7d498f2b41 fix(common-line): keep copied joined parts joined when pasted
Copy and Cut paste through ActionClone, which added each clone to the plate
but never recorded the groups the sources were in, so a pasted common-line
pair came out as separate parts held to the part spacing. ActionClone now
reads the copied parts' groups when it starts (before Cut removes the
sources and their groups) and joins each paste's clones the same way, at the
source group's kerf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 09:27:39 -05:00
ajandClaude Opus 5.5 f09a9ad705 feat(common-line): move joined parts as one and add a part right-click menu
Moving one member of a common-line group used to break the group, so a drag
or nudge silently undid a join. Selecting a member, by click or window, now
selects its whole group, so drag, nudge, push, rotate and align carry the
joined parts together and keep their shared spans.

Right-clicking a part now offers Join Common Line and Remove Common Line,
running the Plate > Arrange commands on the selection. A right-click on a part
keeps its selection (or selects it with its group) instead of clearing it, so
the menu has something to act on; a right-click on empty plate still clears.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:52:54 -05:00
ajandClaude Opus 5.5 78a5ad88e2 feat(drawings): explain an empty list that Hide Fully Nested Drawings emptied
With the filter on and every drawing nested, the Drawings tab was a blank
list with no hint why. It now shows a note that turns the filter off when
clicked. The filter is never turned off on its own: it is the user's
choice and would stay off when new drawings arrive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:21:27 -05:00
ajandClaude Opus 5.5 5eb7fd920b fix(slide): let a part slide along a neighbor touching it through PEP rounding noise
Six-decimal incremental PEP programs round arc centers, so an arc's end
tangent misses its neighbor's by about gap / radius (1.6e-5 rad on the
offset 5/32" fillet in the reported nest). Two parts resting one spacing
apart then gave the slide contact a Minkowski cone a hair wider than a
half-plane, which the classifier read as the whole plane: every direction
blocked, so the top part could not be pushed sideways along the part below.

SlideContact now reads a cone up to TangentSlack (1e-4 rad) past a
half-plane as a noisy half-plane and widens its boundary and concave-ray
comparisons by the excess, so near-tangential slides read as tangential.
A direction admitted that way can enter material by at most travel x
excess at that contact, and every other contact is still classified on its
own; wider cones remain real overlap and block. Raising JointTolerance was
not needed for this case and is left at 1e-6.

Whole-job benchmarks (synthetic nests plus the PEP job at 3 and 60 parts,
--parallel 1) are identical before and after for every engine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:13:54 -05:00
ajandClaude Opus 5.5 dd0c31271a fix(push): let a part push away from a neighbor it touches at the spacing
A part placed exactly one part spacing from its neighbor touches the
neighbor's offset contour. PEP drawings round arc centers to six decimals,
so their offset contours carry hairline kinks and joint gaps where the parts
touch; the slide kernel then classifies that contact as blocking in every
direction, and Push moved the part 0 even straight away from the neighbor.

An obstacle whose bounds end at or behind the moving part's trailing side
can only fall further behind, so Push and PushBoundingBox now skip it
(within Tolerance.Epsilon) instead of only when strictly behind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:37:53 -05:00
ajandClaude Opus 5.5 f88b805e68 fix(cut-off): select head-locked cut-offs by click and window
Clicking and window selection paired the cut-off program's codes as
rapid/cut at even positions. A head-locked far-edge segment carries lock
comments (and, cut from the far edge, a rapid return) between its cuts,
so no pair matched and the line could not be selected, though it drew.

CutOff.CutStrokes now walks the motions once for the renderer, click
hit test, window selection and the Sheet Cut-Off preview, so what is
selectable matches what is drawn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:36:58 -05:00
ajandClaude Opus 5.5 2b23150801 fix(reporting): open the report viewer centered on the main form
FormStartPosition.CenterParent is not applied to modeless windows, so the
viewer opened wherever Windows placed it. Center it on its owner in OnLoad,
shrunk to fit and kept inside that screen's working area.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 19:58:36 -05:00
ajandClaude Opus 5.5 b026bd19d2 feat(common-line): place parts on common lines automatically at a chosen level
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>
2026-11-05 19:38:00 -05:00
ajandClaude Opus 5.5 f9b2713420 feat(common-line): cut each shared line once through Plan Cutting and the CI Fiber post
N26-295's PT42 pairs saved spacing in slice 1 but every shared edge was
still cut twice, costing a pass per edge and risking a freed part
tipping under the head on the second pass.

Plan Cutting now cuts a plate's common lines once unless the plate is set
to cut them twice. Each group is cut member by member, owner before
partner (a captured dependency that search and replay enforce, so a kept
order cutting the partner first is a constraint conflict): the member's
holes, then the shared cuts it owns as open CommonLine centerline cuts
entered through the notch on the span's axis, then its remainder pieces,
each ramped in from the kerf the shared cut opened. Capture marks the
shared spans as tabs on the owned clean copy, so replay's contour
accounting proves each perimeter is cut exactly once; a lead that fits
at neither end refuses as UnsupportedGeometry naming the span, never
shortened. Material still comes from the drawing, so utilization,
quantity and overlap results do not change. Members are never
support-tabbed; locked or excluded members and broken groups refuse.

The CI Fiber post writes shared cuts in the cut-off form with a lead-in
and no G41/G42, and remainders in the contour form on their perimeter's
compensation side, named by a remainder comment. Cincinnati CL (whose
PreApplied mode offsets nothing) and Gravograph refuse such nests, as do
the desktop and console for any post without ICommonLinePostProcessor;
nothing falls back to a silent double cut. Plate > Arrange > Cut Common
Lines Twice keeps slice 1's behavior, persists with the plate and makes
an open plan stale.

Pre-post review now checks each span is cut once, on its centerline, by
the member cut first, and the rapid check treats a finished member's
remainders as an obstacle. Compensation start in void and cancel in
kerf are unconfirmed on the CI FIBER 4020; release waits on a 3/16
stainless coupon trial.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:51:42 -05:00
aj b630b50b7b fix(ui): label the server jobs button Stop Job
The job it acts on is shown as Stopping and then Stopped, and the rest of the
app talks about stopping server jobs, so the button now says Stop Job too.
2026-11-05 15:00:15 -05:00
ajandClaude Opus 5.5 1d87d212de feat(common-line): place parts one kerf apart to share an edge
Expensive alloy jobs (N26-295, 310 stainless) could not fit their last
PT42 at full part spacing; the plate was squeezed to 0.2 spacing with a
part 0.14 from the edge. Parts whose straight edges face each other one
kerf apart are cut on the same centerline, saving the spacing along each
shared edge.

Plate.CommonLines holds groups of parts and the kerf they were joined
at. Shared spans are derived from the placed clean perimeters, never
stored, so a moved member breaks its group instead of leaving a stale
span. Plate > Arrange > Join Common Line slides the selected parts
together at the plate's kerf; Remove Common Line ungroups them. The
plate draws shared spans as a dashed centerline.

Both spacing checks hold group members to the kerf instead of the part
spacing, and a new pre-post Common lines check reports broken groups, a
changed kerf, and leads that run into a partner. Groups persist as part
positions in an optional commonLines array that older readers ignore.

This first slice still cuts every shared edge twice; single-pass cutting
through Plan Cutting and the posts comes next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 14:16:13 -05:00
ajandClaude Opus 5.5 924763493d feat(report): place part IDs wherever they fit best, holes included
Ring-shaped parts (a plate with a large center hole) were left unlabeled:
the material at their pole is too narrow for the ID, and the label was
never tried anywhere else. Group IDs were likewise only ever placed beside
the dotted outline, so a cramped cluster lost its ID or straddled the
sheet edge.

A part whose pole spot is too tight now takes the roomiest clear spot
within its own extents, such as its empty hole. A group ID takes the
roomiest spot inside its outline or beside it; with none clear it may
cover the group's own parts near the center, since operators read part
detail from the parts list. No ID sits across the sheet outline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 13:01:39 -05:00