UNesting 0.19.0
See the version list below for details.
dotnet add package UNesting --version 0.19.0
NuGet\Install-Package UNesting -Version 0.19.0
<PackageReference Include="UNesting" Version="0.19.0" />
<PackageVersion Include="UNesting" Version="0.19.0" />
<PackageReference Include="UNesting" />
paket add UNesting --version 0.19.0
#r "nuget: UNesting, 0.19.0"
#:package UNesting@0.19.0
#addin nuget:?package=UNesting&version=0.19.0
#tool nuget:?package=UNesting&version=0.19.0
UNesting
.NET bindings for the U-Nesting spatial optimization engine.
Features
- 2D Nesting: Optimize polygon placement on sheets
- 3D Bin Packing: Optimize box placement in containers
- Progress Reporting: Real-time progress callbacks with cancellation support
- Async/Await: Full async support with
IProgress<T>andCancellationToken - Cross-Platform: Windows, Linux, and macOS support
Installation
dotnet add package UNesting
Quick Start
2D Nesting
using UNesting;
using UNesting.Models;
// Create a nester
using var nester = new Nester2D();
// Define geometries
var request = new NestingRequest
{
Geometries =
[
Geometry2D.Rectangle("part1", 100, 50, quantity: 5),
new Geometry2D
{
Id = "triangle",
Polygon = [[0.0, 0.0], [80.0, 0.0], [40.0, 60.0]],
Quantity = 3,
Rotations = [0, 90, 180, 270],
},
],
Boundary = Boundary2D.Rectangle(500, 300),
Config = new Config2D
{
Strategy = Strategy2D.BottomLeftFill,
Spacing = 2.0,
},
};
// Solve
var result = nester.Solve(request);
Console.WriteLine($"Utilization: {result.Utilization:P1}");
Console.WriteLine($"Sheets used: {result.SheetsUsed}");
if (!result.AllPlaced)
Console.WriteLine($"{result.UnplacedCount} copies did not fit: {string.Join(", ", result.Unplaced)}");
foreach (var placement in result.Placements)
{
Console.WriteLine($" {placement.Id}#{placement.Instance}: ({placement.X}, {placement.Y}) @ {placement.Rotation}°");
}
A sheet of any shape is a Boundary2D { Polygon = [[x, y], ...] } instead of a rectangle.
3D Bin Packing
using UNesting;
using UNesting.Models;
using var packer = new Packer3D();
var request = new PackingRequest
{
Geometries =
[
Geometry3D.Box("small", width: 20, depth: 20, height: 20, quantity: 10),
Geometry3D.Box("medium", width: 40, depth: 30, height: 25, quantity: 5),
new Geometry3D
{
Id = "fridge", Width = 60, Depth = 40, Height = 90, Quantity = 3,
Mass = 45, Orientation = OrientationConstraint.Upright,
},
],
Boundary = new Boundary3D
{
Width = 200, Depth = 150, Height = 100,
MaxMass = 500,
Gravity = true,
Stability = true,
},
Config = new Config3D { Strategy = Strategy3D.ExtremePoint },
};
var result = packer.Solve(request);
Console.WriteLine($"Utilization: {result.Utilization:P1}");
Console.WriteLine($"Bins used: {result.BinsUsed}");
foreach (var placement in result.Placements)
{
Console.WriteLine($" {placement.Id}#{placement.Instance}: ({placement.X}, {placement.Y}, {placement.Z}) turned {placement.Orientation}");
}
Sizes are width (x), depth (y) and height (z, the vertical) — the order the engine reads
dimensions in. Orientation limits how a box may be turned: Any (the default, six
ways), Upright (height stays vertical) or Fixed. A placement's Orientation says how
it was turned: which of the box's axes (x width, y depth, z height) lies along the
container's x, y and z — Yxz is a quarter turn about the vertical.
Progress Reporting
using var nester = new Nester2D();
// Using events
nester.ProgressChanged += (sender, e) =>
{
Console.WriteLine($"Progress: {e.Progress.Iteration}/{e.Progress.TotalIterations}");
// Cancel if needed
if (e.Progress.Utilization > 0.95)
e.Cancel = true;
};
var result = nester.SolveWithProgress(request);
Async with IProgress
var progress = new Progress<ProgressInfo>(p =>
{
Console.WriteLine($"Iteration {p.Iteration}: {p.Utilization:P1}");
});
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
try
{
var result = await nester.SolveAsync(request, progress, cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Solving was cancelled");
}
Strategies
2D Nesting (Strategy2D)
| Strategy | Description |
|---|---|
BottomLeftFill |
Bottom-left fill (fast, good for simple shapes) — the default |
NfpGuided |
No-fit-polygon guided placement (high quality) |
GeneticAlgorithm |
Genetic algorithm (optimization) |
Brkga |
Biased random-key GA (advanced optimization) |
SimulatedAnnealing |
Simulated annealing (global optimization) |
Gdrr |
Goal-driven ruin and recreate |
Alns |
Adaptive large neighbourhood search |
3D Bin Packing (Strategy3D)
| Strategy | Description |
|---|---|
BottomLeftFill |
Layer-by-layer bottom-left fill (fast) — the default |
ExtremePoint |
Extreme-point heuristic (high quality) |
GeneticAlgorithm |
Genetic algorithm |
Brkga |
Biased random-key GA |
SimulatedAnnealing |
Simulated annealing |
A setting left null in Config2D / Config3D is not sent, so the engine's default
applies; 0 is a value (MaxGenerations = 0 evaluates the first population only,
TimeLimitMs = 0 runs without a time limit).
Refusals
A request the engine cannot honour raises NestingException. Message is readable
text; Reason is a stable code to branch on and Details holds the values behind it:
try
{
nester.Solve(request);
}
catch (NestingException ex) when (ex.Reason == "duplicate_id")
{
var id = ex.Details!.Value.GetProperty("id").GetString();
var again = ex.Details!.Value.GetProperty("index").GetInt32();
Console.WriteLine($"geometry '{id}' is given again at position {again}");
}
The codes and their fields are listed in the npm package README's Refusals section;
the .NET package reports the same ones. parameter is always the path of the value in
the request -- config.spacing, boundary.width, geometries[0].quantity. A NaN or infinite number in a request cannot be
written as JSON, so the client refuses it before calling the engine, with the reason
value_not_finite: Details has parameter, the path to the value (or to the array
holding it, with index its position there), and id when it belongs to a geometry —
{ "parameter": "geometries[1].polygon[2]", "index": 1, "id": "b" } for a vertex's y.
A progress handler that throws stops the solve; SolveWithProgress rethrows its exception.
Trimming and NativeAOT
The solvers serialize requests and read results and progress reports through
source-generated serialization, with no reflection, and the package is marked
IsAotCompatible. They run unchanged in trimmed and NativeAOT applications.
Results (NestingResult, PackingResult, their placements and ProgressInfo) are
records read strictly: a key the engine did not send fails the call instead of reading as
zero.
Platforms
The package carries the native library for win-x64, linux-x64 and linux-arm64
(glibc 2.39 or later), osx-x64 and osx-arm64; no separate install is needed.
API Reference
Nester2D
public class Nester2D : IDisposable
{
// Synchronous solving
NestingResult Solve(NestingRequest request);
// Solving with progress callback
NestingResult SolveWithProgress(NestingRequest request, CancellationToken ct = default);
// Async solving
Task<NestingResult> SolveAsync(NestingRequest request, IProgress<ProgressInfo>? progress = null, CancellationToken ct = default);
// Progress event
event EventHandler<ProgressEventArgs>? ProgressChanged;
}
Packer3D
public class Packer3D : IDisposable
{
// Synchronous solving
PackingResult Solve(PackingRequest request);
// Solving with progress callback
PackingResult SolveWithProgress(PackingRequest request, CancellationToken ct = default);
// Async solving
Task<PackingResult> SolveAsync(PackingRequest request, IProgress<ProgressInfo>? progress = null, CancellationToken ct = default);
// Progress event
event EventHandler<ProgressEventArgs>? ProgressChanged;
}
License
MIT License - see LICENSE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.21.1 | 0 | 10/15/2026 |
| 0.21.0 | 0 | 10/14/2026 |
| 0.20.0 | 24 | 10/14/2026 |
| 0.19.0 | 64 | 10/13/2026 |
| 0.18.0 | 63 | 10/13/2026 |
| 0.17.0 | 94 | 10/7/2026 |
| 0.16.0 | 103 | 10/3/2026 |
| 0.15.0 | 95 | 10/3/2026 |
| 0.14.0 | 90 | 9/30/2026 |
| 0.13.0 | 84 | 9/30/2026 |
| 0.12.0 | 124 | 9/16/2026 |
| 0.11.0 | 110 | 9/15/2026 |
| 0.10.0 | 125 | 9/7/2026 |
| 0.9.0 | 156 | 8/23/2026 |
| 0.8.0 | 130 | 8/23/2026 |
| 0.7.2 | 297 | 7/14/2026 |
| 0.7.1 | 143 | 7/14/2026 |
| 0.7.0 | 135 | 7/14/2026 |
| 0.6.0 | 144 | 7/7/2026 |
| 0.5.2 | 127 | 7/5/2026 |