Commit Graph
100 Commits
Author SHA1 Message Date
ajandClaude Opus 4.6 4cecaba83a fix(core): emit line instead of arc for near-zero sweep to avoid full-circle misinterpretation
Near-zero-sweep arcs with large radius (e.g. from ellipse converter) have
nearly-coincident start/end points. Downstream code (ConvertProgram, Program
BoundingBox) treats coincident start/end as a full 360° circle, inflating the
bounding box and rendering wrong geometry. Emit a LinearMove when sweep is
negligible — geometrically equivalent and avoids the ambiguity. Also fix the
ellipse converter to produce lines instead of degenerate arcs at the source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 08:50:38 -04:00
ajandClaude Opus 4.6 4053f1f989 fix(core): arc bounding box inflated for near-zero sweep arcs
Arcs with sweep angles smaller than Tolerance.Epsilon were treated as
full circles by IsBetweenRad's shortcut check, causing UpdateBounds to
expand the bounding box to Center ± Radius. This made zoom-to-fit zoom
out far beyond the actual part extents.

Skip cardinal angle expansion when sweep is near-zero so the bounding
box uses only the arc's start/end points.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 08:22:20 -04:00
ajandClaude Opus 4.6 ca67b1bd29 fix(io): handle flipped OCS normal on DXF ellipse import
Ellipses with extrusion direction Z=-1 had their parametric direction
reversed, causing the curve to appear mirrored. Negate start/end
parameters when Normal.Z < 0 to correct the minor-axis traversal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 08:22:20 -04:00
aj 199095ee43 fix(engine): canonicalize PlaceBestFitPairs builds to match BestFitCache frame 2026-04-23 08:22:20 -04:00
aj eb493d501a feat(engine): wrap single-item Fill with canonicalize/un-rotate bookends 2026-04-23 08:22:20 -04:00
aj 6c98732117 feat(engine): BestFitCache operates in canonical frame; TryPlaceBestFitPair builds from canonical drawing 2026-04-23 08:22:20 -04:00
aj a2e9fd4d14 feat(engine): extract ML features from canonical drawing frame 2026-04-23 08:22:20 -04:00
aj d228b6b812 refactor(engine): share MBR between PartClassifier and CanonicalAngle 2026-04-23 08:22:20 -04:00
aj c634aecd4b docs(core): refresh SourceInfo.Angle doc now that setter wiring lands 2026-04-23 08:22:19 -04:00
aj 14b7c1cf32 feat(core): store Source.Angle; recompute when Program changes 2026-04-23 08:22:19 -04:00
aj 402af91af5 feat(engine): add CanonicalFrame helper for drawing-to-canonical rotation 2026-04-23 08:22:19 -04:00
aj 9a6b656e3c feat(core): add CanonicalAngle helper for MBR-aligning angle 2026-04-23 08:22:19 -04:00
ajandClaude Opus 4.7 d2f9597b0c refactor(fill): use native entity geometry for linear copy distance
Replaces PartBoundary polygon edges with PartGeometry.GetOffsetPerimeterEntities
(inflated Line/Arc entities) so arcs are handled exactly without the polygon
sampling error that previously required a bboxDim + PartSpacing clamp. Adds
bbox DirectionalGap / PerpendicularOverlap early-outs to skip pair checks
that can't produce a valid slide, and removes the now-unused PartBoundary
cache, GetPatternLines/GetOffsetPatternLines helpers, and ComputeCopyDistance
clamp.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 23:26:21 -04:00
ajandClaude Opus 4.7 c40dcf0e25 chore: remove unused debug logging to desktop
NfpSlideStrategy wrote to nfp-slide-debug.log on the Desktop on every
call. The console's SetUpLog created test-harness-logs/ next to input
files but nothing in the codebase wrote to Trace, so those files were
always empty. Drop both along with the --no-log flag.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 23:24:40 -04:00
ajandClaude Opus 4.6 28653e3a9f feat(shapes): generate unique drawing names from parameters and add toolbar button
Shape library drawings now get descriptive names based on their
parameters (e.g. "Rectangle 12x6", "Circle 8 Dia") instead of generic
type names, preventing silent duplicates in the DrawingCollection
HashSet. Added a Shape Library button to the Drawings tab toolbar
and removed separators between toolbar buttons for a cleaner look.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 15:48:45 -04:00
ajandClaude Opus 4.6 7c3246c6e7 fix(cutting): restrict tabs to external perimeter and clarify tab UI
Tabs were being applied to internal cutouts and circle holes, which is
incorrect — only the external perimeter should be tabbed. Restructured
the Tabs panel to use radio buttons ("Tab all parts" vs "Auto-tab by
smallest dimension") so the two modes are clearly mutually exclusive
instead of the confusing implicit override behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:55:30 -04:00
ajandClaude Opus 4.6 bd48f57ce0 feat(ui): distinct Dark palette and recolor drawings on scheme switch
- Replace Dark part colors with high-contrast neon/electric palette
- Recolor existing drawings in open nests when scheme changes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:06:14 -04:00
ajandClaude Opus 4.6 a6ec21accc fix(ui): address code review issues in color scheme feature
- Sync PlateView.BackColor on repaint so live scheme switch updates background
- Guard FromHex against truncated hex strings (< 6 chars)
- Cache disk schemes to avoid re-reading Schemes/ folder on every access

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:56:15 -04:00
ajandClaude Opus 4.6 320cf40f41 feat(ui): ship Schemes folder for user-defined color scheme JSON
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:52:45 -04:00
ajandClaude Opus 4.6 3beca10429 feat(ui): add color scheme picker to Options dialog
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:52:00 -04:00
ajandClaude Opus 4.6 8bea5dac6c feat(ui): apply active color scheme at startup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:50:03 -04:00
ajandClaude Sonnet 4.6 12f8bbf8f5 feat(ui): add ActiveColorScheme user setting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 21:49:31 -04:00
ajandClaude Opus 4.6 d15790b948 feat(ui): add ColorSchemeRegistry with Classic/Pastel/Dark built-ins
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:48:46 -04:00
ajandClaude Opus 4.6 d80f76e386 feat(ui): add ColorScheme.Name/PartColors instance props and JSON serializer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:46:51 -04:00
ajandClaude Sonnet 4.6 07bce8699a refactor(core): make Drawing.PartColors mutable for scheme overrides
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 21:45:18 -04:00
ajandClaude Opus 4.6 9b84508ff4 refactor(shapes): generalize OctagonShape to NgonShape
Parameterize side count so users can generate any regular n-gon
(n>=3). Width remains the inscribed-circle diameter, preserving n=8
behavior; circumradius derives as Width / (2*cos(pi/n)).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 13:42:02 -04:00
ajandClaude Opus 4.6 6fdf0ad3c5 refactor(cnc): extract rapid enumeration into RapidEnumerator
Pulls the rapid-walk logic (sub-program unwrapping, first-pierce lookup,
incremental-vs-absolute handling, first-rapid skipping) out of
PlateRenderer.DrawRapids into a reusable RapidEnumerator in Core so it
can be unit-tested and reused outside the renderer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 12:49:04 -04:00
aj 4f7bfcc3ad Merge remote-tracking branch 'origin/master' 2026-04-15 12:46:40 -04:00
ajandClaude Opus 4.6 3c53d6fecd fix(engine): default FillContext.Policy to avoid null-deref in ReportProgress
FillContext.ReportProgress dereferences Policy.Comparer, so any caller
that forgot to set Policy hit a NullReferenceException. Default to
FillPolicy(DefaultFillComparer) so tests and ad-hoc callers work without
boilerplate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 06:28:58 -04:00
ajandClaude Opus 4.6 e239967a7b feat(cincinnati): emit SubProgramCall features as M98 hole calls
When a feature is a single SubProgramCall, wrap the call with a G52
offset shift, emit M98 P<num>, reset G52, and add M47 between features.
Accepts an optional hole subprogram id map so the post can remap
drawing-local subprogram ids to machine subprogram numbers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 06:17:31 -04:00
ajandClaude Opus 4.6 9d57d3875a fix(cnc): offset SubProgramCall positions in Program.Offset
Program.Offset only adjusted Motion codes, so subprogram calls kept
their original offsets after a part was translated. Apply the offset
to SubProgramCall.Offset too so hole subprograms follow the part.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 06:17:26 -04:00
ajandClaude Opus 4.6 0e299d7f6f feat(cincinnati): seed material library defaults and add selector dropdown
Adds the full Cincinnati material/etch library list as the committed
default config (seeded into Posts/ on build only when no runtime config
exists), plus a Selected Library override in the PropertyGrid backed by
a TypeConverter that populates from MaterialLibraries. MainForm calls
the new IPostProcessorNestAware hook before showing the config so the
dropdown opens preselected to the best match by nest material and
nearest thickness.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 06:16:29 -04:00
ajandClaude Opus 4.6 c6f544c5d7 feat(ui): populate material combobox from post processors
Replaces the material textbox on EditNestInfoForm with a combobox whose
items are aggregated from every loaded post processor that implements the
new IMaterialProvidingPostProcessor interface. CincinnatiPostProcessor
exposes its configured MaterialLibraries entries. Free-text entry still
works so custom materials remain usable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 06:12:54 -04:00
ajandClaude Opus 4.6 9563094c2b fix(ui): show Drawings tab before Plates in EditNestForm
Users need to import a drawing first, so Drawings tab should be the
default landing tab to reduce steps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 09:10:58 -04:00
ajandClaude Opus 4.6 a3ae61d993 fix(cutting): emit open contours raw instead of applying lead-in/lead-out
Open (non-closed) shapes like scribe lines or partial cuts don't have
a meaningful pierce point or closing segment, so applying lead-in/out
would produce invalid toolpaths. Skip the lead-in/out logic and emit
them as raw contours in both Apply and ApplySingle paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:37:56 -04:00
ajandClaude Opus 4.6 838a247ef9 fix(geometry): replace closest-point heuristic with analytical arc-to-line directional distance
ArcToLineClosestDistance used geometric closest-point as a proxy for
directional push distance, which are fundamentally different queries.
The heuristic could overestimate the safe push distance when an arc
faces an inclined line, causing the Compactor to over-push parts into
overlapping positions.

Replace with analytical computation: for each arc/line pair, solve
dt/dθ = 0 to find the two critical angles where the directional
distance is stationary, evaluate both (if within the arc's angular
span), and fire a ray to verify the hit is within the line segment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:33:48 -04:00
ajandClaude Opus 4.6 a5e5e78c4e refactor(geometry): deduplicate axis branches in SpatialQuery.OneWayDistance
Merge the near-identical Left/Right and Up/Down pruning loops into a
single loop that selects the perpendicular axis via IsHorizontalDirection().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 21:58:45 -04:00
ajandClaude Opus 4.6 c386e462b2 docs(readme): add CAD converter section with screenshots
Add a CAD Converter workflow section and inline thumbnail screenshots.
Rearrange existing screenshots as side-by-side thumbnails with
click-to-enlarge links.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 21:36:39 -04:00
ajandClaude Opus 4.6 2c0457d503 feat(ui): add bend line editing to CAD converter
Add Edit link and double-click handler to the bend lines list so
existing bends can be modified without removing and re-adding them.
BendLineDialog gains a LoadBend method to populate fields from an
existing Bend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 21:36:26 -04:00
ajandClaude Opus 4.6 b03b3eb4d9 fix(bending): detect bend lines on layer "0" in addition to "BEND"
SolidWorks drawings sometimes place centerline bend markers on the
default layer instead of a dedicated BEND layer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 21:36:21 -04:00
ajandClaude Opus 4.6 29c2872819 fix(geometry): add Entity.Clone() and stop NormalizeEntities from mutating originals
ShapeProfile.NormalizeEntities called Shape.Reverse() which flipped arc
directions on the original entity objects shared with the CAD view. Switching
to the Program tab and back would leave arcs reversed. Clone entities before
normalizing so the originals stay untouched.

Adds abstract Entity.Clone() with implementations on Line, Arc, Circle,
Polygon, and Shape (deep-clones children). Also adds CloneAll() extension
and replaces manual duplication in PartGeometry.CopyEntitiesAtLocation and
ProgramEditorControl.CloneEntity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 21:35:13 -04:00
ajandClaude Opus 4.6 3e96c62f33 docs(readme): reformat features as tables and document cutout-aware splitter
Feature list becomes grouped tables (Import/Export, Nesting, Plate
Operations, CNC Output). Nest file format section expands to cover the
newer entities/programs/subs layout. Drawing Splitting section gains a
paragraph explaining cutout-aware clipping: Liang-Barsky line clipping,
arc-vs-region intersection, and connected-component detection that emits
one drawing per physically-disconnected strip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:55:11 -04:00
ajandClaude Opus 4.6 6880dee489 fix(splitter): preserve disconnected strips and trim cuts around cutouts
Splits that cross an interior cutout previously merged physically
disconnected strips into one drawing and drew cut lines through the hole.
The region boundary now spans full feature-edge extents (trimmed against
cutout polygons) and line entities are Liang-Barsky clipped, so multi-split
edges work. Arcs are properly clipped at region boundaries via iterative
split-at-intersection so circles that straddle a split contribute to both
sides. AssemblePieces groups a region's entities into connected closed
loops and nests holes by bbox-pre-check + vertex-in-polygon containment,
so one region can emit multiple drawings when a cutout fully spans it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:46:47 -04:00
ajandClaude Opus 4.6 0e45c13515 feat(shapes): add PlateSizes catalog and wire Ctrl+P to snap-to-standard
PlateSizes holds standard mill sheet sizes (48x96 through 96x240) and
exposes Recommend() which snaps small layouts to an increment and
rounds larger layouts up to the nearest fitting sheet. Plate.SnapToStandardSize
applies the result while preserving long-axis orientation, and the
existing Ctrl+P "Resize to Fit" menu in EditNestForm now calls it
instead of the simple round-up AutoSize.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 20:16:29 -04:00
aj 54def611fa refactor(ui): switch CreateShapeFromInputs to control-type branching 2026-04-10 17:52:03 -04:00
ajandClaude Sonnet 4.6 b1d094104a feat(ui): add filtered pipe size dropdown to shape library
Renders PipeSize as a DropDownList ComboBox, filters entries to those fitting
the current hole geometry, disables the combo when Blind is checked, and
appends an invalid-pipe warning to the preview info when TryGetOD fails.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 17:50:01 -04:00
ajandClaude Sonnet 4.6 9d66b78a11 feat(ui): add bool checkbox support to ShapeLibraryForm
BuildParameterControls now creates a CheckBox (wired to UpdatePreview) for bool properties instead of a TextBox; CreateShapeFromInputs reads the Checked value via a short-circuit before the TextBox cast.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 17:47:36 -04:00
aj eddbbca7ef test(shapes): verify PipeFlangeShape JSON loading and shipped config integrity 2026-04-10 17:45:46 -04:00
ajandClaude Sonnet 4.6 4e7b5304a0 chore(shapes): migrate flange config to PipeFlangeShape schema
Replace NominalPipeSize (double) with PipeSize (string label) and add
PipeClearance: 0.0625 to all 136 entries in PipeFlangeShape.json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 17:42:16 -04:00
aj 06485053fc test(shapes): cover empty-string PipeSize in addition to null 2026-04-10 17:39:50 -04:00
ajandClaude Sonnet 4.6 92a57d33df feat(shapes): add pipe bore, clearance, and blind flag to PipeFlangeShape
Replaces NominalPipeSize (double) with PipeSize (string), PipeClearance (double), and Blind (bool). GetDrawing cuts a center bore at pipeOD + PipeClearance unless Blind is true or PipeSize is unknown/null.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 17:36:10 -04:00
aj 6adc5b0967 refactor(shapes): rename FlangeShape to PipeFlangeShape 2026-04-10 17:33:28 -04:00
aj d215d02844 style(shapes): remove redundant usings and document PipeSizes bound 2026-04-10 17:31:22 -04:00
ajandClaude Sonnet 4.6 57863e16e9 feat(shapes): add ANSI pipe OD lookup table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 17:27:25 -04:00
ajandClaude Opus 4.6 091e750e1b chore(cad-importer): remove dead code and cover named detector branch
- Drop CadImportResult.Document: no caller reads it after the
  migrations (BendDetectorRegistry runs inside CadImporter.Import
  itself, and downstream callers only consume the entity/bend data).
- Drop dead CadConverterForm.GetNextColor() helper: zero callers
  since GetDrawings stopped needing it.
- Drop stale 'using OpenNest.Properties;' and unused 'newItems'
  local in OnSplitClicked.
- Add Import_WhenNamedDetectorDoesNotExist_ReturnsEmptyBends to
  cover the previously untested named-detector branch in
  CadImporter.Import.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:13:44 -04:00
ajandClaude Opus 4.6 87b965f895 refactor(ui): use CadImporter in BomImportForm
Replaces the hand-rolled DXF->Drawing pipeline (Dxf.Import + bend
detection + normalize + ConvertGeometry + pierce offset extraction)
with a single CadImporter.ImportDrawing call. Brings BomImportForm's
output in line with the rest of the callers: drawings now carry
Source.Offset, SourceEntities, SuppressedEntityIds, and detected bends,
and round-trip cleanly through nest files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:11:49 -04:00
aj 08f60690a7 docs: document CadImporter service in CLAUDE.md 2026-04-10 13:27:46 -04:00
ajandClaude Sonnet 4.6 a4609c816c refactor(ui): use CadImporter.BuildDrawing in CadConverterForm.GetDrawings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:24:26 -04:00
ajandClaude Sonnet 4.6 5a4272696e refactor(ui): use CadImporter.Import in CadConverterForm.AddFile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:18:49 -04:00
ajandClaude Sonnet 4.6 2cf03be360 refactor(training): use CadImporter for DXF import
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:13:00 -04:00
ajandClaude Sonnet 4.6 041e184d93 refactor(api): use CadImporter for DXF import in NestRunner
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:08:35 -04:00
ajandClaude Sonnet 4.6 26df3174ea refactor(mcp): use CadImporter for DXF import
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:05:28 -04:00
ajandClaude Sonnet 4.6 0f5aace126 refactor(console): use CadImporter for DXF import
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 13:03:16 -04:00
ajandClaude Sonnet 4.6 399f8dda6e feat: add CadImporter.ImportDrawing convenience method
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 12:59:06 -04:00
ajandClaude Sonnet 4.6 d921558b9c feat: add CadImporter.BuildDrawing stage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 12:51:58 -04:00
ajandClaude Opus 4.6 bf3e3e1f42 feat: add CadImporter.Import stage with bend detection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 12:37:12 -04:00
aj e120ece014 feat: add CadImportResult data object for CadImporter 2026-04-10 12:28:17 -04:00
aj 264e8264be feat: add CadImportOptions for CadImporter service 2026-04-10 12:25:04 -04:00
ajandClaude Opus 4.6 24babe353e fix: show both offset and rotation in SubProgramCall.ToString
The either/or format meant a SubProgramCall with both a non-zero
Offset and non-zero Rotation would only show the Offset, hiding the
rotation metadata. The data model supports both independently, so the
display should too.

Also fixes a zero-field leak where the old fallback emitted
`G65 P_ R0` for calls with no rotation. Now each field is only shown
when non-zero, and `G65 P_` with no arguments is emitted when
neither is set.

Note: SubProgramCall.ToString is purely a debug/display aid. The
Cincinnati post emits sub-calls via the G52 + M98 bracket, not via
G65, so this format doesn't correspond to real machine output.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:37:46 -04:00
ajandClaude Opus 4.6 e63be93051 fix: emit G52 bracket for hole sub-program calls
CincinnatiSheetWriter.WriteHoleSubprogramCall emitted
`M98 P<num> X<x> Y<y>`, but per manual §3.98 ("M98 SUB-PROGRAM CALL
WITH NO ARGUMENTS") M98 takes only P and L — the X/Y had no defined
meaning to the control. The intent was to position the sub-program at
the hole center, which is what G52 is for per §1.52 ("local work
coordinate system") and which explicitly does not move the nozzle.

Emit the documented G52 bracket instead:
  G52 X<hole.x> Y<hole.y>
  M98 P<holeSubNum>
  G52 X0 Y0

The hole sub-program is authored in hole-local coordinates, so its
first rapid (the lead-in to the pierce point) resolves to the absolute
pierce under the G52 shift and moves the tool directly there from the
previous feature's end — no phantom rapid to the hole center.

Also add docs/cincinnati-post-output.md as the reference for the full
post output format, with every emitted G/M code cross-referenced to
the Cincinnati programming manual. Un-ignore docs/ (docs/superpowers/
stays ignored) and track the PDF manual alongside the reference.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:21:15 -04:00
ajandClaude Opus 4.6 ba3c3cbea3 fix: draw sub-program rapid directly to lead-in pierce
The SubProgramCall branch in DrawRapids used to draw a rapid from the
previous feature's end to the hole center, then rely on the sub-program's
own first rapid to draw from center to the lead-in pierce. That rendered
a phantom center-hop segment that doesn't exist physically — a
SubProgramCall is a coordinate-frame shift (emitted as a G52 bracket on
Cincinnati), not a move to the hole center.

Look ahead through the sub-program for its first pierce point in
absolute coordinates and draw a single direct rapid from pos to that
pierce. Recurse into the sub with skipFirstRapid: true so the sub's
first rapid isn't drawn again on top.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:17:35 -04:00
ajandClaude Opus 4.6 572fa06a21 fix: track tool position through sub-programs in ConvertMode
ConvertMode.ToIncremental skipped SubProgramCall codes entirely when
computing deltas, so parent motions after a sub-call were encoded as if
the tool never moved. Several traversal sites (ConvertProgram,
GraphicsHelper, PlateRenderer, CutDirectionArrows, Program.BoundingBox)
worked around this with save/restore hacks that treated sub-calls as
transparent — but DrawRapids legitimately tracks actual tool position,
so after the last hole the first perimeter rapid was applied to the
wrong base, drifting the rendered perimeter past the plate edge by
roughly the distance to the last hole.

Fix the root cause: ToIncremental and ToAbsolute now walk sub-programs
to compute where they leave the tool, and advance pos accordingly. The
other traversals capture a frameOrigin at entry and compute sub-call
placement as frameOrigin + Offset, letting pos advance naturally
through the sub recursion. All the save/restore workarounds are
removed.

Program.BoundingBox also picks up the same frame-origin treatment,
which corrects a latent bug where absolute-mode endpoints and nested
sub-calls dropped the parent's frame origin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 07:51:51 -04:00
ajandClaude Opus 4.6 a6c2235647 fix: let DrawRapids track actual tool position through sub-programs
Don't restore pos after SubProgramCall expansion in DrawRapids — the
machine moves from hole to hole sequentially, so rapids should connect
from the previous hole's end to the next hole's center.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 18:26:39 -04:00
ajandClaude Opus 4.6 5c918a0978 fix: draw rapid move to hole center before sub-program lead-in
The rapid from the previous feature to the hole center is implied by
the SubProgramCall offset but wasn't being drawn. Now DrawRapids
renders this traverse before recursing into the sub-program.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:46:44 -04:00
ajandClaude Opus 4.6 92461deb98 fix: apply SubProgramCall offset additively and restore curpos after expansion
ConvertMode.ToIncremental skips SubProgramCalls when computing deltas,
so all code paths that expand SubProgramCalls must: (1) set curpos to
savedPos + Offset before expanding, and (2) restore curpos afterward
so subsequent incremental codes get correct deltas.

Fixed in ConvertProgram, GraphicsHelper (AddProgram, AddProgramSplit),
PlateRenderer (DrawRapids, DrawProgramPiercePoints, GetFirstPiercePoint),
and CutDirectionArrows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 17:40:05 -04:00
ajandClaude Sonnet 4.6 bc859aa28c feat: handle SubProgramCall offsets in BoundingBox and Rotate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:47:40 -04:00
ajandClaude Opus 4.6 09eac96a03 feat: handle SubProgramCalls in Cincinnati post feature splitting
SubProgramCalls are now treated as standalone features in the Cincinnati
post-processor. SplitByRapids emits them as single-element features
instead of splitting on rapids within sub-programs. A nest-level hole
sub-program registry deduplicates by content and assigns post numbers.
Sheet writers emit M98 calls with X/Y offsets for hole features, and
hole sub-program definitions are written after part sub-programs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:44:58 -04:00
ajandClaude Opus 4.6 df65414a9d feat: serialize and deserialize hole sub-programs in nest file format
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:40:13 -04:00
ajandClaude Opus 4.6 4aed231611 feat: emit SubProgramCalls for circle holes in ContourCuttingStrategy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 14:35:56 -04:00
ajandClaude Sonnet 4.6 c641b3b68e feat: expand SubProgramCalls with Offset in ConvertProgram
Inline sub-program geometry into the parent geometry list using Offset
as the starting curpos, replacing the Shape-wrapping approach.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:31:13 -04:00
ajandClaude Sonnet 4.6 f3b27c32c3 feat: add SubPrograms dictionary to Program with deep-copy support
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:28:37 -04:00
ajandClaude Sonnet 4.6 c270d8ea76 feat: add Offset property to SubProgramCall for hole positioning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 14:26:55 -04:00
ajandClaude Opus 4.6 de6877ac48 feat: add option to round lead-in angles for circle holes
Snaps lead-in angles on ArcCircle contours to a configurable
increment (default 5°), reducing unique hole variations from
infinite to 72 max. Rounding happens upstream in EmitContour
so the PlateView and post output stay in sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:41:33 -04:00
ajandClaude Opus 4.6 3481764416 perf: use perimeter-only drawing in best fit pair evaluation
PairEvaluator was cloning the full CNC program (including all internal
cutouts) for every candidate. For parts with many holes (e.g. 952),
this caused O(n²) overlap checks and thousands of unnecessary polygon
tessellations per candidate.

Now extracts the perimeter shape once, builds a lightweight drawing
from it, and uses that for all Part.CreateAtOrigin calls. Cutouts are
irrelevant for best fit — only the outer boundary matters for pairing.

75x speedup on a 952-hole rectangle (30s → 0.4s).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:14:02 -04:00
ajandClaude Opus 4.6 640814fdf6 fix: marshal timer callbacks to UI thread to prevent GDI+ threading exception
System.Timers.Timer fires on thread pool threads, causing GraphicsPath
objects to be accessed concurrently by hover detection and OnPaint,
triggering "Object is currently in use elsewhere" in DrawParts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:41:18 -04:00
ajandClaude Opus 4.6 6a30828fad feat: optimize external lead-in placement using next-part pierce points
External lead-ins now sit on the line between the last internal cutout
and the next part's first pierce point, minimizing rapid travel. Cutout
sequencing starts from the bounding box corner opposite the origin and
iterates 3 times to converge the perimeter lead-in and internal sequence.
LeadInAssigner and PlateProcessor both use a two-pass approach: first
pass collects pierce points, second pass refines with next-part knowledge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:33:55 -04:00
ajandClaude Opus 4.6 786b6e2e88 fix: show cutting parameters dialog before assigning lead-ins
Auto-assign lead-ins silently reused existing plate parameters with no
way to change them after the first assignment. Now a dialog with the
full CuttingPanel is shown every time, pre-populated with the current
settings, so the user can review and modify before confirming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:32:41 -04:00
ajandClaude Opus 4.6 ba89967448 fix: respect suppression state in filter panel and guard DetermineWinding
FilterPanel.LoadItem was hardcoding all layer and line type checkboxes
to checked, ignoring actual visibility state. Now reads Layer.IsVisible
and entity IsVisible to set correct checked state.

Also guard DetermineWinding against shapes with fewer than 3 polygon
points (defaults to CCW) to prevent crash when applying lead-ins.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:58:11 -04:00
ajandClaude Opus 4.6 b566d984b0 fix: preserve suppression state when reopening converter
LoadItem was resetting all entity visibility to true, overriding the
suppression state set by LoadDrawings. Now stores suppressed entity IDs
on FileListItem and re-applies after the reset. Also auto-unchecks
layers where all entities are suppressed, and syncs suppression state
back to the FileListItem when filters change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:32:18 -04:00
ajandClaude Opus 4.6 c1e6092e83 feat: add entity-based suppression with stable GUIDs
Entities now have a Guid Id for stable identity across edit sessions.
Drawing stores the full source entity set (SourceEntities) and a set of
suppressed entity IDs (SuppressedEntityIds), replacing the previous
SuppressedProgram approach. Unchecked entities in the converter are
suppressed rather than permanently removed, so they can be re-enabled
when editing drawings again.

Entities are serialized as JSON in the nest file (entities/entities-N)
alongside the existing G-code programs. Backward compatible with older
nest files that lack entity data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:25:48 -04:00
ajandClaude Opus 4.6 df86d4367b fix: update drawings in-place when editing in converter so parts reflect changes
EditDrawingsInConverter was replacing Drawing objects with new instances,
but Part.BaseDrawing is readonly — parts kept referencing the old drawings
with stale programs (e.g. etch lines that were removed). Now matches by
name and updates existing drawings in-place, then refreshes all parts.

Also fixes Part.Update() which applied rotation backwards and was missing
UpdateBounds() and lead-in state reset.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:58:51 -04:00
ajandClaude Opus 4.6 40026ab4dc test: add SpatialQuery DirectionalDistance tests for circles, squares, and rounded rects
24 tests covering circle-to-circle, square-to-square, rounded rectangle,
mixed shape types, PushDirection overload, edge cases, and symmetry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:10:32 -04:00
ajandClaude Opus 4.6 b18a82df7a refactor: clean up SpatialQuery duplications and redundancies
Extract ArcToLineClosestDistance helper to eliminate duplicate Phase 3
arc-to-line loops, remove redundant MaxValue guard in curve-to-curve
check, consolidate CollectVertices overloads, and add entity-based
PushDirection overload for API consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 09:33:29 -04:00
ajandClaude Opus 4.6 f090a2e299 fix: add arc-to-line closest-point check in DirectionalDistance
Corner arcs from offset perimeters could slip past vertex sampling,
causing compactor push to undershoot by ~halfSpacing. Use ClosestPointTo
to find the actual nearest point on each arc to each line before firing
the directional ray.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 08:52:30 -04:00
ajandClaude Opus 4.6 55192a4888 chore: update ShapeLibraryForm designer layout
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 08:37:42 -04:00
ajandClaude Opus 4.6 7c28a35ad8 feat: add Edit Drawings in Converter button to reopen nest drawings in CadConverterForm
Adds a toolbar button on the Drawings tab that opens the CAD converter
pre-populated with the current nest drawings, allowing users to revisit
layer filtering, quantities, and other settings without re-importing.

Also fixes PlateView stealing focus from text inputs on mouse enter
and FilterPanel crashing when loaded before form handle is created.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 08:37:20 -04:00
ajandClaude Opus 4.6 b2a723ca60 feat: add Shape Library UI with configurable shapes and flange presets
Add a Shape Library dialog (Nest > Shape Library) for creating drawings
from built-in parametric shapes. Supports configuration presets loaded
from JSON files — ships with 136 standard pipe flanges. Parameters use
TextBox inputs with architectural unit parsing (feet/inches, fractions).

- ShapeLibraryForm with split layout: shape list, preview, parameters
- ShapePreviewControl for auto-zoom rendering with info overlay
- ArchUnits utility for parsing architectural measurements
- SetPreviewDefaults() on all ShapeDefinition subclasses
- Convention-based config discovery (Configurations/{ShapeName}.json)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 07:44:03 -04:00
ajandClaude Opus 4.6 3dca25c601 fix: improve circle nesting with curve-to-curve distance and min copy spacing
Add Phase 3 curve-to-curve direct distance in CpuDistanceComputer to
catch contacts that vertex sampling misses between curved entities.
Enforce minimum copy distance in FillLinear to prevent bounding box
overlap when circumscribed polygon boundaries overshoot true arcs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 00:15:35 -04:00
ajandClaude Opus 4.6 ebc1a5f980 refactor: extract shared helpers in SpatialQuery
Pull duplicated vertex collection, edge conversion, sorting, and
ray-circle solving into reusable private methods. Delegate the
no-offset DirectionalDistance overload to the offset version.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 00:15:30 -04:00
ajandClaude Opus 4.6 b729f92cd6 fix: correct compactor circle-to-circle directional distance
The vertex-to-entity approach in DirectionalDistance only sampled 4
cardinal points per circle, missing the true closest contact when
circles are offset diagonally from the push direction. This caused
the distance to be overestimated, pushing circles too far and
creating overlap that worsened with distance from center.

Add a curve-to-curve pass that computes exact contact distance by
treating the problem as a ray from one center to an expanded circle
(radius = r1 + r2) at the other center. Includes arc angular range
validation for arc-to-arc and arc-to-circle cases.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:51:09 -04:00