Clamps could only sit on the origin-side edge and be edited as a list in
Nest > Clamps. Setting them up against a real sheet is easier by dragging
them where they are, so Plate > Edit Clamps adds an edit mode: drag a clamp
along its edge or onto another edge, click near an edge to add one, click a
clamp's locating dimension to type its distance from the origin, Delete to
remove. Dimensions measure to the clamp center or its near edge
(remembered per user). Edits replace the nest's rail so every sheet follows,
like the dialog.
Rail clamps now carry a ClampEdge (origin side, far side, origin end, far
end), positioned from the origin along that edge. Origin-side clamps keep
their ids and files; other edges persist in optional per-edge arrays in
.nest files and clamps.json, omitted when empty.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Changing a rate, the pierce time or the machine rebuilt the timeline and
kept the playhead's old time. That time pointed at a different move on
the retimed timeline, so the torch jumped around while settings were
adjusted. When the new timeline has the same moves, Load now finds the
same move and how far through it the torch was (CutSimulation.TimeAt
inverts FractionAt, acceleration included).
The in-progress rapid was drawn whole with a dashed pen. GDI+ cost grows
with dash count: zoomed in, one paint of a long rapid can take seconds
to minutes. It is now clipped to the visible area, as finished rapids
already were.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NestProgress gains PreviewSheet, a detached plate for the stock the preview
parts lie on; Irregular builds one per stock. PlateView draws it (size, edge
spacing, clamps) in place of the plate while the preview is shown and drops
it whenever the preview is cleared or the plate changes. Auto Nest zooms out
when a trial sheet falls outside what it has already shown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The overlay re-stroked the whole finished trail on every paint: about
275 ms at zoom-to-fit on a 392-part plate and over 11 s zoomed in, where
dashed rapids spanned millions of on-screen dashes.
The finished trail is now cached in a bitmap at the current scale, so a
pan or playback frame blits it and draws only the moves since. A wheel
zoom stretches the cached image until the wheel has been still for
150 ms, then redraws it sharp. When the trail would be too large an
image, each paint draws only the moves on screen and clips straight
rapids to the view. Arcs are added as GDI+ arcs instead of 5-degree
polylines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plate > Simulate Cutting replays the active plate in cutting order: the
torch traces cuts, rapids and pierces timed like Calculate Cut Time, with
play/pause, scrubbing, 1x-250x speed, step and part/cutout jumps. Parts
the torch has not reached are drawn muted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Operators sometimes pull a clamp for a manual layout, but only Auto Nest's
last-resort search could take clamps off a sheet. Right-clicking a clamp in
Select mode now offers to remove it from that sheet or put it back, reusing
the existing per-sheet RemovedClamps, so the zones, cut-time charge, display
and .nest persistence all behave as they do for Auto Nest removals.
The change is refused while a nest run or fill may be reading the clamps,
sharing the guard the Clamps dialog already used.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Choosing a part from the editor's drawing list armed the pickup only in
Click. Moving the mouse more than 3 px before release started a
drag-and-drop, which takes mouse capture, so the list never raised Click
and nothing attached to the cursor on the plate.
The list now raises DrawingPickedUp on a single left press over a row
and the editor arms the pickup there. Dragging onto the plate still
drops a part at the release point; a completed drop clears the pickup so
a second copy does not follow the cursor. Presses below the last row,
right presses and the second press of a double-click do not pick up.
A disposed PlateView stayed subscribed to its plate, so a plate that
outlived the view kept rebuilding the disposed view's layout and
raising its events; Dispose now detaches the part added, removed and
reordered handlers.
ObservableList.Reorder keeps repeated references, but the reorder
handler mapped each part to one layout, so [a, a, b] collapsed to one
shared layout for both occurrences of a. It now reuses each existing
layout once and creates one only when none is left.
PlateView now follows Plate.PartsReordered, which an applied cutting
plan raises once per changed plate: it puts its layout parts in the
plate's order (the drawn part numbers are the cutting order), marks
them dirty so new programs are redrawn, and raises its own
PartsReordered. The overlap overlay treats a reorder like any other
part change and marks its report out of date.
TestAssemblySetup set the unhandled-exception mode and a ThreadException
handler from a module initializer. WinForms stores both per thread
(ThreadContext/NativeWindow thread statics), so they applied only to the
runner thread that loaded the assembly, never to the STA threads the
tests run on. There, an exception escaping a window message or a
BeginInvoke/SynchronizationContext callback still opened a modal
ThreadExceptionDialog in an interactive session, or was silently ignored
otherwise; the handler would also have logged and swallowed it.
Replace the fifteen copied RunSta helpers with StaTestThread.Run, which
selects UnhandledExceptionMode.ThrowException on each STA thread before
it creates a window, so the exception propagates to the test body and
fails the test. Each class keeps its own timeout and message; all
threads are now background threads. Remove TestAssemblySetup.
StaTestThreadTests covers a throwing BeginInvoke callback through
StaTestThread (fails with the original exception) and, as a control,
the default mode on a plain STA thread (the exception goes to the
thread's handler and never reaches the test body). Cross-compiled on
Linux; Windows execution is the windows-desktop CI job.
The new windows-desktop CI job surfaced several tests that had never
actually run on Windows:
- SeparatorPenLifetimeTests expected Control.Disposed to fire only
once; current WinForms re-fires it on every Dispose() call.
- EditNestFocusTraversalTests assumed the Plates tab was selected by
default, which stopped being true once Drawings became the landing
tab; select it explicitly before asserting focus.
- NestReportExportTests asserted an exact plate count that ignored
PlateManager's trailing sentinel plate, and didn't expect the nest
name in the busy-export message.
- AtomicReportFileTests only expected IOException when replacing a
directory target; Windows raises UnauthorizedAccessException instead,
which callers already treat the same way.
Also register a ThreadException handler so an exception inside a
WndProc callback fails the test normally instead of popping a modal
dialog that blocks the desktop.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The reorder event snapshot could previously show the reverse pass's
stale timeout label. Set a sentinel before each click so the
ProgramChanged handler can only observe the label applied by the
current operation's highlighting pass.
Quality review of 50884da found the unshown RichTextBox oracle compared
cached CRLF text against the highlighted editor's native LF-normalized
text, which would fail on first Windows execution. Compare generated
text with normalized line endings on both sides instead, capture the
status label and preview state inside the ProgramChanged handler to
prove fallback-before-notify ordering, document the HighlightSpan
UTF-16 and rule-index contract, and pin the null-text argument check.