Commit Graph

49 Commits

Author SHA1 Message Date
bc3f1543ee refactor: extract FindCrossing and SplitAtCrossing from RemoveSelfIntersections
Break deeply nested loop structure into focused helper methods,
reducing max nesting from 5 levels to 2. Uses GetRange/AddRange
for cleaner loop building.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:20:11 -04:00
6b0bafc9de merge: integrate NFP-based autonesting from feature/nfp-autonest
Brings in the full NFP implementation: ConvexDecomposition, NoFitPolygon,
InnerFitPolygon, NfpCache, BottomLeftFill, SimulatedAnnealing optimizer,
and INestOptimizer interface. Resolves conflicts by keeping master's
progress reporting infrastructure alongside the new AutoNest methods,
and adapting RunAutoNest_Click to use NFP AutoNest with async/cancellation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:40:38 -04:00
09fc8b889e fix: close polygon offset shape and handle nest template load failure
Shape.OffsetEntity computed joins between consecutive offset segments
but never joined the last segment back to the first, leaving the
closing corner with a straight line instead of a proper miter/arc.
Track the first entity and apply the same join logic after the loop.

Also wrap nest template loading in try-catch so a corrupt template
file doesn't crash the app on startup — falls back to default nest.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 07:37:39 -04:00
c68139e15e merge: resolve .gitignore conflict, keep both entries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 07:22:45 -04:00
66b3aeafc1 fix: correct inverted quadrant-to-exit-point mapping in GetExitPoint
The exit point should be the corner farthest from the origin so the
perimeter (cut last) ends near the machine home. The mapping was
backwards — Q1 (origin bottom-left) was returning (0,0) instead of
(w,l).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:59:04 -04:00
616575e0ee feat: wire contour re-indexing into ContourCuttingStrategy.Apply()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 23:57:17 -04:00
2b4cb849ba feat: add Shape.ReindexAt(Vector, Entity) for contour reordering
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 23:54:54 -04:00
3de44a7293 feat: add Arc.SplitAt(Vector) splitting primitive 2026-03-12 23:53:25 -04:00
b2ff704b73 feat: add Line.SplitAt(Vector) splitting primitive 2026-03-12 23:53:23 -04:00
441628eff2 feat: add ContourCuttingStrategy orchestrator
Exit point from plate quadrant, nearest-neighbor cutout
sequencing via ShapeProfile + ClosestPointTo, contour type
detection, and normal angle computation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:15:38 -04:00
ac7d90ae17 feat: add CuttingParameters and configuration classes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:13:36 -04:00
459738e373 feat: add Tab hierarchy (4 classes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:13:31 -04:00
b112f70f6a feat: add LeadOut hierarchy (5 classes)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:12:09 -04:00
f17db1d2f9 feat: add LeadIn hierarchy (7 classes)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 23:07:31 -04:00
e4e1d9b5a3 refactor: rename DefinedShape to ShapeProfile
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:19:49 -04:00
612b540d9d refactor: rename Size.Height to Size.Length across codebase
"Length" is more natural than "height" for flat plate materials.
Renames the field on OpenNest.Geometry.Size, Box.Height property,
and all references across 38 files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:01:40 -04:00
d0d334e734 fix: use chain tolerance for shape building to handle DXF endpoint gaps
DXF files can have endpoint gaps at entity junctions that fall right at
the floating-point boundary of Tolerance.Epsilon (0.00001). This caused
shapes to not close, resulting in 0 area and 0% utilization in Best-Fit.

Added ChainTolerance (0.0001) for endpoint chaining in GetConnected and
Shape.IsClosed, keeping the tighter Epsilon for geometric precision.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 08:37:39 -04:00
3f3b07ef5d feat: add NFP-based mixed-part autonesting
Implement geometry-aware nesting using No-Fit Polygons and simulated
annealing optimization. Parts interlock based on true shape rather than
bounding boxes, producing tighter layouts for mixed-part scenarios.

New types in Core/Geometry:
- ConvexDecomposition: ear-clipping triangulation for concave polygons
- NoFitPolygon: Minkowski sum via convex decomposition + Clipper2 union
- InnerFitPolygon: feasible region computation for plate placement

New types in Engine:
- NfpCache: caches NFPs keyed by (drawingId, rotation) pairs
- BottomLeftFill: places parts using feasible regions from IFP - NFP union
- INestOptimizer: abstraction for future GA/parallel upgrades
- SimulatedAnnealing: optimizes part ordering and rotation

Integration:
- NestEngine.AutoNest(): new public entry point for mixed-part nesting
- MainForm.RunAutoNest_Click: uses AutoNest instead of Pack
- NestingTools.autonest_plate: new MCP tool for Claude Code integration
- Drawing.Id: auto-incrementing identifier for NFP cache keys
- Clipper2 NuGet added to OpenNest.Core for polygon boolean operations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 08:08:22 -04:00
78ee65d946 fix: resolve grid overlap bug and parallelize fill loops
The push algorithm's copy distance formula (bboxDim - slideDistance)
produced distances smaller than the part width when inflated boundary
arc vertices interacted spuriously, causing ~0.05 unit overlaps between
all adjacent grid parts.

Two fixes applied:
- Clamp ComputeCopyDistance to bboxDim + PartSpacing minimum
- Use circumscribed polygons (R/cos(halfStep)) for PartBoundary arc
  discretization so chord segments never cut inside the true arc,
  eliminating the ChordTolerance offset workaround

Also parallelized three sequential fill loops using Parallel.ForEach:
- FindBestFill angle sweep (up to 38 angles x 2 directions)
- FillPattern angle sweep for group/pair fills
- FillRemainingStrip rotation loop

Added diagnostic logging to HasOverlaps, FindCopyDistance, and
FillRecursive for debugging fill issues.

Test result: 45 parts @ 79.6% -> 47 parts @ 83.1%, zero overlaps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 00:36:48 -04:00
23b5358352 perf: add CloneAtOffset to skip re-rotation and bbox walk on part clones
Part.Clone() re-clones from the drawing's unrotated program, re-rotates,
and walks all CNC codes twice for bounding box — 4 O(c) passes per clone.
CloneAtOffset clones from the already-rotated program and computes the
bounding box arithmetically, reducing to 1 O(c) pass per clone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:53:02 -04:00
5fc16e9dae perf: halve rotating calipers edge iterations
Opposite hull edges (180° apart) produce identical bounding rectangles
(width/height swapped), so only half the edges need to be checked to
find the minimum area. Applied to the unconstrained overload only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:17:24 -04:00
91908c1732 perf: optimize fill hot path — bbox pre-check and geometry inner loop
- Add bounding box rejection in HasOverlaps to skip expensive
  Part.Intersects (CNC→geometry conversion) for non-adjacent parts.
  Eliminates ~35% CPU in IsBetterValidFill for grid layouts.
- Optimize RayEdgeDistance: access Line fields directly instead of
  property getters (avoids Vector struct copies), inline IsEqualTo
  with direct range comparison (avoids Math.Abs), and precompute
  deltas for reuse in interpolation.
- Cache line endpoints in DirectionalDistance outer loop to avoid
  repeated struct copies in the inner loop.
- Add fill timer to ActionClone.Fill, displayed in PlateView status
  bar as "Fill: N parts in M ms".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 22:10:25 -04:00
d1353d58e5 feat: add configurable chord tolerance for offset drawing and push geometry
Replace hardcoded PushChordTolerance constant with a configurable
OffsetTolerance property on PlateView (default 0.001), giving smoother
arc profiles in offset drawing and push-to-part collision detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:48:05 -04:00
a9af5effc5 feat: add Plate.GetRemnants() for finding empty edge strips
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:43:27 -04:00
a92ba7af89 feat: resolve per-entity color and line type from DXF imports
Add LineTypeName to Layer and propagate resolved color/line-type
through all DXF entity conversions (Arc, Circle, Line, Spline,
Polyline, LwPolyline, Ellipse). Entities that inherit ByLayer
properties now correctly resolve to their layer's values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:27:56 -04:00
e078ef4b77 feat: implement color and line type exclusion filters on CadConverterForm
Add per-entity IsVisible flag and wire up the Colors and Line Types
checkedlistboxes to filter entities by exclusion — checking an item
hides matching entities from the preview and from drawing export.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:14:09 -04:00
b738d4c72c refactor: clean up NestEngine — collapse overloads, extract helper, remove dead code
- Fill(NestItem) and Fill(List<Part>) now delegate to their Box overloads
- Add Part.CreateAtOrigin() to replace repeated 4-line build-at-origin pattern
  used in NestEngine, RotationSlideStrategy, and PairEvaluator
- Remove dead code: FillArea overloads, Fill(NestItem, int), FillWithPairs(NestItem),
  ConvertTileResultToParts, PackBottomLeft.FindPointHorizontal, Pattern.GetLines/GetOffsetLines,
  unused count variable in FillNoRotation
- Simplify IsBetterValidFill to delegate to IsBetterFill after overlap check

NestEngine reduced from 717 to 484 lines.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 21:31:15 -05:00
73c20c30ce fix: exclude rapid moves from Part.Intersects to fix FillLinear rejection
Part.Intersects included rapid move geometry (G00 traversals) when
checking for overlaps, causing false positives. The overlap validation
added in 5bebfcb rejected all FillLinear configs, producing 0 parts.
Every other GetShapes caller already filters SpecialLayers.Rapid.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:52:35 -05:00
dd7383467b feat: add Polygon.ContainsPoint using ray casting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:13:12 -05:00
6ce81d99a7 feat: convert OpenNest.Core to .NET 8 SDK-style project
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 13:21:16 -05:00
1c8763a201 feat: update FindBestRotation to use rotating calipers algorithm
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 22:49:09 -05:00
a190ed25b4 feat: add RotatingCalipers class with minimum bounding rectangle algorithm
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 22:46:42 -05:00
13e93c3534 feat: add ConvexHull class with Andrew's monotone chain algorithm
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 22:44:04 -05:00
4d270ae68e fix: remove self-intersecting loops from polygon offset
Polygon offset at concave corners creates geometry that folds back
through itself. Added RemoveSelfIntersections() to Polygon that
detects non-adjacent edge crossings and removes the smaller loop
at each crossing. Applied to both collision detection and rendering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 22:23:44 -05:00
08b31d0797 fix: stop push at contact boundary and filter edges by direction
RayEdgeDistance returned double.MaxValue for touching vertices (dist ≈ 0),
causing rays from other vertices to hit the far side of stationary parts
and allow movement through obstacles. Now returns 0 when touching so the
distance > 0 check in PushSelected correctly prevents further movement.

Added directional edge filtering using outward normals to discard
back-facing edges before ray checks, reducing line count by ~2/3.
DirectionalDistance now checks both StartPoint and EndPoint per line
to preserve vertices at filtered edge boundaries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:42:40 -05:00
1c4015ac62 fix: CadConverter SetRotation ignored rotation parameter
SetRotation always forced CW regardless of the requested rotation,
so cutouts (which should be CCW for kerf-left) were also set to CW.
Now uses the rotation parameter to set the correct winding direction.

Also reverts the Shape.OffsetEntity cutout side inversion since the
correct fix is proper winding from the converter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:41:42 -05:00
af00fa36eb fix: invert offset side for cutouts in Shape.OffsetEntity
Cutouts wind opposite to the perimeter, so using the same OffsetSide
expands holes instead of shrinking them. Invert the side for cutouts
so the offset buffer correctly contracts holes inward.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:40:08 -05:00
30429ab955 fix: compensate for inscribed polygon in offset distance
Polygon chords are always inside the actual arc, making the offset
boundary smaller than intended. Add PushChordTolerance to the offset
distance so the polygon conservatively overestimates, ensuring the
real offset geometry never overlaps stationary parts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:37:58 -05:00
28238cc246 feat: adaptive arc segmentation based on chord tolerance
Add SegmentsForTolerance(double) to Arc and Circle that calculates the
minimum segments needed to keep sagitta within the given tolerance.
Add Shape.ToPolygonWithTolerance() that uses it per arc/circle.

Push distance now uses 0.08" chord tolerance instead of a fixed segment
count, giving appropriate resolution for each arc based on its radius.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:59:30 -05:00
ec5eff4884 perf: reduce arc segments for push distance calculation
ToPolygon() defaults to 1000 segments per arc, causing ~33k line
segments and O(n*m) slowdown. Use 36 segments (10° resolution) which
is more than sufficient for push distance accuracy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:56:27 -05:00
6332298912 feat: add Helper.DirectionalDistance for polygon-based push
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:27:32 -05:00
269c8677f8 feat: add Helper.GetPartLines and GetOffsetPartLines
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:25:11 -05:00
26d020ce3d feat: move PushDirection enum to OpenNest.Core
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:23:02 -05:00
e4df9cacd8 Move converters to OpenNest.Converters namespace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:04:46 -05:00
67a66e10fd Move geometry primitives to OpenNest.Geometry namespace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:02:12 -05:00
8d9aebb83f Move math utilities to OpenNest.Math namespace
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 12:51:16 -05:00
ea71978e0d Rename Quantity to DwgQty
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 08:16:48 -05:00
255d3962a6 Rename CircularMove to ArcMove
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 08:16:43 -05:00
2d956fd3f7 Restructure project layout to flatten directory structure
Move all projects from Source/ to repository root for simpler navigation.
- Remove External/ dependency DLLs (will use NuGet packages)
- Remove Installer/ NSIS script
- Replace PartCollection/PlateCollection with ObservableList
- Add packages.config for NuGet dependencies

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 20:29:12 -05:00