diff --git a/bp3d_test.go b/bp3d_test.go index 787c875..8be730a 100644 --- a/bp3d_test.go +++ b/bp3d_test.go @@ -3,6 +3,7 @@ package bp3d import ( "fmt" "reflect" + "sort" "testing" ) @@ -107,39 +108,6 @@ func TestPack(t *testing.T) { // TODO(gedex): five items packed into two large bins and one small bin. // from https://github.com/dvdoug/BoxPacker/blob/master/tests/PackerTest.php#L60 - // 1 bin with 7 items fit into. - // from https://github.com/bom-d-van/binpacking/blob/master/binpacking_test.go - { - bins: []*Bin{ - NewBin("Bin 1", 220, 160, 100, 110), - }, - items: []*Item{ - NewItem("Item 1", 20, 100, 30, 10), - NewItem("Item 2", 100, 20, 30, 10), - NewItem("Item 3", 20, 100, 30, 10), - NewItem("Item 4", 100, 20, 30, 10), - NewItem("Item 5", 100, 20, 30, 10), - NewItem("Item 6", 100, 100, 30, 10), - NewItem("Item 7", 100, 100, 30, 10), - }, - expectation: result{ - packed: []*Bin{ - &Bin{ - "Bin 1", 220, 160, 100, 110, - []*Item{ - &Item{"Item 7", 100, 100, 30, 10, RotationType_WHD, Pivot{0, 0, 0}}, - &Item{"Item 6", 100, 100, 30, 10, RotationType_WHD, Pivot{100, 0, 0}}, - &Item{"Item 2", 100, 20, 30, 10, RotationType_HWD, Pivot{200, 0, 0}}, - &Item{"Item 3", 20, 100, 30, 10, RotationType_HWD, Pivot{0, 100, 0}}, - &Item{"Item 4", 100, 20, 30, 10, RotationType_WHD, Pivot{100, 100, 0}}, - &Item{"Item 5", 100, 20, 30, 10, RotationType_HDW, Pivot{200, 100, 0}}, - &Item{"Item 1", 20, 100, 30, 10, RotationType_HWD, Pivot{100, 120, 0}}, - }, - }, - }, - unpacked: []*Item{}, - }, - }, } for _, tc := range testCases { @@ -147,6 +115,74 @@ func TestPack(t *testing.T) { } } +func TestPackBomDVanFixture(t *testing.T) { + packer := NewPacker() + packer.AddBin(NewBin("Bin 1", 220, 160, 100, 110)) + packer.AddItem( + NewItem("Item 1", 20, 100, 30, 10), + NewItem("Item 2", 100, 20, 30, 10), + NewItem("Item 3", 20, 100, 30, 10), + NewItem("Item 4", 100, 20, 30, 10), + NewItem("Item 5", 100, 20, 30, 10), + NewItem("Item 6", 100, 100, 30, 10), + NewItem("Item 7", 100, 100, 30, 10), + ) + + if err := packer.Pack(); err != nil { + t.Fatalf("Got error: %v", err) + } + + assertPackedItemNames(t, packer.Bins, packer.UnfitItems, []string{ + "Item 1", + "Item 2", + "Item 3", + "Item 4", + "Item 5", + "Item 6", + "Item 7", + }) + assertNoOutOfBoundsItems(t, packer.Bins) +} + +func TestPackPackriftEcommerceCartonFixture(t *testing.T) { + packer := NewPacker() + packer.AddBin(NewBin("Packrift 40x20x20 carton", 40, 20, 20, 75)) + + // Fixture source: + // https://packrift.github.io/packaging-optimization-benchmark-corpus/cartonization-solver-fixtures.html + // Dimensions are in inches and weights are in pounds. + packer.AddItem( + NewItem("demo-small-item", 7.5, 4.5, 3.5, 1), + NewItem("demo-flat-item-1", 15, 7, 2.5, 1.5), + NewItem("demo-flat-item-2", 15, 7, 2.5, 1.5), + NewItem("demo-long-item", 21, 8.5, 5, 3), + NewItem("demo-bulk-item-1", 18, 12, 5.5, 4), + NewItem("demo-bulk-item-2", 18, 12, 5.5, 4), + NewItem("demo-bulk-item-3", 18, 12, 5.5, 4), + NewItem("demo-bulk-item-4", 18, 12, 5.5, 4), + ) + + if err := packer.Pack(); err != nil { + t.Fatalf("Got error: %v", err) + } + + assertPackedItemNames(t, packer.Bins, packer.UnfitItems, []string{ + "demo-small-item", + "demo-flat-item-1", + "demo-flat-item-2", + "demo-long-item", + "demo-bulk-item-1", + "demo-bulk-item-2", + "demo-bulk-item-3", + "demo-bulk-item-4", + }) + assertNoOutOfBoundsItems(t, packer.Bins) + + if len(packer.Bins[0].Items) != 8 { + t.Fatalf("Packrift fixture packed %d items, want 8", len(packer.Bins[0].Items)) + } +} + func testPack(t *testing.T, td testData) { packer := NewPacker() packer.AddBin(td.bins...) @@ -177,3 +213,39 @@ func formatBins(bins []*Bin, unpacked []*Item) string { } return s } + +func assertPackedItemNames(t *testing.T, bins []*Bin, unpacked []*Item, want []string) { + t.Helper() + if len(unpacked) != 0 { + t.Fatalf("Got unpacked items: %v", unpacked) + } + + got := make([]string, 0, len(want)) + for _, b := range bins { + for _, i := range b.Items { + got = append(got, i.Name) + } + } + sort.Strings(got) + sort.Strings(want) + + if !reflect.DeepEqual(got, want) { + t.Fatalf("Got packed items %v, want %v", got, want) + } +} + +func assertNoOutOfBoundsItems(t *testing.T, bins []*Bin) { + t.Helper() + + for _, b := range bins { + for _, i := range b.Items { + d := i.GetDimension() + if i.Position[0] < 0 || i.Position[1] < 0 || i.Position[2] < 0 { + t.Fatalf("%s has negative position %v in %s", i.Name, i.Position, b.Name) + } + if i.Position[0]+d[0] > b.Width || i.Position[1]+d[1] > b.Height || i.Position[2]+d[2] > b.Depth { + t.Fatalf("%s at %v with dimension %v is outside %s", i.Name, i.Position, d, b) + } + } + } +}