Files
OpenNest/CLAUDE.md
T
aj 6a0fba0fec Add OpenNest.Benchmark: generic head-to-head engine comparison harness
Loads any .nest file (or folder of them) via NestReader and nests every
drawing with quantity > 0 using each registered NestEngineBase, so it
works sight-unseen against arbitrary real jobs without any hardcoded
geometry. Optionally sweeps a fixed --sheet-sizes list instead of each
file's own plate size.

- BenchmarkJob/JobLoader build immutable job specs; a fresh Plate and
  NestItem list is created per (job, engine) run so state never leaks
  between engines or jobs.
- NestValidator rejects a layout if any part falls outside the work
  area, any two parts are closer than PartSpacing (checked via each
  part's own world-space polygon inflated by the spacing, so it holds
  for arbitrary concave/holed geometry, not just bounding boxes), or a
  drawing gets more parts than requested.
- Scoring matches Plate.Utilization() (placed area / full sheet area);
  ties among fully-placed layouts break on the smaller used bounding
  box (more usable remnant).
- Report prints a per-job ranked breakdown plus a per-engine summary
  (wins, avg utilization, time), and can write a flat CSV.

Verified end-to-end against a synthetic .nest file (not committed)
against the four built-in engines; caught a genuine out-of-work-area
bug in StripNestEngine in the process.
2026-09-15 18:31:36 -04:00

18 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

OpenNest is a Windows desktop application for CNC nesting — arranging 2D parts on material plates to minimize waste. It imports DXF drawings, places parts onto plates using NFP-based (No Fit Polygon) and rectangle-packing algorithms, and can export nest layouts as DXF or post-process them to G-code for CNC cutting machines.

Build

This is a .NET 8 solution using SDK-style .csproj files targeting net8.0-windows. Build with:

dotnet build OpenNest.sln

NuGet dependencies: ACadSharp 3.1.32 (DXF/DWG import/export, in OpenNest.IO), System.Drawing.Common 8.0.10, ModelContextProtocol + Microsoft.Extensions.Hosting (in OpenNest.Mcp), Microsoft.ML.OnnxRuntime (in OpenNest.Engine for ML angle prediction), Microsoft.EntityFrameworkCore.Sqlite (in OpenNest.Training).

Architecture

Nine projects form a layered architecture:

OpenNest.Core (class library)

Domain model, geometry, and CNC primitives organized into namespaces:

  • Root (namespace OpenNest): Domain model — NestPlate[]Part[]DrawingProgram. A Nest is the top-level container. Each Plate has a size, material, quadrant, spacing, and contains placed Part instances. Each Part references a Drawing (the template) and has its own location/rotation. A Drawing wraps a CNC Program. Also contains utilities: PartGeometry, Align, Sequence, Timing.
  • CNC (CNC/, namespace OpenNest.CNC): Program holds a list of ICode instructions (G-code-like: RapidMove, LinearMove, ArcMove, SubProgramCall) and an optional Variables dictionary of VariableDefinition entries. Programs support absolute/incremental mode conversion, rotation, offset, bounding box calculation, and cloning. VariableDefinition stores a named variable's expression, resolved value, and flags (Inline, Global). ProgramVariableManager manages numbered machine variables for post-processor output.
  • Geometry (Geometry/, namespace OpenNest.Geometry): Spatial primitives (Vector, Box, Size, Spacing, BoundingBox, IBoundable) and higher-level shapes (Line, Arc, Circle, Polygon, Shape) used for intersection detection, area calculation, and DXF conversion. Also contains Intersect (intersection algorithms), ShapeBuilder (entity chaining), GeometryOptimizer (line/arc merging), SpatialQuery (directional distance, ray casting, box queries), ShapeProfile (perimeter/area analysis), NoFitPolygon, InnerFitPolygon, ConvexHull, ConvexDecomposition, RotatingCalipers, and Collision (overlap detection with Sutherland-Hodgman polygon clipping and hole subtraction).
  • Converters (Converters/, namespace OpenNest.Converters): Bridges between CNC and Geometry — ConvertProgram (CNC→Geometry), ConvertGeometry (Geometry→CNC), ConvertMode (absolute↔incremental).
  • Math (Math/, namespace OpenNest.Math): Angle (radian/degree conversion), Tolerance (floating-point comparison), Trigonometry, Generic (swap utility), EvenOdd, Rounding (factor-based rounding), ExpressionEvaluator (arithmetic expression parser for G-code variable expressions with $name references). Note: OpenNest.Math shadows System.Math — use System.Math fully qualified where both are needed.
  • CNC/CuttingStrategy (CNC/CuttingStrategy/, namespace OpenNest.CNC): ContourCuttingStrategy orchestrates cut ordering, lead-ins/lead-outs, and tabs. Includes LeadIn/LeadOut hierarchies (line, arc, clean-hole variants), Tab hierarchy (normal, machine, breaker), and CuttingParameters/AssignmentParameters/SequenceParameters configuration.
  • Collections (Collections/, namespace OpenNest.Collections): ObservableList<T>, DrawingCollection.
  • CutOffs (namespace OpenNest): CutOff (axis-aligned cut line with position, axis, optional start/end limits), CutOffAxis enum (Horizontal, Vertical), CutOffSettings (clearance, overtravel, min segment length, direction), CutDirection enum (TowardOrigin, AwayFromOrigin). Cut-offs generate CNC Program objects with trimmed line segments that avoid parts.
  • Splitting (Splitting/, namespace OpenNest): DrawingSplitter splits a Drawing into multiple pieces along split lines. ISplitFeature strategy pattern with implementations: StraightSplit (clean edge), WeldGapTabSplit (rectangular tab spacers on one side), SpikeGrooveSplit (interlocking spike/V-groove pairs). AutoSplitCalculator computes split lines for fit-to-plate and split-by-count modes. Supporting types: SplitLine, SplitParameters, SplitFeatureResult.
  • Quadrant system: Plates use quadrants 1-4 (like Cartesian quadrants) to determine coordinate origin placement. This affects bounding box calculation, rotation, and part positioning.

OpenNest.Engine (class library, depends on Core)

Nesting algorithms with a pluggable engine architecture. NestEngineBase is the abstract base class; DefaultNestEngine (formerly NestEngine) provides the multi-phase fill strategy. NestEngineRegistry manages available engines (built-in + plugins from Engines/ directory) and the globally active engine.

  • Engine hierarchy: NestEngineBase (abstract) → DefaultNestEngine (Linear, Pairs, RectBestFit, Remainder phases) → VerticalRemnantEngine (optimizes for right-side drop), HorizontalRemnantEngine (optimizes for top-side drop). Custom engines subclass NestEngineBase and register via NestEngineRegistry.Register() or as plugin DLLs in Engines/.
  • IFillComparer: Interface enabling engine-specific scoring. DefaultFillComparer (count-then-density), VerticalRemnantComparer (minimize X-extent), HorizontalRemnantComparer (minimize Y-extent). Engines provide their comparer via CreateComparer() factory, grouped into FillPolicy on FillContext.
  • NestEngineRegistry: Static registry — Create(Plate) factory, ActiveEngineName global selection, LoadPlugins(directory) for DLL discovery. All callsites use NestEngineRegistry.Create(plate) except BruteForceRunner which uses new DefaultNestEngine(plate) directly for training consistency.
  • Fill/ (namespace OpenNest.Engine.Fill): Fill algorithms — FillLinear (grid-based), FillExtents (extents-based pair tiling), PairFiller (interlocking pairs), ShrinkFiller, RemnantFiller/RemnantFinder, Compactor (post-fill gravity compaction), FillScore (lexicographic comparison: count > utilization > compactness), Pattern/PatternTiler, PartBoundary, RotationAnalysis, AngleCandidateBuilder, BestCombination, AccumulatingProgress.
  • Strategies/ (namespace OpenNest.Engine.Strategies): Pluggable fill strategy layer — IFillStrategy interface, FillContext, FillStrategyRegistry (auto-discovers strategies via reflection, supports plugin DLLs), FillHelpers. Built-in strategies: LinearFillStrategy, PairsFillStrategy, RectBestFitStrategy, ExtentsFillStrategy.
  • BestFit/ (namespace OpenNest.Engine.BestFit): NFP-based pair evaluation pipeline — BestFitFinder orchestrates angle sweeps, PairEvaluator/IPairEvaluator scores part pairs, RotationSlideStrategy/ISlideComputer computes slide distances. BestFitCache and BestFitFilter optimize repeated lookups.
  • RectanglePacking/ (namespace OpenNest.RectanglePacking): FillBestFit (single-item fill, tries horizontal and vertical orientations), PackBottomLeft (multi-item bin packing, sorts by area descending). Both operate on Bin/Item abstractions.
  • CirclePacking/ (namespace OpenNest.CirclePacking): Alternative packing for circular parts.
  • Nfp/ (namespace OpenNest.Engine.Nfp): Internal NFP-based single-part placement utilities — AutoNester (NFP placement with simulated annealing), BottomLeftFill (BLF placement), NfpCache (computed NFP caching), SimulatedAnnealing (optimizer), INestOptimizer/OptimizationResult. Not exposed as a nest engine; used internally for individual part placement.
  • ML/ (namespace OpenNest.Engine.ML): AnglePredictor (ONNX model for predicting good rotation angles), FeatureExtractor (part geometry features), BruteForceRunner (full angle sweep for training data).
  • NestItem: Input to the engine — wraps a Drawing with quantity, priority, and rotation constraints.
  • NestProgress: Progress reporting model with NestPhase enum for UI feedback.

OpenNest.IO (class library, depends on Core)

File I/O and format conversion. Uses ACadSharp for DXF/DWG support.

  • DxfImporter/DxfExporter — DXF file import/export via ACadSharp.
  • NestReader/NestWriter — custom ZIP-based nest format (JSON metadata + G-code programs, v2 format).
  • ProgramReader — G-code text parser.
  • Extensions — conversion helpers between ACadSharp and OpenNest geometry types.
  • CadImporter — shared "DXF → Drawing" service used by the UI, console, MCP, API, and training projects. Two-stage API: Import(path, options) loads raw entities, runs bend detection, and returns a mutable CadImportResult; BuildDrawing(result, visible, bends, quantity, customer, editedProgram) produces a fully-populated Drawing with Source.Offset, SourceEntities, SuppressedEntityIds, and bends. ImportDrawing(path, options) composes both stages for headless callers.
  • CadImportOptions, CadImportResult — inputs and intermediate state for CadImporter.

OpenNest.Console (console app, depends on Core + Engine + IO)

Command-line interface for batch nesting. Supports DXF import, plate configuration, linear fill, and NFP-based auto-nesting (--autonest).

OpenNest.Gpu (class library, depends on Core + Engine)

GPU-accelerated pair evaluation for best-fit nesting. GpuPairEvaluator implements IPairEvaluator, GpuSlideComputer implements ISlideComputer, and PartBitmap handles rasterization. GpuEvaluatorFactory provides factory methods.

OpenNest.Training (console app, depends on Core + Engine)

Training data collection for ML angle prediction. TrainingDatabase stores per-angle nesting results in SQLite via EF Core for offline model training.

OpenNest.Benchmark (console app, depends on Core + Engine + IO)

Compares registered NestEngineBase implementations against each other on real .nest files. Fully generic — it never hardcodes drawing geometry, just reads whatever drawings/quantities/plate settings each input file already has.

  • JobLoader builds BenchmarkJobs from a .nest file or a folder of them via NestReader, using every drawing with Quantity.Required > 0. --sheet-sizes can sweep a fixed list of plate sizes instead of each file's own.
  • BenchmarkRunner gives each (job, engine) pair a fresh Plate/NestItem list (BenchmarkJob.CreatePlate()/CreateItems()) so engines can't see each other's mutated state, then calls the engine's Nest() and times it.
  • NestValidator checks the returned layout: every part inside Plate.WorkArea(), every pair at least Plate.PartSpacing apart (checked geometrically via each part's own world-space polygon, inflated by the spacing — works on arbitrary concave/holed shapes, not just bounding boxes), and no drawing over its requested quantity. An invalid or throwing run scores zero for that job.
  • Scoring matches Plate.Utilization() (placed drawing area / full sheet area, Plate.Area()). If an engine placed every requested part, ties are broken by the smaller used-bounding-box (Report's ranking rule) — a more compact layout leaves a bigger usable remnant.
  • --engines Name1,Name2 filters to specific registered engines (default: all); --csv <path> writes a flat per-job CSV alongside the console report.

OpenNest.Mcp (console app, depends on Core + Engine + IO)

MCP server for Claude Code integration. Exposes nesting operations as MCP tools over stdio transport. Published to ~/.claude/mcp/OpenNest.Mcp/.

  • Tools/InputTools: load_nest, import_dxf, create_drawing (built-in shapes or G-code).
  • Tools/SetupTools: create_plate, clear_plate.
  • Tools/NestingTools: fill_plate, fill_area, fill_remnants, pack_plate.
  • Tools/InspectionTools: get_plate_info, get_parts, check_overlaps.
  • NestSession — in-memory state across tool calls (current Nest, standalone plates/drawings).

OpenNest (WinForms WinExe, depends on Core + Engine + IO)

The UI application with MDI interface.

  • Forms/: MainForm (MDI parent), EditNestForm (MDI child per nest), SplitDrawingForm (split oversized drawings into smaller pieces, launched from CadConverterForm), plus dialogs for plate editing, auto-nesting, DXF conversion, cut parameters, etc.
  • Controls/: PlateView (2D plate renderer with zoom/pan, supports temporary preview parts), DrawingListBox, DrawControl, QuadrantSelect.
  • Actions/: User interaction modes — ActionSelect, ActionClone, ActionFillArea, ActionSelectArea, ActionZoomWindow, ActionSetSequence, ActionCutOff.
  • Post-processing: IPostProcessor plugin interface loaded from DLLs in a Posts/ directory at runtime.

File Format

Nest files (.nest, ZIP-based) use v2 JSON format:

  • nest.json — single JSON file containing all nest metadata: nest info (name, units, customer, dates, notes), plate defaults (size, thickness, quadrant, spacing, material, edge spacing), drawings array (id, name, color, quantity, priority, rotation constraints, material, source), and plates array (id, size, material, edge spacing, parts with drawingId/x/y/rotation, cutoffs with x/y/axis/startLimit/endLimit)
  • programs/program-N — G-code text for each drawing's cut program (N = drawing id)
  • bestfits/bestfit-N — JSON array of best-fit pair evaluation results per drawing, keyed by plate size/spacing (optional, only present if best-fit data was computed)

Tool Preferences

Always use Roslyn Bridge MCP tools (mcp__RoslynBridge__*) as the primary method for exploring and analyzing this codebase. It is faster and more efficient than file-based searches. Use it for finding symbols, references, diagnostics, type hierarchies, and code navigation. Only fall back to Glob/Grep when Roslyn Bridge cannot fulfill the query.

Code Style

  • Always use var instead of explicit types (e.g., var parts = new List<Part>(); not List<Part> parts = new List<Part>();).

Documentation Maintenance

Always keep README.md and CLAUDE.md up to date when making changes that affect project structure, architecture, build instructions, dependencies, or key patterns. If you add a new project, change a namespace, modify the build process, or alter significant behavior, update both files as part of the same change.

Do not commit design specs, implementation plans, or other temporary planning documents (docs/superpowers/ etc.) to the repository. These are working documents only — keep them local and untracked.

Key Patterns

  • OpenNest.Core uses multiple namespaces: OpenNest (root domain), OpenNest.CNC, OpenNest.Geometry, OpenNest.Converters, OpenNest.Math, OpenNest.Collections.
  • OpenNest.Engine uses sub-namespaces: OpenNest.Engine.Fill (fill algorithms), OpenNest.Engine.Strategies (pluggable strategy layer), OpenNest.Engine.BestFit, OpenNest.Engine.Nfp (NFP-based nesting, not yet integrated), OpenNest.Engine.ML, OpenNest.Engine.RapidPlanning, OpenNest.Engine.Sequencing.
  • ObservableList<T> provides ItemAdded/ItemRemoved/ItemChanged events used for automatic quantity tracking between plates and drawings.
  • Angles throughout the codebase are in radians (use Angle.ToRadians()/Angle.ToDegrees() for conversion).
  • Tolerance.Epsilon is used for floating-point comparisons across geometry operations.
  • Nesting uses async progress/cancellation: IProgress<NestProgress> and CancellationToken flow through the engine to the UI's NestProgressForm.
  • Compactor performs post-fill gravity compaction — after filling, parts are pushed toward a plate edge using directional distance calculations to close gaps between irregular shapes.
  • FillScore uses lexicographic comparison (count > utilization > compactness) to rank fill results consistently across all fill strategies.
  • Cut-off materialization lifecycle: CutOff objects live on Plate.CutOffs. Each generates a Drawing (with IsCutOff = true) whose Program contains trimmed line segments. Plate.RegenerateCutOffs(settings) removes old cut-off Parts, recomputes programs, and re-adds them to Plate.Parts. Regeneration triggers: cut-off add/remove/move, part drag complete, fill complete, plate transform. Cut-off Parts are excluded from quantity tracking, utilization, overlap detection, and nest file serialization (programs are regenerated from definitions on load).
  • User-defined G-code variables: Programs can contain named variable definitions (name = expression [inline] [global]) referenced in coordinates with $name. Variables resolve to doubles at parse time for geometry/nesting. VariableRefs on Motion/Feedrate track the symbolic link so post processors can emit machine variable references. Cincinnati post maps non-inline variables to numbered machine variables (#200+) with descriptive comments. Global variables share a number across programs; local variables get per-drawing numbers. ProgramReader uses a two-pass parse (collect definitions, then parse G-code with substitution). NestWriter serializes definitions and $references back to text for round-trip fidelity.
  • CAD import pipeline: All "DXF → Drawing" conversion goes through OpenNest.IO.CadImporter. The UI form uses Import on file load (storing the mutable result in a FileListItem) and BuildDrawing on save (passing the user's current visible entities and bends). Console, MCP, API, and Training projects use ImportDrawing for headless conversion. This guarantees all callers produce drawings with the same shape: pierce-point Source.Offset, stable SourceEntities with GUIDs, SuppressedEntityIds, detected bends, and metadata.