mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-30 04:21:06 -04:00
feat(desktop): Auto Nest remaining quantities without the dialog
AutoNestJob builds the desktop dialog's default job, each drawing's remaining quantity (cut-offs excluded) on stock like a template plate, and commits a NestPipeline result only when it is valid, keepable and places something, onto the nest's empty plates first. RefusalReason names why nothing was committed instead of NestRenest's release-flow note.
This commit is contained in:
1 parent
2db8f5f841
commit
cce2585d79
2 files changed
+175
No files matched your search
@@ -0,0 +1,93 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Engine.Jobs;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Desktop.Tests;
|
||||
|
||||
public class AutoNestJobTests
|
||||
{
|
||||
private static Drawing Square(string name, double side, int required)
|
||||
{
|
||||
var program = new Program();
|
||||
program.Codes.Add(new RapidMove(new Vector(0, 0)));
|
||||
program.Codes.Add(new LinearMove(new Vector(side, 0)));
|
||||
program.Codes.Add(new LinearMove(new Vector(side, side)));
|
||||
program.Codes.Add(new LinearMove(new Vector(0, side)));
|
||||
program.Codes.Add(new LinearMove(new Vector(0, 0)));
|
||||
var drawing = new Drawing(name, program);
|
||||
drawing.Quantity.Required = required;
|
||||
return drawing;
|
||||
}
|
||||
|
||||
private static Nest NestWith(params Drawing[] drawings)
|
||||
{
|
||||
var nest = new Nest { Name = "job" };
|
||||
foreach (var drawing in drawings)
|
||||
nest.Drawings.Add(drawing);
|
||||
nest.Plates.Add(new Plate(new Size(48, 96)) { Quantity = 1 });
|
||||
return nest;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForRemaining_AsksForWhatIsNotPlacedYet_OnTheTemplateStock()
|
||||
{
|
||||
var square = Square("square", 10, 5);
|
||||
var nest = NestWith(square);
|
||||
nest.Plates[0].Parts.Add(new Part(square));
|
||||
|
||||
var request = AutoNestJob.ForRemaining(nest, nest.Plates[0], "Default");
|
||||
|
||||
var item = Assert.Single(request.Items);
|
||||
Assert.Same(square, item.Drawing);
|
||||
Assert.Equal(4, item.Quantity);
|
||||
Assert.Equal("Default", request.EngineName);
|
||||
Assert.Equal(nest.Plates[0].Size, Assert.Single(request.Stock).Size);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForRemaining_SkipsCutOffsAndPlacedDrawings_AndIsNullWhenNothingRemains()
|
||||
{
|
||||
var placed = Square("placed", 10, 1);
|
||||
var cutOff = Square("cut-off", 1, 3);
|
||||
cutOff.IsCutOff = true;
|
||||
var nest = NestWith(placed, cutOff);
|
||||
nest.Plates[0].Parts.Add(new Part(placed));
|
||||
|
||||
Assert.Null(AutoNestJob.ForRemaining(nest, nest.Plates[0], "Default"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidResult_IsCommittedOntoTheEmptyPlate()
|
||||
{
|
||||
var square = Square("square", 10, 6);
|
||||
var nest = NestWith(square);
|
||||
var request = AutoNestJob.ForRemaining(nest, nest.Plates[0], "Default");
|
||||
|
||||
var result = NestPipeline.Run(request);
|
||||
var kept = AutoNestJob.Keep(nest, result);
|
||||
|
||||
Assert.True(kept.Nested, kept.Note);
|
||||
Assert.Null(AutoNestJob.RefusalReason(result));
|
||||
var plate = Assert.Single(nest.Plates);
|
||||
Assert.Equal(6, plate.Parts.Count);
|
||||
Assert.Equal(0, square.Quantity.Remaining);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResultPlacingNothing_LeavesTheNestUnchanged()
|
||||
{
|
||||
var tooBig = Square("too big", 200, 1);
|
||||
var nest = NestWith(tooBig);
|
||||
var request = AutoNestJob.ForRemaining(nest, nest.Plates[0], "Default");
|
||||
|
||||
var result = NestPipeline.Run(request);
|
||||
var kept = AutoNestJob.Keep(nest, result);
|
||||
|
||||
Assert.False(kept.Nested);
|
||||
var reason = AutoNestJob.RefusalReason(result);
|
||||
Assert.StartsWith("Auto Nest placed nothing; nothing was changed. Not placed: ", reason);
|
||||
Assert.Contains("too big", reason);
|
||||
Assert.Empty(Assert.Single(nest.Plates).Parts);
|
||||
Assert.Equal(1, tooBig.Quantity.Remaining);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenNest.Engine;
|
||||
using OpenNest.Engine.Jobs;
|
||||
|
||||
namespace OpenNest.Desktop
|
||||
{
|
||||
/// <summary>
|
||||
/// Auto Nest with the desktop dialog's defaults and no dialog: every drawing's remaining
|
||||
/// quantity, with its priority and rotation constraints, on sheets like a template plate,
|
||||
/// through <see cref="NestPipeline"/>. Only a valid, keepable result that places something
|
||||
/// is committed, onto the nest's empty plates first and then new plates from its defaults.
|
||||
/// </summary>
|
||||
public static class AutoNestJob
|
||||
{
|
||||
/// <summary>
|
||||
/// The job for <paramref name="nest"/>'s remaining quantities on stock like
|
||||
/// <paramref name="template"/>; null when nothing remains. The nest is not changed, so
|
||||
/// the caller can solve it off its thread.
|
||||
/// </summary>
|
||||
public static NestPipelineRequest ForRemaining(Nest nest, Plate template, string engineName)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(nest);
|
||||
ArgumentNullException.ThrowIfNull(template);
|
||||
var items = nest.Drawings
|
||||
.Where(d => !d.IsCutOff && d.Quantity.Remaining > 0)
|
||||
.Select(d => new NestItem
|
||||
{
|
||||
Drawing = d,
|
||||
Quantity = d.Quantity.Remaining,
|
||||
Priority = d.Priority,
|
||||
RotationStart = d.Constraints.StartAngle,
|
||||
RotationEnd = d.Constraints.EndAngle,
|
||||
StepAngle = d.Constraints.StepAngle,
|
||||
})
|
||||
.ToList();
|
||||
if (items.Count == 0)
|
||||
return null;
|
||||
|
||||
return new NestPipelineRequest(engineName, items,
|
||||
NestStockBuilder.FromTemplate(template, null),
|
||||
new NestJobOptions(maxPlates: NestRenest.MaxPlates, salvageRate: 0,
|
||||
minimumSalvageDimension: nest.MinimumRemnantDimension));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits <paramref name="result"/> to <paramref name="nest"/> on the nest's own thread
|
||||
/// when it is valid, keepable and places something; otherwise leaves the nest unchanged
|
||||
/// and says why in the note.
|
||||
/// </summary>
|
||||
public static NestRenestResult Keep(Nest nest, NestPipelineResult result)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(nest);
|
||||
return NestRenest.Keep(result,
|
||||
valid => NestPipelineCommit.ApplyToPlates(valid, () => EmptyOrNewPlate(nest)));
|
||||
}
|
||||
|
||||
private static Plate EmptyOrNewPlate(Nest nest) =>
|
||||
nest.Plates.FirstOrDefault(p => p.Parts.Count == 0) ?? nest.CreatePlate();
|
||||
|
||||
/// <summary>
|
||||
/// Why <see cref="Keep"/> would commit nothing for <paramref name="result"/>, naming the
|
||||
/// first problem; null when it would commit.
|
||||
/// </summary>
|
||||
public static string RefusalReason(NestPipelineResult result)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(result);
|
||||
if (!result.IsValid)
|
||||
return $"Auto Nest found {result.Violations.Count} layout problem"
|
||||
+ $"{(result.Violations.Count == 1 ? "" : "s")} (first: {result.Violations[0]}); nothing was changed.";
|
||||
if (!result.CanKeep)
|
||||
return "Auto Nest's layout cannot be kept faithfully; nothing was changed.";
|
||||
if (result.Plates.All(p => p.Parts.Count == 0))
|
||||
return "Auto Nest placed nothing; nothing was changed."
|
||||
+ (result.Unplaced.Count > 0
|
||||
? " Not placed: " + string.Join("; ", result.Unplaced.Select(u => u.Describe())) + "."
|
||||
: "");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user