diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3491a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,218 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# NSIS installer directory +Installer/* +!*.nsi + +# Visual Studo 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +*.[Cc]ache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Git worktrees +.worktrees/ + +# SQLite databases +*.db +*.db-journal + +# Claude Code +.claude/ +.superpowers/ +docs/superpowers/ + +# Launch settings +**/Properties/launchSettings.json + +# Local test config (contains user-specific paths to proprietary test assets) +OpenNest.Tests/test-config.json diff --git a/Build-Engines.ps1 b/Build-Engines.ps1 index b3c3fa3..237507a 100644 --- a/Build-Engines.ps1 +++ b/Build-Engines.ps1 @@ -1,29 +1,36 @@ <# .SYNOPSIS - Builds every plugin engine under Engines/ and deploys it to the benchmark. + Builds every plugin engine in this repo and deploys it to the OpenNest benchmark. .DESCRIPTION OpenNest.Benchmark loads plugin engines from an Engines/ folder next to its own - build output. This builds the benchmark plus each Engines/OpenNest.Engine.*/ project - (test subprojects are skipped) and copies each engine DLL into that folder. + build output. This builds the benchmark in the OpenNest checkout plus each + OpenNest.Engine.*/ project here (test subprojects are skipped) and copies each + engine DLL into that folder. .EXAMPLE - ./Engines/Build-Engines.ps1 - ./Engines/Build-Engines.ps1 -Engines Opus55,Terra -Configuration Debug + ./Build-Engines.ps1 + ./Build-Engines.ps1 -Engines MyEngine -Configuration Debug + ./Build-Engines.ps1 -OpenNestRoot D:/src/OpenNest #> param( [string]$Configuration = 'Release', # Engine names without the OpenNest.Engine. prefix; default is all of them. - [string[]]$Engines + [string[]]$Engines, + # OpenNest checkout to build against; defaults to a sibling clone (../OpenNest). + [string]$OpenNestRoot = (Join-Path (Split-Path $PSScriptRoot -Parent) 'OpenNest') ) $ErrorActionPreference = 'Stop' -$repoRoot = Split-Path $PSScriptRoot -Parent +if (-not (Test-Path (Join-Path $OpenNestRoot 'OpenNest.Benchmark/OpenNest.Benchmark.csproj'))) { + throw "OpenNest checkout not found at '$OpenNestRoot'. Clone https://git.thecozycat.net/aj/OpenNest.git next to this repo or pass -OpenNestRoot." +} +$OpenNestRoot = (Resolve-Path $OpenNestRoot).Path -dotnet build (Join-Path $repoRoot 'OpenNest.Benchmark/OpenNest.Benchmark.csproj') -c $Configuration +dotnet build (Join-Path $OpenNestRoot 'OpenNest.Benchmark/OpenNest.Benchmark.csproj') -c $Configuration if ($LASTEXITCODE -ne 0) { throw 'OpenNest.Benchmark build failed.' } -$deployDir = Join-Path $repoRoot "OpenNest.Benchmark/bin/$Configuration/net8.0/Engines" +$deployDir = Join-Path $OpenNestRoot "OpenNest.Benchmark/bin/$Configuration/net8.0/Engines" New-Item -ItemType Directory -Force $deployDir | Out-Null $projects = Get-ChildItem $PSScriptRoot -Directory -Filter 'OpenNest.Engine.*' | @@ -31,7 +38,7 @@ $projects = Get-ChildItem $PSScriptRoot -Directory -Filter 'OpenNest.Engine.*' | foreach ($dir in $projects) { $csproj = Join-Path $dir.FullName "$($dir.Name).csproj" - dotnet build $csproj -c $Configuration + dotnet build $csproj -c $Configuration "-p:OpenNestRoot=$OpenNestRoot/" if ($LASTEXITCODE -ne 0) { throw "$($dir.Name) build failed." } $dll = Join-Path $dir.FullName "bin/$Configuration/net8.0/$($dir.Name).dll" diff --git a/Directory.Build.props b/Directory.Build.props index eb217da..563d247 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,15 +1,21 @@ + $(MSBuildThisFileDirectory)../OpenNest/ + $([MSBuild]::EnsureTrailingSlash('$(OpenNestRoot)')) net8.0 enable enable - + diff --git a/Directory.Build.targets b/Directory.Build.targets index 8c753ea..8fa9b1f 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -7,4 +7,9 @@ + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ab732b3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 AJ Isaacs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/OpenNest.Engine.Astra/OpenNest.Engine.Astra.csproj b/OpenNest.Engine.Astra/OpenNest.Engine.Astra.csproj index 724216d..89b4560 100644 --- a/OpenNest.Engine.Astra/OpenNest.Engine.Astra.csproj +++ b/OpenNest.Engine.Astra/OpenNest.Engine.Astra.csproj @@ -1,6 +1,6 @@ - + diff --git a/OpenNest.Engine.Astra/README.md b/OpenNest.Engine.Astra/README.md index f92a641..4cec3f3 100644 --- a/OpenNest.Engine.Astra/README.md +++ b/OpenNest.Engine.Astra/README.md @@ -84,12 +84,12 @@ is exercised separately through the benchmark's materialized geometry validator ## Build, test and deploy ```bash -dotnet build Engines/OpenNest.Engine.Astra/OpenNest.Engine.Astra.csproj -c Release -dotnet test Engines/OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj -c Release +dotnet build OpenNest.Engine.Astra/OpenNest.Engine.Astra.csproj -c Release +dotnet test OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj -c Release dotnet build OpenNest.Benchmark/OpenNest.Benchmark.csproj -c Release -mkdir -p OpenNest.Benchmark/bin/Release/net8.0/Engines -cp Engines/OpenNest.Engine.Astra/bin/Release/net8.0/OpenNest.Engine.Astra.dll OpenNest.Benchmark/bin/Release/net8.0/Engines/ -dotnet OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll Engines/OpenNest.Engine.Astra/benchmarks/dxf --engines AstraNestingEngine --parallel 1 +mkdir -p ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/Engines +cp OpenNest.Engine.Astra/bin/Release/net8.0/OpenNest.Engine.Astra.dll ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/Engines/ +dotnet ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll OpenNest.Engine.Astra/benchmarks/dxf --engines AstraNestingEngine --parallel 1 ``` The host supplies Core, Engine and their dependencies. Plugin discovery uses the CLR type name diff --git a/OpenNest.Engine.Astra/benchmarks/OpenNest.Engine.Astra.Benchmarks.csproj b/OpenNest.Engine.Astra/benchmarks/OpenNest.Engine.Astra.Benchmarks.csproj index 3f698ce..631df9f 100644 --- a/OpenNest.Engine.Astra/benchmarks/OpenNest.Engine.Astra.Benchmarks.csproj +++ b/OpenNest.Engine.Astra/benchmarks/OpenNest.Engine.Astra.Benchmarks.csproj @@ -2,6 +2,6 @@ Exedisable - + diff --git a/OpenNest.Engine.Astra/benchmarks/README.md b/OpenNest.Engine.Astra/benchmarks/README.md index 8f11656..a36dfaf 100644 --- a/OpenNest.Engine.Astra/benchmarks/README.md +++ b/OpenNest.Engine.Astra/benchmarks/README.md @@ -101,7 +101,7 @@ their rerun is recorded in `results/validator-fixed-dxf.csv`. The isolated reproduction does not invoke any nesting engine: ```bash -dotnet run --project Engines/OpenNest.Engine.Astra/benchmarks -c Release -- --diagnose-ring +dotnet run --project OpenNest.Engine.Astra/benchmarks -c Release -- --diagnose-ring ``` `results/ring-validator-reproducer.txt` retains the original failures; @@ -151,7 +151,7 @@ and tries tiny nearby translations when exact contact is unsafe. Run from the repository root: ```bash -dotnet run --project Engines/OpenNest.Engine.Astra/benchmarks -c Release +dotnet run --project OpenNest.Engine.Astra/benchmarks -c Release ``` Run only the extended generated suite with `-- current generated`; the first positional @@ -161,7 +161,7 @@ and crashes make the driver exit with a nonzero status. Incompleteness is report The standalone driver also accepts a prior plugin DLL and optional case-name filter: ```bash -dotnet run --project Engines/OpenNest.Engine.Astra/benchmarks -c Release -- /path/to/previous/OpenNest.Engine.Astra.dll triangles +dotnet run --project OpenNest.Engine.Astra/benchmarks -c Release -- /path/to/previous/OpenNest.Engine.Astra.dll triangles ``` An isolated assembly load context prevents .NET from silently substituting the currently @@ -172,11 +172,11 @@ budget is a benchmark safeguard and is not an elapsed-time stopping rule inside For real DXFs, build and deploy the plugin as described in the parent README, then: ```bash -dotnet OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll Engines/OpenNest.Engine.Astra/benchmarks/dxf --engines AstraNestingEngine --parallel 1 --csv /tmp/astra-dxf.csv +dotnet ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll OpenNest.Engine.Astra/benchmarks/dxf --engines AstraNestingEngine --parallel 1 --csv /tmp/astra-dxf.csv ``` The CSV files under `results/` retain the measured results. Tests run independently: ```bash -dotnet test Engines/OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj -c Release +dotnet test OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj -c Release ``` diff --git a/OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj b/OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj index ae3dfab..5aae5da 100644 --- a/OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj +++ b/OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/OpenNest.Engine.Opus55/README.md b/OpenNest.Engine.Opus55/README.md index b0f3cdb..9606d40 100644 --- a/OpenNest.Engine.Opus55/README.md +++ b/OpenNest.Engine.Opus55/README.md @@ -61,8 +61,8 @@ Every placement decision (which part, which rotation, where, on which sheet) com ## Build / test ```bash -dotnet build Engines/OpenNest.Engine.Opus55/OpenNest.Engine.Opus55.csproj -c Release -dotnet test Engines/OpenNest.Engine.Opus55/tests/OpenNest.Engine.Opus55.Tests.csproj +dotnet build OpenNest.Engine.Opus55/OpenNest.Engine.Opus55.csproj -c Release +dotnet test OpenNest.Engine.Opus55/tests/OpenNest.Engine.Opus55.Tests.csproj ``` This project is intentionally **outside** `OpenNest.sln`, the same pattern as the @@ -72,12 +72,12 @@ This project is intentionally **outside** `OpenNest.sln`, the same pattern as th ```bash dotnet build OpenNest.Benchmark/OpenNest.Benchmark.csproj -c Release -mkdir -p OpenNest.Benchmark/bin/Release/net8.0/Engines -cp Engines/OpenNest.Engine.Opus55/bin/Release/net8.0/OpenNest.Engine.Opus55.dll OpenNest.Benchmark/bin/Release/net8.0/Engines/ -dotnet OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll +mkdir -p ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/Engines +cp OpenNest.Engine.Opus55/bin/Release/net8.0/OpenNest.Engine.Opus55.dll ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/Engines/ +dotnet ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll ``` -Or build and deploy in one step with `./Engines/Build-Engines.ps1 -Engines Opus55`. +Or build and deploy in one step with `./Build-Engines.ps1 -Engines Opus55`. The engine reports as `Opus55NestingEngine`. diff --git a/OpenNest.Engine.Opus55/tests/OpenNest.Engine.Opus55.Tests.csproj b/OpenNest.Engine.Opus55/tests/OpenNest.Engine.Opus55.Tests.csproj index 74d976a..c496c21 100644 --- a/OpenNest.Engine.Opus55/tests/OpenNest.Engine.Opus55.Tests.csproj +++ b/OpenNest.Engine.Opus55/tests/OpenNest.Engine.Opus55.Tests.csproj @@ -12,6 +12,6 @@ - + diff --git a/OpenNest.Engine.Qwen/README.md b/OpenNest.Engine.Qwen/README.md index 4a76745..1d843ef 100644 --- a/OpenNest.Engine.Qwen/README.md +++ b/OpenNest.Engine.Qwen/README.md @@ -42,7 +42,7 @@ engines to start; it must not be the same algorithm re-derived through indirecti ## Build ```bash -dotnet build Engines/OpenNest.Engine.Qwen/OpenNest.Engine.Qwen.csproj +dotnet build OpenNest.Engine.Qwen/OpenNest.Engine.Qwen.csproj ``` This project is intentionally **outside** `OpenNest.sln` (same pattern as the @@ -55,16 +55,16 @@ as part of the main solution. own build output: ```bash -dotnet build Engines/OpenNest.Engine.Qwen/OpenNest.Engine.Qwen.csproj -c Release +dotnet build OpenNest.Engine.Qwen/OpenNest.Engine.Qwen.csproj -c Release dotnet build OpenNest.Benchmark/OpenNest.Benchmark.csproj -c Release -mkdir -p OpenNest.Benchmark/bin/Release/net8.0/Engines -cp Engines/OpenNest.Engine.Qwen/bin/Release/net8.0/OpenNest.Engine.Qwen.dll OpenNest.Benchmark/bin/Release/net8.0/Engines/ +mkdir -p ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/Engines +cp OpenNest.Engine.Qwen/bin/Release/net8.0/OpenNest.Engine.Qwen.dll ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/Engines/ -dotnet OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll +dotnet ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll ``` -Or build and deploy in one step with `./Engines/Build-Engines.ps1 -Engines Qwen`. +Or build and deploy in one step with `./Build-Engines.ps1 -Engines Qwen`. Your engine will show up in the report under its CLR type name (`QwenNestingEngine`), competing on equal footing against the built-in engines. diff --git a/README.md b/README.md new file mode 100644 index 0000000..35f333d --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# OpenNest Engines + +Independent nesting engine plugins for [OpenNest](https://git.thecozycat.net/aj/OpenNest). +Each engine is a standalone class library that OpenNest discovers at runtime via +`NestingEngineRegistry.LoadPlugins()` — drop the DLL into an `Engines/` folder next to +the OpenNest app or `OpenNest.Benchmark` build output. + +| Engine | Approach | +|--------|----------| +| [Astra](OpenNest.Engine.Astra/) | Contact-based placement | +| [Opus55](OpenNest.Engine.Opus55/) | Frontier-advance no-fit-polygon packing | +| [Qwen](OpenNest.Engine.Qwen/) | Scaffold — not yet implemented | + +## Building + +Engines compile against an OpenNest checkout, expected as a sibling clone: + +``` +src/ + OpenNest/ git clone https://git.thecozycat.net/aj/OpenNest.git + OpenNest-Engines/ this repo +``` + +Use a different location with `/p:OpenNestRoot=` (or the `OpenNestRoot` +environment variable). + +```powershell +./Build-Engines.ps1 # build all engines + deploy to the benchmark +./Build-Engines.ps1 -Engines Opus55 # just one +dotnet test OpenNest.Engine.Opus55/tests/OpenNest.Engine.Opus55.Tests.csproj +``` + +Each engine's README covers its algorithm and benchmark results. + +## Layout + +Each engine lives in `OpenNest.Engine./` with an optional `tests/` subproject. +`Directory.Build.props` supplies the target framework and the `OpenNest.Engine` +reference, so an engine's `.csproj` can be nearly empty.