Commit Graph
2290 Commits
Author SHA1 Message Date
ajandClaude Opus 5.5 f4fb6e1f3d feat(reporting): show clamps and sheet rulers on the plate layout view
Operators reading a printed nest report could not see where the machine
clamps sit or which ones must be pulled before cutting, and had no way to
measure positions off the layout. Plate diagrams now draw active clamps as
hatched rectangles (labels keep clear of them) and removed rail clamps as
dashed, crossed-out rectangles over the parts, with a legend line only on
plates that have clamps. A horizontal ruler below and a vertical ruler left
of the drawing read the sheet's plate coordinates in job units; the
diagram is fitted in two passes so the ruler bands match their numbers,
and part labels never enter them. Rulers draw as their own content stream
so the diagram stream holds only the sheet and what is on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 08:02:24 -05:00
ajandClaude Opus 5.5 bffcd52db1 fix(plate-view): recompute part labels after a drawing edit
A part's sequence-number label point was computed once from the drawing's
program and cached forever. Editing a drawing in the converter replaces
that program, sometimes in a different frame, so the labels of every
placed copy stayed at the old frame's point and drifted off the parts.
Keep the cached point only while the drawing program (and its rotation)
is the one it came from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 07:48:35 -05:00
aj b329dd6c6b feat(avalonia): open several nests in tabs along the bottom
Each open nest gets a tab along the bottom of the window, above the
status bar: the shown one is white with a blue line, a dot marks unsaved
Auto Nest results, x closes it and + opens another. Inside a tab, Plates
and Drawings tabs sit at the top of the left panel (Drawings lists each
drawing's nested and required quantity) and the plate header with
first/previous/next/last sits over the plate view. Each nest keeps its
own plate and zoom when you switch.

File > Open takes several files, and a file already open switches to its
tab. File > Close and Close All, View > Next/Previous Nest and
Ctrl+Tab/Ctrl+Shift+Tab switch or close nests. This version cannot save,
so closing a nest with Auto Nest results, or exiting with any, asks first
(Cancel is the default). Other nests can be opened and viewed while Auto
Nest runs; the nest being nested cannot be closed until it finishes.
Several nest paths on the command line open in tabs.
2026-11-09 22:30:12 -05:00
aj c32f3e5df3 feat(desktop): track the open nests as tabs
NestTabs holds the nests open in the desktop app and which one is shown.
Opening a file that is already open switches to its tab instead of
loading it again, and a file that fails to load adds no tab. Closing the
shown tab shows the one to its right, else the one to its left. Each tab
records whether it has unsaved changes, so the host can ask before
closing it or exiting.
2026-11-09 22:30:12 -05:00
aj 0d7207e58e feat(avalonia): remember the main window's size, position and state
The Avalonia main window restores its last size, position and maximized
state from the shared user settings and saves them when it closes, keeping
the normal bounds while maximized. On Windows it reads what the WinForms
app saved: WinForms stores the outer window and Avalonia sets the client
area, so the size differs by the frame between the two apps. --snapshot
skips it, so its image never depends on the user's settings.
2026-11-09 22:05:09 -05:00
aj 53e5b78e3c feat(desktop): shared window placement in the WinForms settings format
WindowPlacement reads and writes a window's position, size and maximized
state under the names the WinForms app already stores (MainWindowLocation
"x, y", MainWindowSize "width, height", MainWindowState), in screen pixels.
A minimized window restores normal. FitTo drops a position that is on no
current screen and a size too big for its screen, as the WinForms main
window does when it restores.
2026-11-09 22:05:09 -05:00
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