mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-03 01:12:11 -04:00
fix(packing): apply work-area slack only at plate boundaries
This commit is contained in:
1 parent
a26e663f01
commit
6b6928213d
5 files changed
+159
-7
No files matched your search
@@ -1,5 +1,7 @@
|
||||
using OpenNest.Engine.Fill;
|
||||
using OpenNest.Engine.Jobs;
|
||||
using OpenNest.Engine.Jobs.Placement;
|
||||
using OpenNest.Engine.NestingEngines.Rectangles;
|
||||
using OpenNest.Engine.RectanglePacking;
|
||||
using OpenNest.Engine.Tests.Jobs;
|
||||
using OpenNest.Geometry;
|
||||
@@ -44,6 +46,132 @@ public class AreaPackerTests
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0)]
|
||||
[InlineData(1, 0)]
|
||||
[InlineData(0, 3)]
|
||||
[InlineData(1, 3)]
|
||||
public void PackArea_AllowsBoundaryOverhangAndValidates(double spacing, double edge)
|
||||
{
|
||||
var plate = new Plate(new Size(10 + 2 * edge, 10 + 2 * edge))
|
||||
{
|
||||
PartSpacing = spacing,
|
||||
EdgeSpacing = new Spacing(edge, edge),
|
||||
};
|
||||
var area = plate.WorkArea();
|
||||
var items = new List<NestItem> { Item("panel", 10 + SheetPacker.OverhangAllowance * 0.8, 4, 2) };
|
||||
|
||||
var parts = PlateFillService.PackArea("Default", plate, area, items, null, CancellationToken.None);
|
||||
var again = PlateFillService.PackArea("Default", plate, area, items, null, CancellationToken.None);
|
||||
|
||||
Assert.Equal(2, parts.Count);
|
||||
Assert.Equal(parts.Select(p => (p.Location, p.Rotation)), again.Select(p => (p.Location, p.Rotation)));
|
||||
Assert.All(parts, p =>
|
||||
{
|
||||
Assert.Equal(area.Left, p.BoundingBox.Left, 9);
|
||||
Assert.True(p.BoundingBox.Right > area.Right);
|
||||
Assert.True(p.BoundingBox.Right <= area.Right + NestTolerances.WorkAreaSlack);
|
||||
});
|
||||
Assert.True(parts[1].BoundingBox.Bottom - parts[0].BoundingBox.Top >= spacing - Eps);
|
||||
var requirements = items.ToDictionary(i => i.Drawing, i => (i.Drawing.Name, i.Quantity));
|
||||
Assert.Empty(NestLayoutCheck.Validate(new() { (plate, parts) }, requirements));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
[InlineData(3)]
|
||||
[InlineData(4)]
|
||||
public void PackArea_TopOverhangKeepsSpacingInEveryQuadrant(int quadrant)
|
||||
{
|
||||
var plate = new Plate(new Size(12, 12))
|
||||
{
|
||||
Quadrant = quadrant,
|
||||
EdgeSpacing = new Spacing(1, 1),
|
||||
PartSpacing = 1,
|
||||
};
|
||||
var area = plate.WorkArea();
|
||||
var items = new List<NestItem> { Item("panel", 4, 10 + SheetPacker.OverhangAllowance * 0.8, 2) };
|
||||
var parts = PlateFillService.PackArea("Default", plate, area, items, null, CancellationToken.None);
|
||||
|
||||
Assert.Equal(2, parts.Count);
|
||||
Assert.All(parts, p =>
|
||||
{
|
||||
Assert.Equal(area.Bottom, p.BoundingBox.Bottom, 9);
|
||||
Assert.True(p.BoundingBox.Top > area.Top);
|
||||
Assert.True(p.BoundingBox.Top <= area.Top + NestTolerances.WorkAreaSlack);
|
||||
});
|
||||
var ordered = parts.OrderBy(p => p.BoundingBox.Left).ToList();
|
||||
Assert.True(ordered[1].BoundingBox.Left - ordered[0].BoundingBox.Right >= 1 - Eps);
|
||||
Assert.Empty(NestLayoutCheck.Validate(new() { (plate, parts) },
|
||||
items.ToDictionary(i => i.Drawing, i => (i.Drawing.Name, i.Quantity))));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void PackArea_FullAreaDemandCapIncludesSlack(bool rotated)
|
||||
{
|
||||
var plate = new Plate(new Size(6, 10)) { PartSpacing = 0 };
|
||||
var excess = SheetPacker.OverhangAllowance * 0.8;
|
||||
var items = new List<NestItem> { rotated ? Item("panel", 6 + excess, 10 + excess, 1)
|
||||
: Item("panel", 10 + excess, 6 + excess, 1) };
|
||||
|
||||
var part = Assert.Single(PlateFillService.PackArea("Default", plate, plate.WorkArea(), items, null, CancellationToken.None));
|
||||
|
||||
Assert.True(part.BoundingBox.Right <= 10 + NestTolerances.WorkAreaSlack);
|
||||
Assert.True(part.BoundingBox.Top <= 6 + NestTolerances.WorkAreaSlack);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
public void PackArea_RejectsBeyondBoundaryAllowance(double spacing)
|
||||
{
|
||||
var plate = new Plate(new Size(6, 10)) { PartSpacing = spacing };
|
||||
var items = new List<NestItem> { Item("panel", 10 + NestTolerances.WorkAreaSlack * 1.1, 4, 1) };
|
||||
Assert.Empty(PlateFillService.PackArea("Default", plate, plate.WorkArea(), items, null, CancellationToken.None));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, 0, 5, 6, false)]
|
||||
[InlineData(5, 0, 5, 6, true)]
|
||||
[InlineData(0, 0, 10, 3, false)]
|
||||
[InlineData(0, 3, 10, 3, true)]
|
||||
public void PackArea_OnlyMatchingPositivePlateEdgesAllowSlack(double x, double y, double w, double h, bool fits)
|
||||
{
|
||||
var plate = new Plate(new Size(6, 10)) { PartSpacing = 0.5 };
|
||||
var area = new Box(x, y, w, h);
|
||||
var excess = SheetPacker.OverhangAllowance * 0.8;
|
||||
var items = new List<NestItem> { w == 5 ? Item("panel", w + excess, h - 0.25, 1) : Item("panel", 8, h + excess, 1) };
|
||||
var parts = PlateFillService.PackArea("Default", plate, area, items, null, CancellationToken.None);
|
||||
Assert.Equal(fits ? 1 : 0, parts.Count);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void MaxRects_InternalFreeEdgesStayStrict(bool horizontal)
|
||||
{
|
||||
var sheet = new MaxRectsSheet(10, 10, SheetPacker.OverhangAllowance, SheetPacker.OverhangAllowance);
|
||||
sheet.Place(horizontal ? new Rect(5, 0, 5, 10) : new Rect(0, 5, 10, 5));
|
||||
var excess = SheetPacker.OverhangAllowance * 0.8;
|
||||
Assert.Null(sheet.FindBest(horizontal ? 5 + excess : 10, horizontal ? 10 : 5 + excess, FitRule.BottomLeft));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void MaxRects_BoundaryFreeEdgesAllowSlackAfterPlacement(bool horizontal)
|
||||
{
|
||||
var sheet = new MaxRectsSheet(10, 10, SheetPacker.OverhangAllowance, SheetPacker.OverhangAllowance);
|
||||
sheet.Place(horizontal ? new Rect(0, 0, 5, 10) : new Rect(0, 0, 10, 5));
|
||||
var excess = SheetPacker.OverhangAllowance * 0.8;
|
||||
var fit = sheet.FindBest(horizontal ? 5 + excess : 10, horizontal ? 10 : 5 + excess, FitRule.BottomLeft);
|
||||
Assert.NotNull(fit);
|
||||
Assert.Equal(horizontal ? new Rect(5, 0, 5, 10) : new Rect(0, 5, 10, 5), fit.Value.Place);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultPackArea_PlacesDemandThatCornerPointPackingMissed()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user