build: stand alone from the OpenNest repo

Engines are moving out of OpenNest so they can be published on their own
and so OpenNest checkouts handed to a model for an engine-building run no
longer contain the competing engines. History for Engines/ (and the
pre-move root-level engine folders) was carried over with git filter-repo.

Engines now build against a sibling OpenNest clone via $(OpenNestRoot),
overridable with /p:OpenNestRoot, with a clear error when it is missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
aj
2026-09-24 10:01:19 -04:00
co-authored by Claude Opus 5.5
parent fc9e46cad3
commit cfce484952
14 changed files with 335 additions and 39 deletions
+218
View File
@@ -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
+17 -10
View File
@@ -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"
+9 -3
View File
@@ -1,15 +1,21 @@
<Project>
<!--
Shared settings for out-of-solution nesting engine plugins. Each engine lives in
Engines/OpenNest.Engine.<Name>/ with an optional tests/ subproject; both import this.
Shared settings for OpenNest nesting engine plugins. Each engine lives in
OpenNest.Engine.<Name>/ with an optional tests/ subproject; both import this.
AssemblyName and RootNamespace default to the project file name.
Engines build against an OpenNest checkout. By default that is a sibling clone
(../OpenNest next to this repo); override with /p:OpenNestRoot=<path> or an
OpenNestRoot environment variable.
-->
<PropertyGroup>
<OpenNestRoot Condition="'$(OpenNestRoot)' == ''">$(MSBuildThisFileDirectory)../OpenNest/</OpenNestRoot>
<OpenNestRoot>$([MSBuild]::EnsureTrailingSlash('$(OpenNestRoot)'))</OpenNestRoot>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)../OpenNest.Engine/OpenNest.Engine.csproj" />
<ProjectReference Include="$(OpenNestRoot)OpenNest.Engine/OpenNest.Engine.csproj" />
</ItemGroup>
</Project>
+5
View File
@@ -7,4 +7,9 @@
<ItemGroup>
<Compile Remove="tests/**/*.cs" />
</ItemGroup>
<Target Name="CheckOpenNestRoot" BeforeTargets="ResolveProjectReferences;_CheckForInvalidConfigurationAndPlatform">
<Error Condition="!Exists('$(OpenNestRoot)OpenNest.Engine/OpenNest.Engine.csproj')"
Text="OpenNest checkout not found at '$(OpenNestRoot)'. Clone https://git.thecozycat.net/aj/OpenNest.git next to this repo, or pass /p:OpenNestRoot=&lt;path&gt;." />
</Target>
</Project>
+21
View File
@@ -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.
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Compile Remove="tests/**/*.cs;benchmarks/**/*.cs" />
<ProjectReference Include="../../OpenNest.Core/OpenNest.Core.csproj" />
<ProjectReference Include="$(OpenNestRoot)OpenNest.Core/OpenNest.Core.csproj" />
</ItemGroup>
</Project>
+5 -5
View File
@@ -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
@@ -2,6 +2,6 @@
<PropertyGroup><OutputType>Exe</OutputType><Nullable>disable</Nullable></PropertyGroup>
<ItemGroup>
<ProjectReference Include="../OpenNest.Engine.Astra.csproj" />
<Compile Include="../../../OpenNest.Benchmark/NestValidator.cs" Link="NestValidator.cs" />
<Compile Include="$(OpenNestRoot)OpenNest.Benchmark/NestValidator.cs" Link="NestValidator.cs" />
</ItemGroup>
</Project>
+5 -5
View File
@@ -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
```
@@ -10,7 +10,7 @@
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
<Compile Include="../../../OpenNest.Benchmark/NestValidator.cs" Link="NestValidator.cs" />
<Compile Include="$(OpenNestRoot)OpenNest.Benchmark/NestValidator.cs" Link="NestValidator.cs" />
<ProjectReference Include="../OpenNest.Engine.Astra.csproj" />
</ItemGroup>
</Project>
+6 -6
View File
@@ -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 <path-to-.nest-or-manifest-or-folder>
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 <path-to-.nest-or-manifest-or-folder>
```
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`.
@@ -12,6 +12,6 @@
<Using Include="Xunit" />
<ProjectReference Include="../OpenNest.Engine.Opus55.csproj" />
<!-- The benchmark's NestValidator is the arbiter the engine is scored by. -->
<ProjectReference Include="../../../OpenNest.Benchmark/OpenNest.Benchmark.csproj" />
<ProjectReference Include="$(OpenNestRoot)OpenNest.Benchmark/OpenNest.Benchmark.csproj" />
</ItemGroup>
</Project>
+6 -6
View File
@@ -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 <path-to-.nest-or-folder>
dotnet ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll <path-to-.nest-or-folder>
```
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.
+39
View File
@@ -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=<path>` (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.<Name>/` 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.