refactor: rename Astra engine to Gpt6Astra
OpenAI reuses model codenames across generations (GPT-6 reused GPT-5.6's Sol and Luna), so a bare codename like "Astra" can't identify which model built the engine. Prefixing the model version keeps engine names unambiguous as more runs are added. The CLR type is now Gpt6AstraNestingEngine, so benchmark reports show the new name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@ using Clipper2Lib;
|
|||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
using M = System.Math;
|
using M = System.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Astra;
|
namespace OpenNest.Engine.Gpt6Astra;
|
||||||
|
|
||||||
/// <summary>Per-solve configuration-space cache, never a shared mutable geometry cache.</summary>
|
/// <summary>Per-solve configuration-space cache, never a shared mutable geometry cache.</summary>
|
||||||
internal sealed class ContactGeometry
|
internal sealed class ContactGeometry
|
||||||
@@ -3,7 +3,7 @@ using OpenNest.Engine.Jobs;
|
|||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
using M = System.Math;
|
using M = System.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Astra;
|
namespace OpenNest.Engine.Gpt6Astra;
|
||||||
|
|
||||||
internal sealed record PackedShape(ShapeVariant Variant, double X, double Y);
|
internal sealed record PackedShape(ShapeVariant Variant, double X, double Y);
|
||||||
internal sealed record SheetTrial(int StockIndex, int[] Counts, List<PackedShape> Shapes, double Area, double Span);
|
internal sealed record SheetTrial(int StockIndex, int[] Counts, List<PackedShape> Shapes, double Area, double Span);
|
||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
using OpenNest.Engine.Jobs;
|
using OpenNest.Engine.Jobs;
|
||||||
using M = System.Math;
|
using M = System.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Astra;
|
namespace OpenNest.Engine.Gpt6Astra;
|
||||||
|
|
||||||
/// <summary>Independent configuration-space contact packing with bounded stock-plan search.</summary>
|
/// <summary>Independent configuration-space contact packing with bounded stock-plan search.</summary>
|
||||||
public sealed class AstraNestingEngine : INestingEngine
|
public sealed class Gpt6AstraNestingEngine : INestingEngine
|
||||||
{
|
{
|
||||||
public NestJobResult Solve(NestJob job, IProgress<NestJobProgress>? progress = null,
|
public NestJobResult Solve(NestJob job, IProgress<NestJobProgress>? progress = null,
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
+1
-1
@@ -5,7 +5,7 @@ using OpenNest.Engine.Jobs.Adapters;
|
|||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
using M = System.Math;
|
using M = System.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Astra;
|
namespace OpenNest.Engine.Gpt6Astra;
|
||||||
|
|
||||||
internal sealed record PreparedPart(NestJobPart Requirement, double Area, ShapeVariant[] Variants);
|
internal sealed record PreparedPart(NestJobPart Requirement, double Area, ShapeVariant[] Variants);
|
||||||
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
# OpenNest.Engine.Astra
|
# OpenNest.Engine.Gpt6Astra
|
||||||
|
|
||||||
An independent, deterministic .NET 8 CNC nesting plugin. Its public parameterless
|
An independent, deterministic .NET 8 CNC nesting plugin. Its public parameterless
|
||||||
`AstraNestingEngine` implements `INestingEngine`. The project remains outside `OpenNest.sln`.
|
`Gpt6AstraNestingEngine` implements `INestingEngine`. The project remains outside `OpenNest.sln`.
|
||||||
|
|
||||||
## Placement algorithm
|
## Placement algorithm
|
||||||
|
|
||||||
Astra searches configuration space: for each stationary/moving orientation pair, a no-fit
|
Gpt6Astra searches configuration space: for each stationary/moving orientation pair, a no-fit
|
||||||
polygon describes the translations that would overlap. Subtracting these regions from the
|
polygon describes the translations that would overlap. Subtracting these regions from the
|
||||||
sheet's usable translation rectangle exposes contact positions where another part can fit.
|
sheet's usable translation rectangle exposes contact positions where another part can fit.
|
||||||
This permits overlapping bounding rectangles, complementary triangle pairs, staggered circles,
|
This permits overlapping bounding rectangles, complementary triangle pairs, staggered circles,
|
||||||
@@ -46,9 +46,9 @@ concave interlocking, and insertion into straight-edged and curved holes.
|
|||||||
and the contract's job-level stop reason. Cancellation throws without returning a partial job.
|
and the contract's job-level stop reason. Cancellation throws without returning a partial job.
|
||||||
|
|
||||||
The engine never invokes another engine, registry, job runner, whole-plate nester or filler.
|
The engine never invokes another engine, registry, job runner, whole-plate nester or filler.
|
||||||
All order, stock, orientation, placement, search and stopping decisions belong to Astra.
|
All order, stock, orientation, placement, search and stopping decisions belong to Gpt6Astra.
|
||||||
Core geometry and Clipper are primitives, not alternative nesters. A shared Core collision fix
|
Core geometry and Clipper are primitives, not alternative nesters. A shared Core collision fix
|
||||||
corrects curved-hole validation; the placement algorithm remains entirely in Astra.
|
corrects curved-hole validation; the placement algorithm remains entirely in Gpt6Astra.
|
||||||
|
|
||||||
## Precision and safety
|
## Precision and safety
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ is exercised separately through the benchmark's materialized geometry validator
|
|||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
- `AstraNestingEngine.cs`: bounded stock-plan search, accounting, progress and result construction.
|
- `Gpt6AstraNestingEngine.cs`: bounded stock-plan search, accounting, progress and result construction.
|
||||||
- `PreparedGeometry.cs`: snapshots, allowed orientations, symmetry reduction and material regions.
|
- `PreparedGeometry.cs`: snapshots, allowed orientations, symmetry reduction and material regions.
|
||||||
- `ContactGeometry.cs`: cached no-fit polygons and rectangle specialization.
|
- `ContactGeometry.cs`: cached no-fit polygons and rectangle specialization.
|
||||||
- `ContactPlacer.cs`: incremental available regions, contact/inside-hole search and collision checks.
|
- `ContactPlacer.cs`: incremental available regions, contact/inside-hole search and collision checks.
|
||||||
@@ -78,22 +78,22 @@ is exercised separately through the benchmark's materialized geometry validator
|
|||||||
- `benchmarks/`: standalone synthetic benchmark driver, reproducible repository-DXF manifests,
|
- `benchmarks/`: standalone synthetic benchmark driver, reproducible repository-DXF manifests,
|
||||||
baseline/current CSV results and comparison notes. It is not compiled into the plugin.
|
baseline/current CSV results and comparison notes. It is not compiled into the plugin.
|
||||||
|
|
||||||
`OpenNest.Engine.Astra.csproj` references Core explicitly and inherits Engine/net8.0 settings from
|
`OpenNest.Engine.Gpt6Astra.csproj` references Core explicitly and inherits Engine/net8.0 settings from
|
||||||
`../Directory.Build.props`. Test and benchmark sources are excluded from the plugin assembly.
|
`../Directory.Build.props`. Test and benchmark sources are excluded from the plugin assembly.
|
||||||
|
|
||||||
## Build, test and deploy
|
## Build, test and deploy
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet build OpenNest.Engine.Astra/OpenNest.Engine.Astra.csproj -c Release
|
dotnet build OpenNest.Engine.Gpt6Astra/OpenNest.Engine.Gpt6Astra.csproj -c Release
|
||||||
dotnet test OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj -c Release
|
dotnet test OpenNest.Engine.Gpt6Astra/tests/OpenNest.Engine.Gpt6Astra.Tests.csproj -c Release
|
||||||
dotnet build OpenNest.Benchmark/OpenNest.Benchmark.csproj -c Release
|
dotnet build OpenNest.Benchmark/OpenNest.Benchmark.csproj -c Release
|
||||||
mkdir -p ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/Engines
|
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/
|
cp OpenNest.Engine.Gpt6Astra/bin/Release/net8.0/OpenNest.Engine.Gpt6Astra.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
|
dotnet ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll OpenNest.Engine.Gpt6Astra/benchmarks/dxf --engines Gpt6AstraNestingEngine --parallel 1
|
||||||
```
|
```
|
||||||
|
|
||||||
The host supplies Core, Engine and their dependencies. Plugin discovery uses the CLR type name
|
The host supplies Core, Engine and their dependencies. Plugin discovery uses the CLR type name
|
||||||
`AstraNestingEngine`; no registry call exists in the plugin.
|
`Gpt6AstraNestingEngine`; no registry call exists in the plugin.
|
||||||
Rebuild the host with this checkout's `OpenNest.Core` as well: replacing only the plugin DLL
|
Rebuild the host with this checkout's `OpenNest.Core` as well: replacing only the plugin DLL
|
||||||
does not update the shared curved-hole collision fix.
|
does not update the shared curved-hole collision fix.
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ orientations are sampled/pruned. Hole search uses anchor positions, not a comple
|
|||||||
polygon solver. Small usable regions inside complex cutouts may be missed. The benchmark report
|
polygon solver. Small usable regions inside complex cutouts may be missed. The benchmark report
|
||||||
includes an isolated host-validator reproducer and its corrected outcomes.
|
includes an isolated host-validator reproducer and its corrected outcomes.
|
||||||
Filling NFP interior voids can exclude unusual interlocking configurations.
|
Filling NFP interior voids can exclude unusual interlocking configurations.
|
||||||
Salvage-credit options and `PlacementStrategy` do not change Astra's objective; `MaxPlates` is
|
Salvage-credit options and `PlacementStrategy` do not change Gpt6Astra's objective; `MaxPlates` is
|
||||||
respected. Stock dimensions, all edge spacings, quadrants, priorities and rotation policies are
|
respected. Stock dimensions, all edge spacings, quadrants, priorities and rotation policies are
|
||||||
honored. No real `.nest` fixtures were available in this workspace.
|
honored. No real `.nest` fixtures were available in this workspace.
|
||||||
|
|
||||||
+1
-1
@@ -6,7 +6,7 @@ using OpenNest.Shapes;
|
|||||||
using CncProgram = OpenNest.CNC.Program;
|
using CncProgram = OpenNest.CNC.Program;
|
||||||
using M = System.Math;
|
using M = System.Math;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Astra.Benchmarks;
|
namespace OpenNest.Engine.Gpt6Astra.Benchmarks;
|
||||||
|
|
||||||
internal static class GeneratedCases
|
internal static class GeneratedCases
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup><OutputType>Exe</OutputType><Nullable>disable</Nullable></PropertyGroup>
|
<PropertyGroup><OutputType>Exe</OutputType><Nullable>disable</Nullable></PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../OpenNest.Engine.Astra.csproj" />
|
<ProjectReference Include="../OpenNest.Engine.Gpt6Astra.csproj" />
|
||||||
<Compile Include="$(OpenNestRoot)OpenNest.Benchmark/NestValidator.cs" Link="NestValidator.cs" />
|
<Compile Include="$(OpenNestRoot)OpenNest.Benchmark/NestValidator.cs" Link="NestValidator.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
+3
-3
@@ -38,8 +38,8 @@ if (args.Contains("--diagnose-ring"))
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var assembly = args.Length > 0 && args[0].EndsWith(".dll")
|
var assembly = args.Length > 0 && args[0].EndsWith(".dll")
|
||||||
? new AssemblyLoadContext("benchmark-plugin", isCollectible: true).LoadFromAssemblyPath(Path.GetFullPath(args[0])) : Assembly.Load("OpenNest.Engine.Astra");
|
? new AssemblyLoadContext("benchmark-plugin", isCollectible: true).LoadFromAssemblyPath(Path.GetFullPath(args[0])) : Assembly.Load("OpenNest.Engine.Gpt6Astra");
|
||||||
var engine = (INestingEngine)Activator.CreateInstance(assembly.GetType("OpenNest.Engine.Astra.AstraNestingEngine")!);
|
var engine = (INestingEngine)Activator.CreateInstance(assembly.GetType("OpenNest.Engine.Gpt6Astra.Gpt6AstraNestingEngine")!);
|
||||||
var cases = new List<(string Name, NestJob Job)>();
|
var cases = new List<(string Name, NestJob Job)>();
|
||||||
var standard = new[] { new NestPlateStock("small", new Size(24, 48), partSpacing: 0.15),
|
var standard = new[] { new NestPlateStock("small", new Size(24, 48), partSpacing: 0.15),
|
||||||
new NestPlateStock("large", new Size(48, 96), partSpacing: 0.15) };
|
new NestPlateStock("large", new Size(48, 96), partSpacing: 0.15) };
|
||||||
@@ -72,7 +72,7 @@ Add("tail", new[] { Part("rect", Rect(6, 4), 17) }, new[] {
|
|||||||
new NestPlateStock("small", new Size(8, 12), partSpacing: 0.1), new NestPlateStock("large", new Size(20, 30), partSpacing: 0.1) });
|
new NestPlateStock("small", new Size(8, 12), partSpacing: 0.1), new NestPlateStock("large", new Size(20, 30), partSpacing: 0.1) });
|
||||||
Add("plate-cap", new[] { Part("r", Rect(5, 5), 10) }, new[] {
|
Add("plate-cap", new[] { Part("r", Rect(5, 5), 10) }, new[] {
|
||||||
new NestPlateStock("small", new Size(10, 10)), new NestPlateStock("large", new Size(20, 20)) }, new NestJobOptions(maxPlates: 1));
|
new NestPlateStock("small", new Size(10, 10)), new NestPlateStock("large", new Size(20, 20)) }, new NestJobOptions(maxPlates: 1));
|
||||||
cases.AddRange(OpenNest.Engine.Astra.Benchmarks.GeneratedCases.Create());
|
cases.AddRange(OpenNest.Engine.Gpt6Astra.Benchmarks.GeneratedCases.Create());
|
||||||
Console.WriteLine("case,valid,placed,requested,sheets,area,milliseconds");
|
Console.WriteLine("case,valid,placed,requested,sheets,area,milliseconds");
|
||||||
foreach (var (name, job) in cases)
|
foreach (var (name, job) in cases)
|
||||||
{
|
{
|
||||||
+13
-13
@@ -1,8 +1,8 @@
|
|||||||
# Astra development benchmark report
|
# Gpt6Astra development benchmark report
|
||||||
|
|
||||||
Measured locally on Linux with .NET SDK 8.0.425, Release builds, 2026-09-23.
|
Measured locally on Linux with .NET SDK 8.0.425, Release builds, 2026-09-23.
|
||||||
The shared-validator fix was verified on 2026-09-24; its results are recorded separately below.
|
The shared-validator fix was verified on 2026-09-24; its results are recorded separately below.
|
||||||
The baseline is Astra's original independent guillotine/bounding-rectangle implementation,
|
The baseline is Gpt6Astra's original independent guillotine/bounding-rectangle implementation,
|
||||||
archived before the contact-search rewrite. These are not measurements against Opus or a
|
archived before the contact-search rewrite. These are not measurements against Opus or a
|
||||||
claim of performance on an unseen competition dataset.
|
claim of performance on an unseen competition dataset.
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ triangulated collision check can report a violation with required spacing 0.175.
|
|||||||
also changes with translations. Checking every candidate against that routine made a small
|
also changes with translations. Checking every candidate against that routine made a small
|
||||||
ring job take approximately 80–90 seconds.
|
ring job take approximately 80–90 seconds.
|
||||||
|
|
||||||
Astra initially reserved curved cutouts as solid during placement, preserving the original
|
Gpt6Astra initially reserved curved cutouts as solid during placement, preserving the original
|
||||||
drawing in output. This workaround finished the ring/insert job in about 50 ms at the baseline
|
drawing in output. This workaround finished the ring/insert job in about 50 ms at the baseline
|
||||||
sheet cost. It has now been removed following a fix in `OpenNest.Core/Geometry/Collision.cs`.
|
sheet cost. It has now been removed following a fix in `OpenNest.Core/Geometry/Collision.cs`.
|
||||||
|
|
||||||
@@ -87,8 +87,8 @@ oracle in the new tests, not a replacement per-pair validator.
|
|||||||
The eight original translated reproductions all pass. Regression coverage also rejects real
|
The eight original translated reproductions all pass. Regression coverage also rejects real
|
||||||
spacing violations, checks both operand orders and windings, exercises all four quadrants,
|
spacing violations, checks both operand orders and windings, exercises all four quadrants,
|
||||||
and compares overlap areas against Clipper on 80 seeded pairs with multiple/concave holes.
|
and compares overlap areas against Clipper on 80 seeded pairs with multiple/concave holes.
|
||||||
The main suite passes 1,096 tests (12 font-fixture skips), Engine passes 170, and Astra passes
|
The main suite passes 1,096 tests (12 font-fixture skips), Engine passes 170, and Gpt6Astra passes
|
||||||
27. Astra's curved-hole tests now require a ring and insert to share stock whose usable area
|
27. Gpt6Astra's curved-hole tests now require a ring and insert to share stock whose usable area
|
||||||
fits only the ring, proving that insertion is enabled.
|
fits only the ring, proving that insertion is enabled.
|
||||||
|
|
||||||
All 34 synthetic/generated jobs remain valid and complete with unchanged sheet-area costs.
|
All 34 synthetic/generated jobs remain valid and complete with unchanged sheet-area costs.
|
||||||
@@ -101,7 +101,7 @@ their rerun is recorded in `results/validator-fixed-dxf.csv`.
|
|||||||
The isolated reproduction does not invoke any nesting engine:
|
The isolated reproduction does not invoke any nesting engine:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet run --project OpenNest.Engine.Astra/benchmarks -c Release -- --diagnose-ring
|
dotnet run --project OpenNest.Engine.Gpt6Astra/benchmarks -c Release -- --diagnose-ring
|
||||||
```
|
```
|
||||||
|
|
||||||
`results/ring-validator-reproducer.txt` retains the original failures;
|
`results/ring-validator-reproducer.txt` retains the original failures;
|
||||||
@@ -132,8 +132,8 @@ single case does not establish general superiority. There is no Opus result avai
|
|||||||
|
|
||||||
## Local geometry optimizations
|
## Local geometry optimizations
|
||||||
|
|
||||||
Apart from the shared Core collision fix described above, these optimizations are in Astra.
|
Apart from the shared Core collision fix described above, these optimizations are in Gpt6Astra.
|
||||||
Astra caches NFPs and incrementally
|
Gpt6Astra caches NFPs and incrementally
|
||||||
subtracts each newly placed part from available translation regions, avoiding repeated unions
|
subtracts each newly placed part from available translation regions, avoiding repeated unions
|
||||||
of all previous obstacles. A four-vertex rectangle configuration-space specialization avoids
|
of all previous obstacles. A four-vertex rectangle configuration-space specialization avoids
|
||||||
round-offset polygons and polygon collision work for exact axis-aligned rectangle contacts.
|
round-offset polygons and polygon collision work for exact axis-aligned rectangle contacts.
|
||||||
@@ -142,7 +142,7 @@ specialization. This is an end-to-end observation, not an isolated component mic
|
|||||||
|
|
||||||
Precision regression tests cover exact clearances and rotated zero-spacing contacts. In the
|
Precision regression tests cover exact clearances and rotated zero-spacing contacts. In the
|
||||||
latter case, the host's four-decimal polygon rounding and triangulated collision test can
|
latter case, the host's four-decimal polygon rounding and triangulated collision test can
|
||||||
reject a contact accepted by Clipper at six decimals. Astra now retains the original rotated
|
reject a contact accepted by Clipper at six decimals. Gpt6Astra now retains the original rotated
|
||||||
frame for that validation, checks zero-clearance contacts with Core's collision primitive,
|
frame for that validation, checks zero-clearance contacts with Core's collision primitive,
|
||||||
and tries tiny nearby translations when exact contact is unsafe.
|
and tries tiny nearby translations when exact contact is unsafe.
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ and tries tiny nearby translations when exact contact is unsafe.
|
|||||||
Run from the repository root:
|
Run from the repository root:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet run --project OpenNest.Engine.Astra/benchmarks -c Release
|
dotnet run --project OpenNest.Engine.Gpt6Astra/benchmarks -c Release
|
||||||
```
|
```
|
||||||
|
|
||||||
Run only the extended generated suite with `-- current generated`; the first positional
|
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:
|
The standalone driver also accepts a prior plugin DLL and optional case-name filter:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet run --project OpenNest.Engine.Astra/benchmarks -c Release -- /path/to/previous/OpenNest.Engine.Astra.dll triangles
|
dotnet run --project OpenNest.Engine.Gpt6Astra/benchmarks -c Release -- /path/to/previous/OpenNest.Engine.Gpt6Astra.dll triangles
|
||||||
```
|
```
|
||||||
|
|
||||||
An isolated assembly load context prevents .NET from silently substituting the currently
|
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:
|
For real DXFs, build and deploy the plugin as described in the parent README, then:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
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
|
dotnet ../OpenNest/OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll OpenNest.Engine.Gpt6Astra/benchmarks/dxf --engines Gpt6AstraNestingEngine --parallel 1 --csv /tmp/astra-dxf.csv
|
||||||
```
|
```
|
||||||
|
|
||||||
The CSV files under `results/` retain the measured results. Tests run independently:
|
The CSV files under `results/` retain the measured results. Tests run independently:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet test OpenNest.Engine.Astra/tests/OpenNest.Engine.Astra.Tests.csproj -c Release
|
dotnet test OpenNest.Engine.Gpt6Astra/tests/OpenNest.Engine.Gpt6Astra.Tests.csproj -c Release
|
||||||
```
|
```
|
||||||
+25
-25
@@ -4,9 +4,9 @@ using OpenNest.Engine.Jobs;
|
|||||||
using OpenNest.Engine.Jobs.Adapters;
|
using OpenNest.Engine.Jobs.Adapters;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Astra.Tests;
|
namespace OpenNest.Engine.Gpt6Astra.Tests;
|
||||||
|
|
||||||
public class AstraNestingEngineTests
|
public class Gpt6AstraNestingEngineTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(1)]
|
[InlineData(1)]
|
||||||
@@ -21,11 +21,11 @@ public class AstraNestingEngineTests
|
|||||||
}, new[] { new NestPlateStock("stock", new Size(15, 20), 10, 0.25,
|
}, new[] { new NestPlateStock("stock", new Size(15, 20), 10, 0.25,
|
||||||
new Spacing(1, 2, 3, 1), quadrant) });
|
new Spacing(1, 2, 3, 1), quadrant) });
|
||||||
var before = job.Parts.Select(p => p.Geometry.Motions.ToArray()).ToArray();
|
var before = job.Parts.Select(p => p.Geometry.Motions.ToArray()).ToArray();
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
for (var i = 0; i < job.Parts.Count; i++) Assert.Equal(before[i], job.Parts[i].Geometry.Motions);
|
for (var i = 0; i < job.Parts.Count; i++) Assert.Equal(before[i], job.Parts[i].Geometry.Motions);
|
||||||
var again = new AstraNestingEngine().Solve(job);
|
var again = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(result.Plates.SelectMany(p => p.Placements), again.Plates.SelectMany(p => p.Placements));
|
Assert.Equal(result.Plates.SelectMany(p => p.Placements), again.Plates.SelectMany(p => p.Placements));
|
||||||
Assert.Equal(result.Plates.Select(p => p.StockId), again.Plates.Select(p => p.StockId));
|
Assert.Equal(result.Plates.Select(p => p.StockId), again.Plates.Select(p => p.StockId));
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ public class AstraNestingEngineTests
|
|||||||
{
|
{
|
||||||
var job = new NestJob(new[] { Rectangle("p", 2, 2, 1) }, new[] {
|
var job = new NestJob(new[] { Rectangle("p", 2, 2, 1) }, new[] {
|
||||||
new NestPlateStock("large", new Size(20, 20)), new NestPlateStock("small", new Size(2, 2)) });
|
new NestPlateStock("large", new Size(20, 20)), new NestPlateStock("small", new Size(2, 2)) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal("small", Assert.Single(result.Plates).StockId);
|
Assert.Equal("small", Assert.Single(result.Plates).StockId);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ public class AstraNestingEngineTests
|
|||||||
{
|
{
|
||||||
var job = new NestJob(new[] { Rectangle("p", 2, 2, 3) },
|
var job = new NestJob(new[] { Rectangle("p", 2, 2, 3) },
|
||||||
new[] { new NestPlateStock("s", new Size(2, 2), quantity) }, new NestJobOptions(maxPlates: limit));
|
new[] { new NestPlateStock("s", new Size(2, 2), quantity) }, new NestJobOptions(maxPlates: limit));
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(reason, result.StopReason);
|
Assert.Equal(reason, result.StopReason);
|
||||||
Assert.Equal(2, Assert.Single(result.Fulfillment).Unplaced);
|
Assert.Equal(2, Assert.Single(result.Fulfillment).Unplaced);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
@@ -57,11 +57,11 @@ public class AstraNestingEngineTests
|
|||||||
public void ImpossibleAndEmptyJobsTerminate()
|
public void ImpossibleAndEmptyJobsTerminate()
|
||||||
{
|
{
|
||||||
var part = Rectangle("p", 10, 10, 1);
|
var part = Rectangle("p", 10, 10, 1);
|
||||||
Assert.Equal(NestJobStopReason.NoPlacementFound, new AstraNestingEngine().Solve(
|
Assert.Equal(NestJobStopReason.NoPlacementFound, new Gpt6AstraNestingEngine().Solve(
|
||||||
new NestJob(new[] { part }, new[] { new NestPlateStock("s", new Size(2, 2)) })).StopReason);
|
new NestJob(new[] { part }, new[] { new NestPlateStock("s", new Size(2, 2)) })).StopReason);
|
||||||
Assert.Equal(NestJobStopReason.StockExhausted, new AstraNestingEngine().Solve(
|
Assert.Equal(NestJobStopReason.StockExhausted, new Gpt6AstraNestingEngine().Solve(
|
||||||
new NestJob(new[] { part }, Array.Empty<NestPlateStock>())).StopReason);
|
new NestJob(new[] { part }, Array.Empty<NestPlateStock>())).StopReason);
|
||||||
Assert.Equal(NestJobStatus.Complete, new AstraNestingEngine().Solve(
|
Assert.Equal(NestJobStatus.Complete, new Gpt6AstraNestingEngine().Solve(
|
||||||
new NestJob(Array.Empty<NestJobPart>(), Array.Empty<NestPlateStock>())).Status);
|
new NestJob(Array.Empty<NestJobPart>(), Array.Empty<NestPlateStock>())).Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ public class AstraNestingEngineTests
|
|||||||
new NestJobPart("circle", PartGeometrySnapshot.FromProgram(circle), 5),
|
new NestJobPart("circle", PartGeometrySnapshot.FromProgram(circle), 5),
|
||||||
new NestJobPart("incremental", PartGeometrySnapshot.FromProgram(incremental), 5)
|
new NestJobPart("incremental", PartGeometrySnapshot.FromProgram(incremental), 5)
|
||||||
}, new[] { new NestPlateStock("s", new Size(20, 20), partSpacing: 0.4) });
|
}, new[] { new NestPlateStock("s", new Size(20, 20), partSpacing: 0.4) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
}
|
}
|
||||||
@@ -93,9 +93,9 @@ public class AstraNestingEngineTests
|
|||||||
new[] { new NestPlateStock("s", new Size(10, 10)) });
|
new[] { new NestPlateStock("s", new Size(10, 10)) });
|
||||||
using var cts = new CancellationTokenSource();
|
using var cts = new CancellationTokenSource();
|
||||||
cts.Cancel();
|
cts.Cancel();
|
||||||
Assert.Throws<OperationCanceledException>(() => new AstraNestingEngine().Solve(job, token: cts.Token));
|
Assert.Throws<OperationCanceledException>(() => new Gpt6AstraNestingEngine().Solve(job, token: cts.Token));
|
||||||
using var during = new CancellationTokenSource();
|
using var during = new CancellationTokenSource();
|
||||||
Assert.Throws<OperationCanceledException>(() => new AstraNestingEngine().Solve(job,
|
Assert.Throws<OperationCanceledException>(() => new Gpt6AstraNestingEngine().Solve(job,
|
||||||
new CallbackProgress(_ => during.Cancel()), during.Token));
|
new CallbackProgress(_ => during.Cancel()), during.Token));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ public class AstraNestingEngineTests
|
|||||||
var high = new NestJobPart("high", low.Geometry, 1, priority: 9);
|
var high = new NestJobPart("high", low.Geometry, 1, priority: 9);
|
||||||
var job = new NestJob(new[] { low, high }, new[] { new NestPlateStock("s", new Size(2, 2), 1) });
|
var job = new NestJob(new[] { low, high }, new[] { new NestPlateStock("s", new Size(2, 2), 1) });
|
||||||
var updates = new List<NestJobProgress>();
|
var updates = new List<NestJobProgress>();
|
||||||
var result = new AstraNestingEngine().Solve(job, new CallbackProgress(updates.Add));
|
var result = new Gpt6AstraNestingEngine().Solve(job, new CallbackProgress(updates.Add));
|
||||||
Assert.Equal("high", Assert.Single(Assert.Single(result.Plates).Placements).PartId);
|
Assert.Equal("high", Assert.Single(Assert.Single(result.Plates).Placements).PartId);
|
||||||
Assert.Equal(NestJobStage.PlateCommitted, updates.Last().Stage);
|
Assert.Equal(NestJobStage.PlateCommitted, updates.Last().Stage);
|
||||||
Assert.Equal(1, updates.Last().CommittedParts);
|
Assert.Equal(1, updates.Last().CommittedParts);
|
||||||
@@ -125,7 +125,7 @@ public class AstraNestingEngineTests
|
|||||||
new NestJobPart("concave", PartGeometrySnapshot.FromProgram(l), 7),
|
new NestJobPart("concave", PartGeometrySnapshot.FromProgram(l), 7),
|
||||||
new NestJobPart("hole", PartGeometrySnapshot.FromProgram(holed), 3)
|
new NestJobPart("hole", PartGeometrySnapshot.FromProgram(holed), 3)
|
||||||
}, new[] { new NestPlateStock("s", new Size(20, 30), partSpacing: 0.2) });
|
}, new[] { new NestPlateStock("s", new Size(20, 30), partSpacing: 0.2) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ public class AstraNestingEngineTests
|
|||||||
new NestPlateStock("small", new Size(15, 20), 1, 0.1),
|
new NestPlateStock("small", new Size(15, 20), 1, 0.1),
|
||||||
new NestPlateStock("large", new Size(25, 30), partSpacing: 0.3)
|
new NestPlateStock("large", new Size(25, 30), partSpacing: 0.3)
|
||||||
});
|
});
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ public class AstraNestingEngineTests
|
|||||||
triangle.LineTo(0, 10); triangle.LineTo(0, 0);
|
triangle.LineTo(0, 10); triangle.LineTo(0, 0);
|
||||||
var job = new NestJob(new[] { new NestJobPart("t", PartGeometrySnapshot.FromProgram(triangle), 2) },
|
var job = new NestJob(new[] { new NestJobPart("t", PartGeometrySnapshot.FromProgram(triangle), 2) },
|
||||||
new[] { new NestPlateStock("s", new Size(10, 10), 1) });
|
new[] { new NestPlateStock("s", new Size(10, 10), 1) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Assert.Single(result.Plates);
|
Assert.Single(result.Plates);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
@@ -170,7 +170,7 @@ public class AstraNestingEngineTests
|
|||||||
frame.LineTo(9, 1); frame.LineTo(1, 1);
|
frame.LineTo(9, 1); frame.LineTo(1, 1);
|
||||||
var job = new NestJob(new[] { new NestJobPart("frame", PartGeometrySnapshot.FromProgram(frame), 1),
|
var job = new NestJob(new[] { new NestJobPart("frame", PartGeometrySnapshot.FromProgram(frame), 1),
|
||||||
Rectangle("insert", 7, 7, 1) }, new[] { new NestPlateStock("s", new Size(10, 10), 1, 0.25) });
|
Rectangle("insert", 7, 7, 1) }, new[] { new NestPlateStock("s", new Size(10, 10), 1, 0.25) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Assert.Single(result.Plates);
|
Assert.Single(result.Plates);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
@@ -182,7 +182,7 @@ public class AstraNestingEngineTests
|
|||||||
var job = new NestJob(new[] { Rectangle("p", 5, 5, 10) }, new[] {
|
var job = new NestJob(new[] { Rectangle("p", 5, 5, 10) }, new[] {
|
||||||
new NestPlateStock("small", new Size(10, 10)), new NestPlateStock("large", new Size(20, 20))
|
new NestPlateStock("small", new Size(10, 10)), new NestPlateStock("large", new Size(20, 20))
|
||||||
}, new NestJobOptions(maxPlates: 1));
|
}, new NestJobOptions(maxPlates: 1));
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Assert.Equal("large", Assert.Single(result.Plates).StockId);
|
Assert.Equal("large", Assert.Single(result.Plates).StockId);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
@@ -193,7 +193,7 @@ public class AstraNestingEngineTests
|
|||||||
{
|
{
|
||||||
var job = new NestJob(new[] { Rectangle("diagonal", 10, 1, 1, RotationPolicy.Automatic) },
|
var job = new NestJob(new[] { Rectangle("diagonal", 10, 1, 1, RotationPolicy.Automatic) },
|
||||||
new[] { new NestPlateStock("s", new Size(8, 8), 1) });
|
new[] { new NestPlateStock("s", new Size(8, 8), 1) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ public class AstraNestingEngineTests
|
|||||||
triangle.LineTo(0, 10); triangle.LineTo(0, 0);
|
triangle.LineTo(0, 10); triangle.LineTo(0, 0);
|
||||||
var job = new NestJob(new[] { new NestJobPart("t", PartGeometrySnapshot.FromProgram(triangle), 20) },
|
var job = new NestJob(new[] { new NestJobPart("t", PartGeometrySnapshot.FromProgram(triangle), 20) },
|
||||||
new[] { new NestPlateStock("s", new Size(24, 48), partSpacing: 0.15) });
|
new[] { new NestPlateStock("s", new Size(24, 48), partSpacing: 0.15) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ public class AstraNestingEngineTests
|
|||||||
var job = new NestJob(new[] { Rectangle("p", 6, 4, 100, RotationPolicy.Automatic) }, new[] {
|
var job = new NestJob(new[] { Rectangle("p", 6, 4, 100, RotationPolicy.Automatic) }, new[] {
|
||||||
new NestPlateStock("small", new Size(8, 12), partSpacing: 0.1),
|
new NestPlateStock("small", new Size(8, 12), partSpacing: 0.1),
|
||||||
new NestPlateStock("large", new Size(20, 30), partSpacing: 0.1) });
|
new NestPlateStock("large", new Size(20, 30), partSpacing: 0.1) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Assert.True(result.Plates.Sum(p => p.Stock.Size.Length * p.Stock.Size.Width) <= 3600);
|
Assert.True(result.Plates.Sum(p => p.Stock.Size.Length * p.Stock.Size.Width) <= 3600);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
@@ -227,7 +227,7 @@ public class AstraNestingEngineTests
|
|||||||
{
|
{
|
||||||
var job = new NestJob(new[] { Rectangle("p", 2, 2, 4) },
|
var job = new NestJob(new[] { Rectangle("p", 2, 2, 4) },
|
||||||
new[] { new NestPlateStock("s", new Size(4.25, 4.25), 1, 0.25) });
|
new[] { new NestPlateStock("s", new Size(4.25, 4.25), 1, 0.25) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ public class AstraNestingEngineTests
|
|||||||
new NestJobPart("t", PartGeometrySnapshot.FromProgram(t), 7),
|
new NestJobPart("t", PartGeometrySnapshot.FromProgram(t), 7),
|
||||||
Rectangle("r", 3, 2, 5, RotationPolicy.Fixed(trial * System.Math.PI / 7))
|
Rectangle("r", 3, 2, 5, RotationPolicy.Fixed(trial * System.Math.PI / 7))
|
||||||
}, new[] { new NestPlateStock("s", new Size(20, 25), partSpacing: spacing) });
|
}, new[] { new NestPlateStock("s", new Size(20, 25), partSpacing: spacing) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
}
|
}
|
||||||
@@ -265,7 +265,7 @@ public class AstraNestingEngineTests
|
|||||||
new NestJobPart("insert", PartGeometrySnapshot.FromProgram(insert.Program), 1)
|
new NestJobPart("insert", PartGeometrySnapshot.FromProgram(insert.Program), 1)
|
||||||
}, new[] { new NestPlateStock("s", new Size(10.8, 10.6), 1, partSpacing: 0.175,
|
}, new[] { new NestPlateStock("s", new Size(10.8, 10.6), 1, partSpacing: 0.175,
|
||||||
edgeSpacing: new Spacing(0.2, 0.3, 0.4, 0.5), quadrant: quadrant) });
|
edgeSpacing: new Spacing(0.2, 0.3, 0.4, 0.5), quadrant: quadrant) });
|
||||||
var result = new AstraNestingEngine().Solve(job);
|
var result = new Gpt6AstraNestingEngine().Solve(job);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Assert.Equal(2, Assert.Single(result.Plates).Placements.Count);
|
Assert.Equal(2, Assert.Single(result.Plates).Placements.Count);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
@@ -281,7 +281,7 @@ public class AstraNestingEngineTests
|
|||||||
new[] { new NestPlateStock("s", new Size(25, 43), 1, 0.25,
|
new[] { new NestPlateStock("s", new Size(25, 43), 1, 0.25,
|
||||||
new Spacing(0.2, 0.3, 0.4, 0.5), 3) });
|
new Spacing(0.2, 0.3, 0.4, 0.5), 3) });
|
||||||
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||||
var result = new AstraNestingEngine().Solve(job, token: cancellation.Token);
|
var result = new Gpt6AstraNestingEngine().Solve(job, token: cancellation.Token);
|
||||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||||
Assert.Single(result.Plates);
|
Assert.Single(result.Plates);
|
||||||
Validate(job, result);
|
Validate(job, result);
|
||||||
+1
-1
@@ -11,6 +11,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Using Include="Xunit" />
|
<Using Include="Xunit" />
|
||||||
<Compile Include="$(OpenNestRoot)OpenNest.Benchmark/NestValidator.cs" Link="NestValidator.cs" />
|
<Compile Include="$(OpenNestRoot)OpenNest.Benchmark/NestValidator.cs" Link="NestValidator.cs" />
|
||||||
<ProjectReference Include="../OpenNest.Engine.Astra.csproj" />
|
<ProjectReference Include="../OpenNest.Engine.Gpt6Astra.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -7,7 +7,7 @@ the OpenNest app or `OpenNest.Benchmark` build output.
|
|||||||
|
|
||||||
| Engine | Approach |
|
| Engine | Approach |
|
||||||
|--------|----------|
|
|--------|----------|
|
||||||
| [Astra](OpenNest.Engine.Astra/) | Contact-based placement |
|
| [Gpt6Astra](OpenNest.Engine.Gpt6Astra/) | Contact-based placement |
|
||||||
| [Opus55](OpenNest.Engine.Opus55/) | Frontier-advance no-fit-polygon packing |
|
| [Opus55](OpenNest.Engine.Opus55/) | Frontier-advance no-fit-polygon packing |
|
||||||
| [Qwen](OpenNest.Engine.Qwen/) | Scaffold — not yet implemented |
|
| [Qwen](OpenNest.Engine.Qwen/) | Scaffold — not yet implemented |
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user