Files
OpenNest/OpenNest.Engine.Tests/Jobs/NestLayoutCheckPreparedTests.cs
T
ajandClaude Fable 5.1 d317a90f1e perf(engine): cut Auto Nest time on many-part jobs
A 67-drawing, 118-part job took about six minutes through the desktop
Auto Nest path and was cancelled before any layout appeared. Almost all
of it was the cutout pre-pass, the rest the Irregular engine.

Cutout pre-pass (317 s -> 8 s on that job, same bundles and poses):
- The lattice and NFP searches prepare the frame's and the occupants'
  validation outlines once per search (NestLayoutCheck.Prepare) instead
  of re-outlining and re-triangulating the frame at every candidate, and
  test candidates against the small occupants before the frame. Answers
  match the pairwise check exactly, including its leftmost-inflated rule.
- A hole's proposal is replayed for the same frame, hole and offered
  inserts: automatic-rotation inserts ask the same question at every
  frame angle.

Irregular engine (40 s -> 15 s, benchmark 42 s -> 19 s, same layouts):
- Concave no-fit polygons are built as the union of pairwise convex sums
  of each footprint's convex pieces (ear clipping plus Hertel-Mehlhorn
  merging on the Clipper lattice) instead of n*m edge-pair quads; a
  footprint that cannot be decomposed keeps the old sweep.
- Pair and block catalogs share one private drawing per part geometry,
  owned by the engine instance, so each part's best-fit search runs once
  per instance and across a pipeline's trial and ordinary solves.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-18 21:13:15 -04:00

68 lines
3.2 KiB
C#

using OpenNest.CNC;
using OpenNest.Engine.Jobs;
using TestShapes = OpenNest.Engine.Tests.NestingEngines.Shapes;
namespace OpenNest.Engine.Tests.Jobs;
/// <summary>A prepared placement must answer exactly as the pairwise check, whichever side
/// is prepared, so the cutout searches can prepare the frame and the occupants once.</summary>
public class NestLayoutCheckPreparedTests
{
[Theory]
[InlineData(0.0)]
[InlineData(0.25)]
public void PreparedFrameMatchesPairwiseCheckAcrossInsertPoses(double spacing)
{
var program = TestShapes.Rectangle(20, 20);
program.MoveTo(5, 5);
program.LineTo(5, 15);
program.LineTo(15, 15);
program.LineTo(15, 5);
program.LineTo(5, 5);
var frame = JobPartGeometry.Read(PartGeometrySnapshot.FromProgram(program));
var insert = JobPartGeometry.Read(PartGeometrySnapshot.FromProgram(TestShapes.Rectangle(4, 3)));
var framePose = new NestJobPlacement("frame", 0, 0, 0, 0);
var prepared = NestLayoutCheck.Prepare(frame, framePose, spacing);
var agreements = 0;
var clear = 0;
foreach (var rotation in new[] { 0.0, System.Math.PI / 2, 0.3 })
for (var x = -6.0; x <= 22; x += 1.37)
for (var y = -6.0; y <= 22; y += 1.13)
{
var pose = new NestJobPlacement("insert", 0, x, y, rotation);
var expected = NestLayoutCheck.Clears(frame, framePose, insert, pose, spacing);
Assert.Equal(expected, NestLayoutCheck.Clears(prepared, insert, pose));
agreements++;
if (expected)
clear++;
}
Assert.True(clear > 0 && clear < agreements, "the sweep should mix clear and blocked poses");
}
[Fact]
public void PreparedSideMayBeLeftOrRightOfTheOtherPart()
{
// The pairwise rule inflates whichever raw outline is leftmost; preparing the part on
// either side must reproduce that, including exact ties on the left edge.
var disc = new Program();
disc.MoveTo(2, 0);
disc.Codes.Add(new ArcMove(2, 0, 0, 0, RotationType.CW));
var round = JobPartGeometry.Read(PartGeometrySnapshot.FromProgram(disc));
var box = JobPartGeometry.Read(PartGeometrySnapshot.FromProgram(TestShapes.Rectangle(4, 3)));
var preparedRound = NestLayoutCheck.Prepare(round, new NestJobPlacement("disc", 0, 0, 0, 0), 0.25);
var preparedBox = NestLayoutCheck.Prepare(box, new NestJobPlacement("box", 0, 0, 0, 0), 0.25);
foreach (var x in new[] { -8.0, -4.3, -2.0, -1.99, 0.0, 2.0, 2.24, 2.26, 2.3, 4.0, 9.0 })
foreach (var y in new[] { -5.0, -1.0, 0.0, 1.5, 3.3, 6.0 })
{
var boxPose = new NestJobPlacement("box", 0, x, y, 0);
Assert.Equal(NestLayoutCheck.Clears(round, new("disc", 0, 0, 0, 0), box, boxPose, 0.25),
NestLayoutCheck.Clears(preparedRound, box, boxPose));
var discPose = new NestJobPlacement("disc", 0, x, y, 0);
Assert.Equal(NestLayoutCheck.Clears(box, new("box", 0, 0, 0, 0), round, discPose, 0.25),
NestLayoutCheck.Clears(preparedBox, round, discPose));
}
}
}