mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-02 16:01:36 -05:00
The repo mixed variants such as center/centre and color/colour, so a name could be spelled either way and searches missed half the hits. Standardize on the US spelling, matching .NET's own APIs (Color, Center). Wire values owned by other systems keep their spelling: the material ledger's "Cancelled" hold status and GitHub's "cancelled" job status. No behavior change beyond the spelling of a few status messages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
3.3 KiB
C#
72 lines
3.3 KiB
C#
using System.Diagnostics;
|
|
using OpenNest.Benchmark;
|
|
using OpenNest.CNC;
|
|
using OpenNest.Engine.Jobs;
|
|
using OpenNest.Engine.Jobs.Adapters;
|
|
using OpenNest.Geometry;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace OpenNest.Tests.Benchmark;
|
|
|
|
public class NestLayoutCheckTimingTests
|
|
{
|
|
private readonly ITestOutputHelper output;
|
|
|
|
public NestLayoutCheckTimingTests(ITestOutputHelper output) => this.output = output;
|
|
|
|
[Fact]
|
|
public void ManyPartLayoutMatchesFrozenValidatorAndReportsTiming()
|
|
{
|
|
const int count = 100;
|
|
var program = new Program();
|
|
program.MoveTo(1, 0);
|
|
program.ArcTo(-1, 0, 0, 0, RotationType.CCW);
|
|
program.ArcTo(1, 0, 0, 0, RotationType.CCW);
|
|
var part = new NestJobPart("disc", PartGeometrySnapshot.FromProgram(program), count);
|
|
var stock = new NestPlateStock("sheet", new Size(30, 30), partSpacing: 0.01);
|
|
var job = new NestJob(new[] { part }, new[] { stock });
|
|
var builder = new NestJobResultBuilder(job);
|
|
// Dense bounding-box candidates exercise the pair gate; both overlapping and
|
|
// disjoint diagonal neighbors occur. Violations are deliberate and compared.
|
|
builder.AddSheet(stock, Enumerable.Range(0, count)
|
|
.Select(i => (part.Id, 2 + (i % 10) * 1.5, 2 + (i / 10) * 1.5, 0.0)));
|
|
var result = builder.Build(NestJobStopReason.Completed);
|
|
var materialized = NestResultMaterializer.Materialize(job, result);
|
|
var requirements = job.Parts.ToDictionary(p => materialized.DrawingsByPartId[p.Id], p => (p.Id, p.Quantity));
|
|
var runs = materialized.Nest.Plates.Select(p => (p, p.Parts.ToList())).ToList();
|
|
var expected = LegacyNestValidator.Validate(runs, requirements).Violations;
|
|
Assert.NotEmpty(expected);
|
|
Assert.Equal(expected, NestValidator.Validate(runs, requirements).Violations);
|
|
Assert.Equal(expected, NestLayoutCheck.Violations(job, result));
|
|
|
|
var legacyTimes = new double[5];
|
|
var currentTimes = new double[5];
|
|
for (var i = 0; i < legacyTimes.Length; i++)
|
|
{
|
|
// Alternate order after warmup to reduce systematic timing bias.
|
|
if (i % 2 == 0)
|
|
{
|
|
legacyTimes[i] = Measure(() => Assert.Equal(expected, LegacyNestValidator.Validate(runs, requirements).Violations));
|
|
currentTimes[i] = Measure(() => Assert.Equal(expected, NestValidator.Validate(runs, requirements).Violations));
|
|
}
|
|
else
|
|
{
|
|
currentTimes[i] = Measure(() => Assert.Equal(expected, NestValidator.Validate(runs, requirements).Violations));
|
|
legacyTimes[i] = Measure(() => Assert.Equal(expected, LegacyNestValidator.Validate(runs, requirements).Violations));
|
|
}
|
|
}
|
|
Array.Sort(legacyTimes);
|
|
Array.Sort(currentTimes);
|
|
output.WriteLine($"100 discs; {expected.Count} identical ordered violations; "
|
|
+ $"median of 5: frozen={legacyTimes[2]:F3}ms current={currentTimes[2]:F3}ms; "
|
|
+ $"ratio={legacyTimes[2] / currentTimes[2]:F2}x. Includes outline preparation; excludes materialization.");
|
|
}
|
|
|
|
private static double Measure(Action action)
|
|
{
|
|
var watch = Stopwatch.StartNew();
|
|
action();
|
|
return watch.Elapsed.TotalMilliseconds;
|
|
}
|
|
}
|