The Cincinnati CL post always drew an "incomplete final rapid sequence"
warning because it never declared IPostVerificationSupport. It writes etch
features before cuts, inserts or drops rapids, and with part sub-programs
rapids to each part's corner, so it cannot declare it unconditionally.
IPostVerificationSupport gains a nest-aware PlacedProgramOrderChange(nest)
(null = preserved; otherwise the reason, shown in the warning). The CL post
answers it from its own writer: part sub-programs and smart rapids off, and
every part's and hole's posted motions exactly equal to its placed ones.
Plan Cutting cuts scribes first, so planned nests qualify. CNC output is
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plans every occupied plate of every .nest file in a local folder the way
Plan Cutting does after clearing lead-ins and cut-offs, and writes one
JSON line per plate with its outcome, warnings, fallbacks, expansions,
time and spacing pairs. It is how the reliability sweep found its
failure classes over 1,260 production plates; the nests and the report
stay outside the repository, and the test skips without the folder.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contour recovery tries each outside entry's hole-tour search first for a
part with at most 16 holes, and the reading-order sweeps after them; once
the searched entries had spent the recovery allowance it jumped straight
to the widened tries and never swept. A frame cut from inside its own
cutout, after the strips nested there, is often routed only by a sweep,
which leaves the cutout on its far side toward the outside entry beyond
it, so such plates fell back to the safest route with head raises or to
no plan at all. The sweeps now still get their few expansions per hole,
bounded by the whole budget. On the production corpus six plates went
from a safest-route or head-raise fallback to a strict plan, five kept
fewer raises, one blocked plate gained a safest route, and none got
worse; the problem-nest corpus is unchanged in every outcome and share.
No synthetic plate reproduced the exhausted allowance yet, so the corpus
guards this change.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
With a pierce diameter set, every internal contour whose configured
pierce is inside the zone is resolved by flattening the cutout, finding
its pole and eroding it, and the search emits a part's whole chain again
on every expansion, so on parts with dozens of such holes almost all of
the planning time went there: three 1,600-motion parts took 41 minutes
and timed out in production. The resolution is a pure function of the
hole, the entry, the configured lead-in, the zone and the turn, so each
preparation now keeps it by contour ordinal and reuses it, unchanged,
across its emissions and copies. The same plate plans in 148 s.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A fillet whose circle is tangent inside the arc it continues, as a
keyhole's slot ends are, gives the supporting-circle query of the pair
no real root: the circles' distance is the radii's difference up to
rounding, so the intersection comes back NaN, and the unfiltered check
that runs before any adjacency handling refused the whole part as
"nonfinite program coordinates". On the production corpus 17 plates
failed this way. Adjacent curves are now resolved from their shared
vertex before the native query would run, so no query runs for them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two curves that meet end to end share that vertex, but every contact
check asked the native intersection query about them, and it is
ill-conditioned there: two circles meeting at a shallow angle (a fillet
continuing into a larger arc) or a line meeting its fillet put their root
up to 1e-5 off the joint when the DXF arcs were rounded apart from their
neighbors by 1e-9. Material validation then refused the part as
self-touching, and the open-cut proof read a tabbed perimeter's fillet
joint as a loop, so with support tabs on every such part lost its
retention proof and its plate came back blocked. On a corpus of 1260
production plates this was the largest planner failure class: 61 of 67
NoSolutionWithinBudget plates, all planning once the joint is exact.
Contacts now report an adjacent pair's shared vertex itself and derive
the pair's other intersection from it analytically (two circles meet
again at the vertex's mirror image across the line through their
centers; a line meets a circle again at the far end of its chord),
keeping it only on both finite spans. No tolerance grows: a genuine
second crossing beside the joint is still reported, and overlapping
spans still refuse.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A server Auto Nest result could only be reviewed in the session that sent it,
and nothing stopped two desktops from keeping the same result. Phase 3 adds
My Server Jobs, so results that finished while OpenNest was closed are easy
to find, and records each review on the server.
- Server: GET /api/nest-jobs lists jobs newest first with state, engine,
submitter, times, queue position, review and the worker's summary (parts,
sheets, utilization, unplaced). It filters to active, finished or
unreviewed jobs and to a submitter (a bare user name matches that user on
any workstation). An ETag turns unchanged polls into 304s. Schema v17 adds
nestId, review and summary columns, only where missing.
- Keep once: POST .../keep and .../discard take If-Match on the job revision
and change an unreviewed result only once. A second desktop gets 409 with
who kept it. DELETE removes finished jobs. A succeeded job keeps its
package until it is reviewed, so a later session can rebuild the job.
- Retention: OPENNEST_JOB_RETENTION_DAYS (default 7) deletes finished jobs
with their results. It runs on claims and list reads. A malformed value
fails startup instead of deleting results early.
- Worker: reports the result's summary with the transcript. A job cancelled
while its result arrived keeps no summary (found in the Docker smoke run).
- Desktop: Nest > My Server Jobs polls the list every 3 s; polling rather
than per-job SSE because the list must show other desktops' jobs and
removals. Open rebuilds the job from job.json against the open nest's own
drawings. New per-drawing geometry fingerprints make the freshness check
work across sessions. Cancel Job and Remove act on the selected job. A
status-bar item counts this user's results waiting for review; there is
no popup. Auto Nest's review now records Keep on the server before it
commits, and records Discard too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A server Auto Nest showed only "Nesting on the nest server" for however long
the job took, and Stop could take a whole 20-second heartbeat to reach the
worker. Now the desktop shows the same Best plan so far strip a local run
does, and a job that ends while OpenNest is in the background says so.
- Worker: RecordingNestingEngine taps each solve's progress. NestJobReporter
stores only the latest stage and plan, at most once every two seconds, so a
chatty engine cannot flood SQLite. Each round also reads the cancel flag,
so Stop reaches the worker within one interval (about 2 s in the Docker
smoke test).
- Server: schema v16 adds per-job revision, progress, plan and planRevision
columns, which the upgrade adds only where missing. PUT .../progress and
.../plan renew the lease like a heartbeat. GET .../events is Server-Sent
Events, woken by an in-process change signal: plan and job events with
revision ids, keep-alives, Last-Event-ID resume, and the final state always
sent before the stream ends. GET .../plan serves the polling fallback.
Progress goes over HTTP because the worker never opens the database
(another host, NFS).
- Desktop: NestJobWatch follows the stream. When the stream drops it polls
once and reconnects, and it polls throughout when the server has no stream.
RemoteNestPlanPreview draws worker plans from the client's own request,
including cutout-trial frame proxies. ServerNestNotice shows a tray balloon
(a toast on Windows 10/11) when the job ends while OpenNest is not in
front. A real toast would need an AUMID shortcut that Velopack does not
promise. Clicking the balloon brings the review forward; nothing is kept
without Keep.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Large nests tie up the workstation that runs them. In Database mode, Auto Nest
can now queue the job on the nest server, where a worker container solves one
job at a time under its own CPU and memory limits. The job has no time limit,
so a throttled worker finds the same nest a fast one does, only later.
- Server: /api/nest-jobs queue (schema v15). The worker holds a job on a
heartbeat lease. When the lease lapses, the job goes back to the queue,
or fails after three attempts. Only one job runs at a time. Packages and
results are opaque to the HTTP server, which stays off Engine and IO.
- Worker: OpenNest.Server.Worker in the same image (OPENNEST_ROLE=worker),
reaching the server over HTTP only. It records each engine answer with
RecordingNestingEngine and gives its job back on shutdown.
- Desktop: "Nest on the server" in the Auto Nest dialog, for built-in
engines only. The client replays the recorded answers with
RemoteNestSubmission, so validation, the review and Keep run locally
against the client's own drawings, exactly as after a local run. A
drawing edited after sending refuses the result. If the server cannot
take the job, the user may nest locally instead.
- compose.server.yaml adds the throttled worker service. The Dockerfile,
.dockerignore, CI paths and the deploy script now include the worker's
projects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reopening a nest regenerated its cut-offs with built-in defaults (no
overtravel, a different minimum segment) rather than the machine's
settings it was made with, so a nest saved, reopened and posted could
cut scrap that never separates. Plates now record the settings their
cut-offs were last generated with, and files carry them; older files
still load with the defaults.
Saving over a nest file truncated it before writing, so any failure
part way lost the only good copy, and the error reached the user as an
unhandled exception after the save path had already changed. Nest and
settings files are now written beside the target and swapped in, and a
failed save is reported with the old file left intact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ahead of the new laser schedule, three ways a post could silently cut
with the wrong process, and one way its settings silently reset:
- Cincinnati CL wrote no G89 when no cut library matched the nest's
material, thickness and gas (or no etch library for etch features),
so the controller cut with whatever library it loaded last. It now
refuses, unless the new "Require libraries" setting is turned off.
- Cincinnati CL declared G20/G21 from its own setting without comparing
the nest's units, so a metric nest posted 25.4x too large. It now
refuses a mismatch.
- CI Fiber posted any material missing from its table as the default
code (MSN), so stainless or aluminum ran with mild-steel technology.
A named, unmapped material is now refused; the default code covers
only nests with no material.
- Posts saved their settings beside their DLL, inside the install
folder a Velopack update replaces, so every update reset them to the
shipped defaults. Saves now go to %APPDATA%\OpenNest\Posts and the
shipped JSON only seeds them; constructing a post no longer writes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A support-tabbed part that no single tab can hold level was limited to its
one steadiest entry. Two strips stacked in a frame's cutout then blocked
the plate: from the upper strip's only entry, every route to the lower one
crossed holes already cut. Its other entries clear of inside corners now
follow the steadiest, in the search and in contour recovery; the part is
still tabbed and still warned about. Parts a tab can hold keep their
steadying entries.
The unverified headline also named fixed lead-ins when the only warning
was a part that may tip; it now says what needs review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every part the support grid could not hold level got a tabbed perimeter
with the outside lead-out after its tab, and planning refused any lead-out
after its own open cut, so with a lead-out configured no such part could
be planned. Pre-post review already proves such cuts release nothing (the
lead-out stays out of the tab gap); planning now runs the same proof, so
planning and review agree and a lead-out that reaches into the tab is
still refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--autonest without --quantity nested one copy of each drawing, so a
118-part job of 67 drawings solved as 67 parts and reported success.
Default to the required quantity less copies on other plates, which the
run keeps; drawings without one are left out, as on the desktop, unless
no drawing has one. --quantity still overrides.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Search insert selections as boxes with a counted beam search over maximal
free rectangles, pair low-density parts with their half turn, then gap-fill
with the frontier packer. The router takes it on seated outline area.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Compare cutout proposals, frame angles and bundles by insert outline area,
and add bounded largest-first packer candidates so big parts get the first
move in an empty hole. On the real job the four frames go from 54-70% to
77-81% filled.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The spiral used only the least-waste rows and columns. Those often make
one pair of blocks the larger on both axes, so it overlapped itself and
placed nothing, leaving Fill at two blocks. It now also tries every
four-block combination and takes one that holds more parts than both the
least-waste spiral and the two-block layout, so ties keep today's layout.
A 9 x 6.5 part on a 60 x 120 sheet goes from 109 to 112.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An empty canvas corner gave no sign of whether a check was still running,
which matters on large nests. A Layout section beside utilization now
follows the automatic check: checking, OK, or findings in red, with the
full wording in its tooltip. It has its own label so rechecks never
replace the last command's status text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the check mark: a clear result, and an automatic recheck after one,
now draw nothing. Findings and failed, canceled or incomplete checks keep
their text, and views without automatic rechecks keep their pending text.
The status bar still reports a manual check's result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pending or running recheck no longer swaps the green check for status
text. A clear result stays as a gray check, and earlier findings stay as
dimmed text, neither of which claims anything about the edited layout.
Views without automatic rechecks still ask for a manual check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The overlap check now runs the pre-post part spacing scan on its snapshot,
so the canvas label and posting agree. Too-close pairs are counted in the
label and marked with an orange ring showing the gap, with hover details.
A fully checked plate with no findings draws a green check instead of
text; the status bar keeps the wording. Changing the plate's part spacing
now stales the report, and repeated parts share one offset outline so
automatic rechecks stay fast.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Fill" said nothing about what the engine does, since every engine fills.
It is now Lattice, and Fill stays an alias, so saved selections, scripts
and API requests keep working. Engine descriptions are rewritten in plain
language and shown beside each name in the desktop engine lists; the main
toolbar's tooltip also says when Fill Area falls back to Lattice.
Docs also cover the leftover placement from the previous commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A quantity-limited fill trimmed the parts farthest along the trim axis,
which could take one half from each of two pairs. TrimToCount now finds
interlocked pairs and keeps or drops them whole (from d1d68a91), so the
spare space stays one usable cell.
After a fill, leftovers now run in two orders, pack-first and pairs-first,
and the strategy's comparer keeps the better whole layout, so its ranking
of this step cannot get worse. Even quantities go in as best-fit pairs,
may turn a quarter when unrestricted, are tried in every remnant and are
ranked with the layout around them. Each placed pair slides left and down
against true outlines to take the space its bounding box could not.
On compare.nest Vertical Remnant goes from 6665.1 to 5862.6 and Lattice
from 7200 to 5915.8. Nine characterization pins change from the trim with
counts unchanged; the fill pins now also check layout validity. Vertical
Remnant's golden layout shortens from 28 to 26.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
F1 opening help kept taking over from placing a part. Help stays on
the Help menu and F1 belongs to the Clone tool alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Whole-job fills treated clamps as occupied space, so a clamped edge cost
a row: 8.25in circles stopped at 96 on a sheet the plate fill packs with
102. When one drawing still has unmet demand, also fill the sheet as if
the clamps were absent, drop the parts on them and keep the better
layout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cut-off always runs away from its fixed edge: the origin side, the only
edge guaranteed to be a real sheet edge. The part order is proposed as a
traveling-salesman path over one point per placement, and a cut-off's
point was the middle of its line. The route did not know the tool must
arrive at the line's start and leaves at its far end, so the parts
before a sheet-splitting line finished level with its middle, and the
rapid down to its start crossed parts already cut.
Each placement now has an entry and an exit point; they differ only for
cut-offs (start and departure). Nearest neighbor and Or-opt go on from
each part's exit, and 2-opt costs a reversed run along its own directed
legs; with no cut-off on the plate the exits are not passed and the
symmetric route is unchanged. The part cut before a cut-off faces its
start.
Cut-off direction is not chosen here: the order reads each cut-off's start
and end from its program as given, and the joint search may still reverse a
head-locked far-edge segment.
When a blocked approach teaches an order rule, the search backs up to the
earliest part that approach crossed, keeps the parts before it and
re-plans the rest. The last kept part was planned facing the part that
followed it in the old order. If the new order puts another part after
it, its entry and exit still face the old one, so the plan ends with a
stale exit that a kept-order re-plan of the same order would fix.
Now that part is planned again, in the same place, facing its new
successor; when its successor is unchanged nothing is re-planned.
The locked-among-regenerated fixture's plan changes: rapid 67.60 ->
60.22, expansions 528 -> 268 (digest updated). Learning "cut the
cutoff first" now takes 40 expansions rather than 22, because B is
planned again to face the cutoff; retrying every entry of B took 60.
A closed contour around its center with at least 270 degrees of arc on
one center and radius now counts as a keyed hole, in its own row after
round holes of that diameter. Curved slots stay out: their long arcs wrap
around solid material, so the center is outside the contour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Expand Spacing measured every ring pair of every part pair for each trial
move, so a packed layout of parts with holes took minutes per spacing
probe. Skip ring pairs, far part pairs and off-path parts whose bounds
already settle the answer.
Every pair was also held at its full starting gap, so parts could not
move toward anything and most layouts stayed at their entry spacing.
Part pairs may now close to the spacing being tried, never below a
smaller starting gap. Cut-offs and clamps keep their full entry gap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A nest with no material, grade or thickness now takes them from the
library record instead of warning that the drawing differs and dropping
its cut pins. The machine's spacing and cut-off settings follow. Later
drawings still get the usual difference warning, and drawings from files
carry no thickness, so they leave the nest blank.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plate > Set as Default and Nest > Edit's Defaults tab only changed the
open nest, so new nests never picked up the values. Set as Default now
also saves the new-nest defaults, and the Defaults tab gains an opt-in
"Use these defaults for new nests" check.
Machine thicknesses can now set part and edge spacing. A nest takes them
when its material, grade, thickness or machine changes and when a release
creates it; BOM import seeds its groups from them. Spacing edited in the
same dialog stays, and only an untouched trailing sheet follows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Equal-cost plans break ties by the largest edge offcut, from any of the
four edges. A caller that wants the leftover off one way, such as a
shop that keeps only end drops, or a benchmark scoring the used sheet
height, had no way to say so. On the converted Nestbolt jobs the
extended Irregular search then found plans whose larger offcut is a
strip down the side, which Nestbolt scores as worse: b-blaz1-strip
3.601 -> 3.867, b-shirts-strip 2.674 -> 2.995.
NestJobOptions.OffcutCut (a CutOffAxis, default null) counts only the
strips one cut along that axis frees, for salvage credit and the
compactness tie-break alike: Horizontal the full-width bottom and top
strips, Vertical the full-length sides. PlateRemnant.LargestOffcut takes
the axis; NestJobCost, the time limit and clamp-removal recovery pass
it on. Unset, nothing changes.
Benchmark manifests accept "offcutCut": "horizontal" or "vertical".
With horizontal, one CPU, 20 s limit, Default:
b-blaz1-strip 3.934 -> 3.333
b-shirts-strip 2.995 -> 2.610
m-box-deep 1.918 -> 1.276 (Nestbolt 1.251)
Manifests accept only the words horizontal or vertical (any case); a
list such as "horizontal,vertical" is rejected. PlateRemnant rejects an
undefined axis. Fixed strategies keep their own no-salvage tie-break
and ignore the option; desktop, Console and MCP do not offer it yet.
Mutation reds: 14/14 (offcut-mutations.py).
Under a time limit, Irregular often finished in about a second and left
the rest unused: on the converted Nestbolt b-jakobs1 job it used five
sheets where four fit.
NestJobOptions.SearchUntilCancelled asks an engine to keep searching
after its normal search until the token is cancelled.
DeadlineNestingEngine sets it. Irregular then re-runs its six variants
with seeded per-type weights on its placement rule (seeds 1, 2, 3, ...,
each weight within e^+-0.4), reports each plan that beats its best,
then re-plans that plan's tail and reports any gain. It skips this when
the token cannot be cancelled, nobody listens for progress, nothing is
placeable, or the job has one part type, so unlimited solves are
unchanged.
Default now runs Irregular last (Rectangles, Circles, Irregular) so
Circles still runs, and chooses only after every candidate has run, in
tie-break order, so the run order never changes the choice. Unlimited
choices match the old run order {Rectangles, Irregular, Circles} except
when three candidates are near-tied within the 1e-9 comparison
tolerance without being transitively ordered (only on all-round jobs,
where Circles bids).
b-jakobs1 on one CPU, 10 s limit: 5 sheets (4.500) -> 4 (3.751).
"rotations": [0, 90, 180, 270] mapped to automatic rotation, which
allows any angle: Rectangles placed parts at minimum-area angles such
as 279.8 degrees in jobs that allow only quarter turns. Quarter turns
are now a 90 degree sweep from 0 to 270. Without "rotations", a part
still rotates automatically (any angle), as before 2dab9232; the doc and
comment that called that the quarter-turn default are corrected.
DeadlineNestingEngine used CancelAfter, a thread-pool timer. A CPU-bound
Parallel.For keeps pool threads on their own local work, so the timer
callback can run seconds late; pinned to one CPU it waited for the
whole best-fit search (m-sign-3 under a 10 s limit returned at 64.5 s).
A dedicated background thread now waits for the limit, or for the
solve to finish, and cancels; the solve joins it before returning.
A test runs a CPU-bound Parallel.For from a pool thread under a 200 ms
limit and requires the solve to end within 3 s; with CancelAfter it
took about 7 s.
A manifest part may list "rotations" in degrees: [0] locks it, [0, 180]
locks it at 0 with the half turn allowed (Allow180Equivalent), and
[0, 90, 180, 270] is the automatic quarter-turn default. Any other set,
or "rotations" together with "allowRotation": false, is an error naming
the part. Without it, "allowRotation" behaves as before.
Lets converted external benchmark jobs (Nestbolt's 0/180 garment and
box jobs) keep their own rotation rules instead of being widened to
quarter turns.
Wraps each solve in DeadlineNestingEngine so a solve stopped at the
limit scores the best valid plan its engine reported; with none it is
reported as timed out. The harness's own five-minute abandon timeout
grows to the limit plus a minute so the engine's limit always fires
first. Values outside 0.001 s to the timer's range, or a missing value,
fail with exit 1.
On the converted Nestbolt m-sign-1 job, Default with --time-limit 60
returns 96/96 parts on one valid sheet in 60.6 s wall, where an
unlimited run took over 300 s.
DeadlineNestingEngine wraps any engine with a wall-clock limit (any positive time up to
the cancellation timer's ~49.7 days). An engine that finishes in time
keeps its result, as does a result it returns after the limit. When the
limit cancels it, the best whole-job plan it reported (PlanImproved,
ranked like Default ranks candidates, a tie going to the later plan) is
returned if it passes the layout check; with none the solve fails with
TimeoutException. The caller's own cancellation is never turned into a
result. Engine plate commits are dropped and the returned result's
commits reported once it is known; plans are forwarded only when they
beat every plan shown so far.
Default now runs Rectangles, then Irregular, then Circles, so a search
stopped early already holds a valid plan. Ties still break by candidate
order (Irregular, Rectangles, Circles), so for these three the choice is
unchanged, and when every candidate throws, the first in that order is
still the one rethrown. A test seam takes a run order. Desktop, Console
and MCP do not apply a limit yet; Console can solve a job twice (cutout
trial and plain), which needs one shared deadline.
Mutations killed (10/10): no fallback at the limit, invalid plan
accepted, caller cancellation becomes a result, engine commits
forwarded, a worse later plan replaces the best, a tie keeps the earlier
plan, no upper limit bound, failure rethrown in run order, built-in run
order unchanged, run order ignored.
Holes and radii need sizes on the drawing as well as the overall extents. Select
circles or arcs, right-click and choose Add Radius Dimension or Add Diameter
Dimension. The value is measured live from the curve, found by its entity ID, so
it follows edits; only the leader angle and label offset are saved. A radius on
an arc stays on its sweep, and labels are kept clear of the curve and center.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shops need a part's overall size on the drawing while converting it. Width and
height are measured live from the geometry that will be cut, so they follow
filters, exclusions, simplifying, breakoffs and tabbed cutouts instead of going
stale. Only the placement is saved; dimensions never touch the program.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Limits are plain coordinates, so moving a cut-off left the lines trimmed
to it stopping where it used to be. A drag now takes along perpendicular
lines with a limit or gap edge on its center, extending or trimming them
to keep meeting it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Customers and design tools hand over SVGs, and getting them in meant a
trip through another CAD tool. CadImporter now reads them like any other
drawing, and plates, programs and converter entities can be written out.
An SVG's numbers are not drawing units, so import converts to the nest's
units and never guesses: only an absolute width or height counts as a
stated size, since px means 1/96 in to browsers but 1/72 in to
Illustrator. Without one the desktop asks, showing the size each choice
gives, and headless callers must pass the scale or the import fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The repo mixed variants such as center/centre and color/colour, so a
name could be spelled either way and searches missed half the hits.
Standardize on the US spelling, matching .NET's own APIs (Color, Center).
Wire values owned by other systems keep their spelling: the material
ledger's "Cancelled" hold status and GitHub's "cancelled" job status.
No behavior change beyond the spelling of a few status messages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Small features got leads that ignored their shape: full 0.15 leads at a
shallow angle along letter strokes, stubs at the pierce zone's edge in
round holes that could be pierced in the middle, and a lead aimed at a
letter's far pole that ran longer than configured.
Plan Cutting now limits a lead to the feature's depth at its entry, where
a second wall becomes as near. The pierce-zone fallback goes there first
and otherwise aims beside the entry rather than at the whole cutout's
pole. Pinned contours keep their leads, and wide cutouts are unchanged.
Centred pierces exposed the line/circle tangent test: its absolute
discriminant tolerance called a radial line through a hole under about
0.056 radius tangent, reporting a false contact at the centre. Tangent
now also requires the line to stay at the rim, which keeps true grazes
as they were.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Edits in the Cutting Parameters dialog only reached user settings, while a
plate's stored parameters took priority, so a change was lost on any
planned plate unless Apply succeeded. User settings now win; a plate's own
are the fallback when none are saved or readable.
WinForms tests saved to the real user-settings.json, and with user settings
winning a test's lead-ins would feed real planning, so they now use a temp
file and skip the legacy settings carry-over.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WinForms tests open real windows that take focus from the user's work.
Test-Hidden.ps1 runs them on a separate Win32 desktop in the same window
station, so forms keep real handles and message loops while nothing appears.
A job object ends the whole tree on any exit, and a timeout stops runs stuck
on a modal dialog nobody can dismiss there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Trim refused the piece between two crossing cut-offs because a cut-off had
one start and one end, so a line could not be stopped short around a pocket
of parts without deleting it and drawing two. A cut-off now carries Gaps,
stretches between its limits left uncut: segment generation excludes them
like parts, so the line splits into segments that each take their own place
in the cut sequence. Trimming a middle piece adds a gap from one crossing
centre to the other (merged with any it touches), Extend near a gap's edge
closes it, and Swap Through-Cut refuses a junction a gap touches rather
than guess which side of the gap should run through.
Every reader of the limits treats a gap as uncut, so nothing waits for,
warns about or credits a cut that never happens: segment spans stop at a
gap, the cutting dependencies skip a part inside one, the clamp check
ignores a clamp inside one, the cutting-state and overlap stamps go stale
when gaps change, and a gapped line is never full span for the expected
remnant or automatic cut-offs. Gaps persist as an optional gaps array;
older versions ignore it and would cut straight through.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lasers with the Lower Pallet Special Function option need M43 on before
the program's first move. It is written before GOTO1 so restarting at a
later sheet still runs in lower pallet mode, and is off by default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes to the cut planner kept reopening nests fixed earlier. The budget
counts expansions, so outcomes repeat, but the corpus only ranked the
outcome and could not show a plate passing just inside its budget, the
kind a reordered search tips into NoSolutionWithinBudget. Each plate now
reports the share of its budget its strict search spent, and the run
ends with the plates at 80% or more, to compare with master's run.
part-order and entries reorder the search on every plate, so naming
either in OPENNEST_PROBLEM_NESTS_CHECKS now plans every nest instead of
only those tagged with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>