Archived
fix(qwen38flashnext): make layouts deterministic and use host services
The gap-fill pass stopped on a 120 ms stopwatch and QWEN_* environment variables switched strategies, so the same job could nest differently with machine load or environment. Gap fill now stops after eight failed insertion sweeps, and the switches are internal properties with the old defaults. Part reading, work area, rotation angles, scoring and result assembly now use the host APIs; its collision gate is unchanged. Synthetic benchmark (5 jobs, salvage 0.5): all valid, cost 7660.01 -> 7572.05, time 839 -> 553 ms. Not yet calibrated on production-size jobs. Co-Authored-By: Codex <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
using OpenNest.Engine.Testing;
|
||||
using static OpenNest.Engine.Testing.JobBuilder;
|
||||
using static OpenNest.Engine.Testing.Shapes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenNest.Benchmark;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Engine.Jobs;
|
||||
using OpenNest.Engine.Jobs.Adapters;
|
||||
@@ -30,7 +32,7 @@ public class Qwen38FlashNextNestingEngineTests
|
||||
|
||||
var result = new Qwen38FlashNextNestingEngine().Solve(job);
|
||||
|
||||
AssertValid(job, result);
|
||||
LayoutAssert.Valid(job, result);
|
||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||
Assert.Single(result.Plates);
|
||||
Assert.Equal(12, result.Plates[0].Placements.Count);
|
||||
@@ -55,7 +57,7 @@ public class Qwen38FlashNextNestingEngineTests
|
||||
|
||||
var result = new Qwen38FlashNextNestingEngine().Solve(job);
|
||||
|
||||
AssertValid(job, result);
|
||||
LayoutAssert.Valid(job, result);
|
||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||
}
|
||||
|
||||
@@ -76,7 +78,7 @@ public class Qwen38FlashNextNestingEngineTests
|
||||
|
||||
var result = new Qwen38FlashNextNestingEngine().Solve(job);
|
||||
|
||||
AssertValid(job, result);
|
||||
LayoutAssert.Valid(job, result);
|
||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||
}
|
||||
|
||||
@@ -87,7 +89,7 @@ public class Qwen38FlashNextNestingEngineTests
|
||||
|
||||
var result = new Qwen38FlashNextNestingEngine().Solve(job);
|
||||
|
||||
AssertValid(job, result);
|
||||
LayoutAssert.Valid(job, result);
|
||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||
Assert.True(result.Plates.Count > 1);
|
||||
}
|
||||
@@ -102,7 +104,7 @@ public class Qwen38FlashNextNestingEngineTests
|
||||
|
||||
var result = new Qwen38FlashNextNestingEngine().Solve(job);
|
||||
|
||||
AssertValid(job, result);
|
||||
LayoutAssert.Valid(job, result);
|
||||
var huge = Assert.Single(result.Fulfillment, f => f.PartId == "huge");
|
||||
Assert.Equal(1, huge.Unplaced);
|
||||
}
|
||||
@@ -120,7 +122,7 @@ public class Qwen38FlashNextNestingEngineTests
|
||||
|
||||
var result = new Qwen38FlashNextNestingEngine().Solve(job);
|
||||
|
||||
AssertValid(job, result);
|
||||
LayoutAssert.Valid(job, result);
|
||||
Assert.Equal(NestJobStatus.Complete, result.Status);
|
||||
Assert.Equal(2, Assert.Single(result.Plates).Placements.Count);
|
||||
}
|
||||
@@ -138,65 +140,6 @@ public class Qwen38FlashNextNestingEngineTests
|
||||
Assert.Equal(Enumerable.Range(0, result.Plates.Count), result.Plates.Select(p => p.PlateIndex));
|
||||
}
|
||||
|
||||
// ---- helpers -------------------------------------------------------------------------
|
||||
|
||||
private static void AssertValid(NestJob job, NestJobResult result)
|
||||
{
|
||||
var materialized = NestResultMaterializer.Materialize(job, result);
|
||||
var runs = materialized.Nest.Plates.Select(plate => (Plate: plate, Parts: plate.Parts.ToList())).ToList();
|
||||
var requirements = job.Parts.ToDictionary<NestJobPart, Drawing, (string Name, int Quantity)>(
|
||||
p => materialized.DrawingsByPartId[p.Id],
|
||||
p => (p.Id, p.Quantity),
|
||||
ReferenceEqualityComparer.Instance
|
||||
);
|
||||
var validation = NestValidator.Validate(runs, requirements);
|
||||
NestValidator.ValidateAgainstJob(job, result, job.Parts.ToDictionary(p => p.Id, p => p.Id), validation);
|
||||
Assert.True(validation.Valid, string.Join(Environment.NewLine, validation.Violations));
|
||||
|
||||
foreach (var f in result.Fulfillment)
|
||||
Assert.Equal(f.Requested, f.Placed + f.Unplaced);
|
||||
}
|
||||
|
||||
private static NestJob Job(NestJobPart[] parts, NestPlateStock[] stock, NestJobOptions? options = null) =>
|
||||
new(parts, stock, options);
|
||||
|
||||
private static NestJobPart Part(string id, Program program, int quantity, RotationPolicy? rotation = null) =>
|
||||
new(id, PartGeometrySnapshot.FromProgram(program), quantity, 0, rotation);
|
||||
|
||||
/// <param name="width">Y extent.</param>
|
||||
/// <param name="length">X extent.</param>
|
||||
private static NestPlateStock Stock(
|
||||
string id,
|
||||
double width,
|
||||
double length,
|
||||
double spacing = 0,
|
||||
Spacing edge = default,
|
||||
int quadrant = 1,
|
||||
int? quantity = null
|
||||
) => new(id, new Size(width, length), quantity, spacing, edge, quadrant);
|
||||
|
||||
private static Program Polyline(params (double X, double Y)[] points)
|
||||
{
|
||||
var program = new Program();
|
||||
program.Codes.Add(new RapidMove(points[0].X, points[0].Y));
|
||||
foreach (var (x, y) in points.Skip(1))
|
||||
program.Codes.Add(new LinearMove(x, y));
|
||||
program.Codes.Add(new LinearMove(points[0].X, points[0].Y));
|
||||
return program;
|
||||
}
|
||||
|
||||
private static Program Rectangle(double w, double h) => Polyline((0, 0), (w, 0), (w, h), (0, h));
|
||||
|
||||
private static Program Triangle(double w, double h) => Polyline((0, 0), (w, 0), (w * 0.3, h));
|
||||
|
||||
private static Program LShape(double w, double h, double t) => Polyline((0, 0), (w, 0), (w, t), (t, t), (t, h), (0, h));
|
||||
|
||||
private static Program Disc(double r)
|
||||
{
|
||||
var program = new Program();
|
||||
program.Codes.Add(new RapidMove(r, 0));
|
||||
program.Codes.Add(new ArcMove(-r, 0, 0, 0, RotationType.CCW));
|
||||
program.Codes.Add(new ArcMove(r, 0, 0, 0, RotationType.CCW));
|
||||
return program;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Qwen38FlashNextContractTests : EngineContractTests<Qwen38FlashNextNestingEngine> { }
|
||||
|
||||
Reference in New Issue
Block a user