Files
OpenNest/README.md
T

135 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenNest
A Windows desktop application for CNC nesting — imports DXF drawings, arranges parts on material plates, and exports layouts as DXF or G-code for cutting.
<p>
<a href="screenshots/screenshot-nest-1.png"><img src="screenshots/screenshot-nest-1.png" width="420" alt="OpenNest - parts nested on a 36x36 plate"></a>
<a href="screenshots/screenshot-nest-2.png"><img src="screenshots/screenshot-nest-2.png" width="420" alt="OpenNest - 44 parts nested on a 60x120 plate"></a>
</p>
## Features
- **Import / export** — DXF & DWG parts (ACadSharp), Excel BOMs, bend-line detection, built-in parametric shapes; export DXF or post-processed G-code.
- **Nesting** — pluggable whole-job engines (Default, Strip, Vertical/Horizontal Remnant, StockLadder, plus DLL plugins), NFP-based interlocking pair evaluation, gravity compaction, rotation sweeps, multi-plate/multi-material jobs.
- **Plate operations** — sheet cut-offs, oversized-part splitting (straight, weld-gap tabs, spike-groove), interactive editing.
- **CNC output** — configurable lead-ins/outs and tabs, contour editing, user-defined G-code variables (`$name` → `#200+` machine variables), plugin post-processors (Cincinnati CL-707/800/900/940/CLX included).
## Requirements
- Windows 10+ for the desktop app; the console, API, and most test projects build on Linux/macOS too.
- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
## Build, Test, Run
```bash
git clone https://git.thecozycat.net/aj/OpenNest.git
cd OpenNest
dotnet build OpenNest.sln # full solution (Windows)
dotnet test OpenNest.Engine.Tests/OpenNest.Engine.Tests.csproj # cross-platform engine tests
dotnet test OpenNest.Tests/OpenNest.Tests.csproj # core/engine/IO/API tests
dotnet run --project OpenNest/OpenNest.csproj # desktop app (Windows)
```
`OpenNest.WinForms.Tests` (desktop-assembly tests) runs on Windows only. Format changed files with `dotnet format OpenNest.sln --include <path>`.
Shared coding-agent guidance lives in [AGENTS.md](AGENTS.md). [CLAUDE.md](CLAUDE.md) imports it for Claude Code compatibility; make shared instruction changes in AGENTS.md, not in duplicate agent-specific copies.
### Opt-in fill performance measurements
```bash
OPENNEST_RUN_FILL_PERF=1 dotnet test OpenNest.Tests/OpenNest.Tests.csproj -c Release \
--filter "Category=FillPerformance" --logger "console;verbosity=detailed"
```
For PowerShell, set `$env:OPENNEST_RUN_FILL_PERF = '1'` before the `dotnet test` command and remove it afterward with `Remove-Item Env:OPENNEST_RUN_FILL_PERF`. Without the exact value `1`, these tests skip, including during normal suite runs.
The comparer microbenchmark uses deterministic, valid nonoverlapping rectangles, warmup, seven interleaved actual/reference batches, both argument orders, and an equal-count control. It reports min/median/max duration and synchronous current-thread allocations, both per batch and per call; construction and correctness assertions are outside the timed region. Keep inputs and batch sizes fixed when comparing changes. These measurements are not timing-threshold tests and do not establish whole-job speedups. Debug-only skipped-score work checks run with `dotnet test OpenNest.Tests/OpenNest.Tests.csproj -c Debug --filter "FullyQualifiedName~DefaultFillComparerWorkTests"`.
The group-pattern measurement also compares default scoring with a custom comparer on a valid two-part group. It includes the real fill and scheduling work and omits allocation totals because fills can use worker threads. Run only that case with `--filter "FullyQualifiedName~GroupPattern_ReportsDefaultAndCustomComparer"`; helper behavior and Debug score-work checks use `--filter "FullyQualifiedName~FillHelpersTests"`. Workload details and measured limitations are in [the fill performance report](docs/performance/fill-performance.md). The report's Task 5 section additionally records a `--parallel 1` whole-job before/after over a small real-DXF manifest corpus (`OpenNest.Benchmark` with a `*.manifest.json`): identical valid outcomes, placed counts, and cost; timing differences inside baseline run-to-run spread, so whole-job timing is inconclusive — outcome confirmation, not a speedup or performance-equivalence claim.
The extents measurement exercises repeated column rebuilding with a closed triangle at zero and positive spacing. It times the synchronous production fill and reports current-thread allocations; the frozen legacy implementation is used only for correctness checks outside timing. Run only that case with `--filter "FullyQualifiedName~Extents_ReportsRepeatedColumnRebuilds"`. Extents behavior, overlap-fallback, and Debug boundary-preparation checks use `--filter "FullyQualifiedName~FillExtentsTests|FullyQualifiedName~StrategyOverlapTests"`. Finite nonnegative spacing uses the equivalent bounding-box pitch without discarded vertical boundary preparation; negative/nonfinite spacing retains the legacy calculation rather than adding validation.
The synchronous rotated-pattern construction control uses a 32-part native-arc group at angles 0 and 0.37. Run it with `--filter "FullyQualifiedName~RotatedPattern_ReportsBoundsConstruction"`. Task 3 reuses the extents benchmark above; Debug `--filter "FullyQualifiedName~BoundsWork"` checks part-bounds work. Only three extents recomputations were removable: anchor, vertical-shift, and group-clone recomputations remain because removing them changes exact floating-point layouts. See the measured report for the partial-delivery evidence and inconclusive timing results.
The feature-extraction measurement compares default `FeatureExtractor.Extract` (32×32 training bitmask) against `Extract(drawing, includeBitmask: false)` on a synthetic ring; run it with `--filter "FullyQualifiedName~FeatureExtraction_ReportsFullAndScalarOnly"`. The one-argument overload keeps generating the bitmap for training callers; scalar-only inference callers receive `Bitmask = null` with identical scalar features. Behavior and Debug bitmask-scan counter checks use `--filter "FullyQualifiedName~FeatureExtractorTests"`.
`AngleCandidateBuilder` now checks the shared one-attempt predictor initialization before extracting anything; unavailable or failed model loads retain the ordered fallback sweep, while available inference requests scalar-only features. Initialization completion is published only after the load outcome is known. Measure the warm missing-model path with `--filter "FullyQualifiedName~IrregularAngles_ReportsWarmNoModelPath"`; this skips when a model is installed and never alters model files. Loader-concurrency and prediction-double tests use `--filter "FullyQualifiedName~AngleCandidateBuilderTests|FullyQualifiedName~AnglePredictorTests|FullyQualifiedName~FeatureExtractorTests"`. These tests do not establish actual ONNX accuracy. See [fill verification](docs/performance/fill-verification.md) for shared workflow safeguards.
### Quick start
1. File > New Nest
2. Import DXFs via the CAD Converter (layer/color filtering, bend detection, G-code preview) or create built-in shapes
3. Define plate size, material, quadrant, spacing
4. Fill — the engine arranges parts
5. Optionally add cut-off lines, then save `.nest`, export DXF, or post-process to G-code
## Command-Line Interface
```bash
dotnet run --project OpenNest.Console -- part.dxf --size 60x120 # fill one plate
dotnet run --project OpenNest.Console -- part1.dxf part2.dxf --size 60x120 --autonest
dotnet run --project OpenNest.Console -- project.zip # re-fill a nest file
```
Key options: `--size WxL`, `--autonest` (whole-job nesting), `--engine <name>` (jobs engine or fill strategy), `--quantity`, `--spacing`, `--template <nest>`, `--output <path>`, `--check-overlaps`, `--post <name>`, `--no-save`. Run without arguments for the full list.
## Benchmarking Engines
`OpenNest.Benchmark` runs every registered `INestingEngine` against `.nest` files (or a JSON manifest of DXFs + quantities) and scores by salvage-credited sheet area, with a penalty per unplaced part.
```bash
dotnet run --project OpenNest.Benchmark -- ./benchmark-jobs \
--sheet-sizes 48x96,60x120,72x120 --engines Default,StockLadder --csv results.csv
```
Layouts are validated (bounds, spacing, quantity, rotation, stock match); invalid runs place nothing and pay the penalty. `--parallel` (default 3) speeds up scoring but inflates `Time(ms)` — use `--parallel 1` when comparing speed. Pass `--sheet-sizes` for an unbiased run; otherwise only each file's original sizes are offered. `--progress` logs each solve's start, the engine's `NestJobProgress` (plate evaluations throttled to one line per 2 s, every plate commit) and its finish. Custom engines drop in as DLLs implementing `INestingEngine` (public parameterless constructor) in an `Engines/` folder next to the benchmark. Community engines live in [OpenNest-Engines](https://git.thecozycat.net/aj/OpenNest-Engines).
## Project Structure
| Project | Purpose |
|---------|---------|
| **OpenNest** | WinForms desktop app |
| **OpenNest.Core** | Domain model, geometry, CNC primitives |
| **OpenNest.Engine** | Nesting algorithms and whole-job contracts |
| **OpenNest.IO** | DXF/DWG, `.nest`, G-code, BOM I/O; CAD import |
| **OpenNest.Console** | Headless batch nesting |
| **OpenNest.Api / .Data** | Programmatic pipeline; machine & cutting-parameter data |
| **OpenNest.Gpu** | GPU-accelerated pair evaluation (ILGPU) |
| **OpenNest.Benchmark** | Head-to-head engine comparison |
| **OpenNest.Mcp** | MCP server for AI tool integration |
| **OpenNest.Posts.Cincinnati** | Cincinnati laser post-processor plugin |
| **\*.Tests** | Cross-platform suites; WinForms tests are Windows-only |
## Nesting Engines
Jobs-only API: engines implement `INestingEngine.Solve(NestJob)`; only `NestJobRunner` commits demand and stock, and every candidate passes the placement validator (bounds, spacing, rotation policy, stock match) before it consumes anything.
| Engine | Description |
|--------|-------------|
| **Default** | Multi-phase: linear fill → pairs → rect best-fit → extents |
| **Strip** | Iterative shrink-fill for mixed-drawing layouts |
| **Vertical / Horizontal Remnant** | Optimizes a clean remnant drop on one edge |
| **StockLadder** | Whole-job, stock-constrained baseline with salvage-credit ranking |
## File Format
`.nest` files are ZIP archives: `nest.json` (metadata, plates, drawings, placements), `programs/program-N` (G-code per drawing), optional `entities/`, sub-programs, and cached best-fit data.
## Supported Formats
| Format | Import | Export |
|--------|--------|--------|
| DXF | Yes | Yes |
| DWG | Yes | No |
| Excel BOM | Yes | No |
| G-code | No | Yes (post-processors) |
| `.nest` | Yes | Yes |
## Keyboard Shortcuts
`Ctrl+F` fill area · `F` zoom to fit · `Shift+wheel` / middle-click rotate · `X`/`Y` push · arrows nudge · `Shift+arrow` push.
## Status & License
Actively developed; core workflows run end-to-end from DXF import to G-code. Contributions welcome. MIT licensed — see [LICENSE](LICENSE).