Replace verbose value tuple with named PartPair record struct, extract
AnchorToWorkArea/PairBbox helpers to eliminate duplication, and delegate
RepeatColumns to FillLinear.Fill which already handles geometry-aware
column tiling with overlap fallback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Polygon-polygon collision detection using convex decomposition (ear-clipping
triangulation) followed by Sutherland-Hodgman clipping on each triangle pair.
Handles overlapping, non-overlapping, edge-touching, containment, and concave
polygons. Includes hole subtraction support for future use.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Immutable result type that holds overlap flag, overlap regions (as polygons),
intersection points, and computed overlap area.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FillGrid had no overlap check after perpendicular tiling of the row
pattern (Step 2), unlike Step 1 which had one. When geometry-aware
FindPatternCopyDistance underestimated row spacing, overlapping parts
were returned unchecked.
Changes:
- Make FillLinear.HasOverlappingParts shape-aware (bbox pre-filter +
Part.Intersects) instead of bbox-only, preventing false positives on
interlocking pairs while catching real overlaps
- Add missing overlap safety check after Step 2 perpendicular tiling
with bbox fallback
- Add diagnostic Debug.WriteLine logging when overlap fallback triggers,
including engine label, step, direction, work area, spacing, pattern
details, and overlapping part locations/rotations for reproduction
- Add FillLinear.Label property set at all callsites for log traceability
- Refactor LinearFillStrategy and ExtentsFillStrategy to use shared
FillHelpers.BestOverAngles helper for angle-sweep logic
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EllipseConverter computed arc radius from start point only, causing
~0.0009 unit gaps between consecutive arcs. Use circumcircle of
(start, mid, end) points so both endpoints lie exactly on the arc.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract shared BestFitAxis helper parameterized by orientation,
eliminating 23-line duplicate in rectangle packing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
StripeFiller and FillExtents had identical 24-line overlap detection
methods; move to FillHelpers and delegate from both callers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract shared EvenlyDistribute helper parameterized by axis,
eliminating 27-line duplicate between the two methods.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The arc and line Optimize methods had identical merge-loop structure;
extract a generic MergePass helper with type-specific delegates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both ActionSelect and ActionZoomWindow had identical 29-line
GetRectangle methods; consolidate into the common base class.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The parameterless rotation is equivalent to rotating around (0,0),
so delegate to the origin overload to eliminate 30-line duplicate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract shared FillAxis helper parameterized by orientation,
eliminating 34-line duplicate between horizontal and vertical fills.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move identical FitWithStartTangent and MaxRadialDeviation methods
to a shared ArcFit class, eliminating 40-line duplicate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract shared SortStrips helper parameterized by axis selectors,
eliminating 61-line near-duplicate between column and row sorting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convex corners were being miter-joined (lines extended to a point)
because IntersectsUnbounded always finds an intersection for non-parallel
lines. Now checks the cross product of original line directions to detect
convex corners and inserts an arc instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Part.Intersects: filter intersection points at a vertex of either
shape (was both), so edge-touching parts are not flagged as overlapping
- NestEngineBase.HasOverlaps: use epsilon-based bounding box pre-filter
consistent with FillExtents and Plate.HasOverlappingParts
- PartGeometry.GetOffsetPartLines: remove extra chordTolerance added to
spacing offset — was causing 0.002" gap beyond the intended part spacing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UpdateEtchEntities was removing all entities on the ETCH layer, which
also deleted user-added etch marks like part numbers. Now tags generated
bend etch lines with a BendEtch tag and filters on that instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Place circle labels on the circumference using golden angle distribution
so concentric circles don't overlap. Hide labels when the entity is too
small on screen to fit the badge.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Labels are drawn at each entity's midpoint with a filled background
circle for readability. Toggle via "Labels" checkbox in the detail bar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Etch marks for up bends are now real geometry entities on an ETCH layer
instead of being drawn dynamically. They flow through the full pipeline:
entities → FilterPanel layers → ConvertGeometry (tagged as Scribe) →
post-processor sequencing before cut geometry.
Also includes ShapeProfile normalization (CW perimeter, CCW cutouts)
applied consistently across all import paths, and inward offset support
for cutout shapes in overlap/offset polygon calculations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shape.OffsetOutward produces inward offsets for certain rotated polygons,
causing geometry-aware copy distances to be too small and placing
overlapping parts. Root cause is in the offset winding direction
detection — this commit adds safety nets while that is investigated.
- FillLinear.FillGrid: detect bbox overlaps after geometry-aware tiling,
fall back to bbox-based spacing when overlaps found
- FillExtents.RepeatColumns: detect overlaps after Compactor computes
copy distance, fall back to columnWidth + spacing
- PairFiller/StripeFiller remnant fills: use FillLinear directly instead
of spawning full engine pipeline (avoids strategies with the bug)
- Add PairOverlapDiagnosticTests reproducing the issue
- MCP config: use shadow-copy wrapper for dev hot-reload
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
High-utilization pairs (>=75%) are no longer discarded for exceeding
the aspect ratio limit, since the material isn't being wasted.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wires the OpenNest.Data layer into the UI: adds project reference, creates MachineConfigForm (tree-based editor for machines/materials/thicknesses with import/export), and adds Tools > Machine Configuration... menu item.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Embeds CL-980.json as a resource in OpenNest.Data and adds EnsureDefaults()
to LocalJsonProvider, which seeds the machines directory on first run when empty.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
One JSON file per machine named by GUID, stored in a configurable directory.
Supports save, load, list (as summaries), and delete with IO-error retry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Parts tab: shows all BOM items, editable Material/Thickness for
matched rows, grayed-out rows for items without DXF files
- Groups tab: auto-computed from parts with editable Plate Width/Length
per material+thickness group
- Editing Material/Thickness on Parts tab immediately re-groups
- Per-group plate sizes preserved across re-groups
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fill must be at index 0 (front) so it's processed last by the
docking layout engine. Edge docks at higher indices process first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AutoSize with Dock.Fill child causes circular sizing and collapses
the GroupBox. Use fixed Height=200 instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced absolute-positioned controls with TableLayoutPanel in the input
section and Dock-based layout for bottom buttons. Fixes controls being
hidden at non-100% DPI scaling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Fill control last so edge-docked controls get space first
- Remove stale hardcoded Location on bottom panel
- Switch to Sizable border with MinimumSize so user can resize
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix critical: use MdiParentForm (custom property) instead of MdiParent
(WinForms property) in ImportBom_Click to avoid InvalidOperationException
- Add null guard in CreateNests_Click
- Set Drawing.Material from BOM group
- Move DxfImporter creation outside loop
- Improve summary label text with reason descriptions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Task 9: CreateNests_Click validates plate dimensions, then for each
MaterialGroup creates a Nest with name '{job} - {thickness} {material}',
sets PlateDefaults (size, thickness, material, quadrant=1, spacing),
imports each matched DXF via DxfImporter, converts entities to Program
with leading RapidMove offset handling (same pattern as CadConverterForm),
sets Quantity.Required from BOM qty, then opens an EditNestForm for each
nest that has drawings. Summary MessageBox reports count and import errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>