Skip to content

Commit

Permalink
Backport changes from 3.x
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdoug committed Dec 21, 2019
1 parent f03c49a commit b219234
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ env:
- TEST_SUITE=functional
- TEST_SUITE=efficiency
php:
- 7.4snapshot
- 7.4
- 7.3
- 7.2
- 7.1
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"description": "An implementation of the 3D (actually 4D) bin packing/knapsack problem (aka creating parcels by putting items into boxes)",
"keywords": ["packing","binpacking","bin packing","knapsack","box","boxpacking","parcel","parcelpacking","shipping","packaging","boxes", "container"],
"homepage": "http://boxpacker.io/",
"type": "library",
"authors": [
{
"name": "Doug Wright",
Expand Down
69 changes: 51 additions & 18 deletions src/OrientatedItemFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ class OrientatedItemFactory implements LoggerAwareInterface
*/
protected static $emptyBoxCache = [];

/**
* @var int[]
*/
protected static $lookaheadCache = [];


public function __construct(Box $box)
{
$this->box = $box;
Expand Down Expand Up @@ -336,27 +342,54 @@ protected function calculateAdditionalItemsPackedWithThisOrientation(
$currentRowLength = max($prevItem->getLength(), $currentRowLengthBeforePacking);

$itemsToPack = $nextItems->topN(8); // cap lookahead as this gets recursive and slow

$tempBox = new WorkingVolume($originalWidthLeft - $prevItem->getWidth(), $currentRowLength, $depthLeft, PHP_INT_MAX);
$tempPacker = new VolumePacker($tempBox, clone $itemsToPack);
$tempPacker->setLookAheadMode(true);
$remainingRowPacked = $tempPacker->pack();
/** @var PackedItem $packedItem */
foreach ($remainingRowPacked->getItems() as $packedItem) {
$itemsToPack->remove($packedItem);
$cacheKey = $originalWidthLeft .
'|' .
$originalLengthLeft .
'|' .
$prevItem->getWidth() .
'|' .
$prevItem->getLength() .
'|' .
$currentRowLength .
'|'
. $depthLeft;

/** @var Item $itemToPack */
foreach (clone $itemsToPack as $itemToPack) {
$cacheKey .= '|' .
$itemToPack->getWidth() .
'|' .
$itemToPack->getLength() .
'|' .
$itemToPack->getDepth() .
'|' .
$itemToPack->getWeight();
}

$tempBox = new WorkingVolume($originalWidthLeft, $originalLengthLeft - $currentRowLength, $depthLeft, PHP_INT_MAX);
$tempPacker = new VolumePacker($tempBox, clone $itemsToPack);
$tempPacker->setLookAheadMode(true);
$nextRowsPacked = $tempPacker->pack();
/** @var PackedItem $packedItem */
foreach ($nextRowsPacked->getItems() as $packedItem) {
$itemsToPack->remove($packedItem);
}
if (!isset(static::$lookaheadCache[$cacheKey])) {
$tempBox = new WorkingVolume($originalWidthLeft - $prevItem->getWidth(), $currentRowLength, $depthLeft, PHP_INT_MAX);
$tempPacker = new VolumePacker($tempBox, clone $itemsToPack);
$tempPacker->setLookAheadMode(true);
$remainingRowPacked = $tempPacker->pack();
/** @var PackedItem $packedItem */
foreach ($remainingRowPacked->getItems() as $packedItem) {
$itemsToPack->remove($packedItem);
}

$this->logger->debug('Lookahead with orientation', ['packedCount' => $packedCount, 'orientatedItem' => $prevItem]);
$tempBox = new WorkingVolume($originalWidthLeft, $originalLengthLeft - $currentRowLength, $depthLeft, PHP_INT_MAX);
$tempPacker = new VolumePacker($tempBox, clone $itemsToPack);
$tempPacker->setLookAheadMode(true);
$nextRowsPacked = $tempPacker->pack();
/** @var PackedItem $packedItem */
foreach ($nextRowsPacked->getItems() as $packedItem) {
$itemsToPack->remove($packedItem);
}

$this->logger->debug('Lookahead with orientation', ['packedCount' => $packedCount, 'orientatedItem' => $prevItem]);

static::$lookaheadCache[$cacheKey] = $nextItems->count() - $itemsToPack->count();
}

return $nextItems->count() - $itemsToPack->count();
return static::$lookaheadCache[$cacheKey];
}
}
19 changes: 19 additions & 0 deletions src/Packer.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ public function doVolumePacking()
{
$packedBoxes = new PackedBoxList();

$this->sanityPrecheck();

//Keep going until everything packed
while ($this->items->count()) {
$boxesToEvaluate = clone $this->boxes;
Expand Down Expand Up @@ -257,4 +259,21 @@ public function redistributeWeight(PackedBoxList $originalBoxes)

return $redistributor->redistributeWeight($originalBoxes);
}

private function sanityPrecheck()
{
/** @var Item $item */
foreach (clone $this->items as $item) {
$possibleFits = 0;
/** @var Box $box */
foreach (clone $this->boxes as $box) {
if ($item->getWeight() <= ($box->getMaxWeight() - $box->getEmptyWeight())) {
$possibleFits += count((new OrientatedItemFactory($box))->getPossibleOrientationsInEmptyBox($item));
}
}
if ($possibleFits === 0) {
throw new ItemTooLargeException('Item ' . $item->getDescription() . ' is too large to fit into any box', $item);
}
}
}
}
22 changes: 22 additions & 0 deletions tests/PackerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,26 @@ public function testIssue170()

self::assertCount(2, $packedBoxes);
}


/**
* From issue #182.
*/
public function testIssue182A()
{
$packer = new Packer();
$packer->addBox(new TestBox('Box', 410, 310, 310, 2000, 410, 310, 310, 60000));
$packer->addBox(new TestBox('Box', 410, 310, 260, 2000, 410, 310, 260, 60000));
$packer->addBox(new TestBox('Box', 410, 310, 205, 2000, 410, 310, 205, 60000));
$packer->addBox(new TestBox('Box', 310, 310, 210, 2000, 310, 310, 210, 60000));
$packer->addBox(new TestBox('Box', 310, 210, 210, 2000, 310, 210, 210, 60000));
$packer->addBox(new TestBox('Box', 310, 210, 155, 2000, 310, 210, 155, 60000));
$packer->addBox(new TestBox('Box', 210, 160, 105, 2000, 210, 160, 105, 60000));
$packer->addItem(new TestItem('Item', 150, 100, 100, 1, false), 200);

/** @var PackedBox[] $packedBoxes */
$packedBoxes = iterator_to_array($packer->pack(), false);

self::assertCount(9, $packedBoxes);
}
}

0 comments on commit b219234

Please sign in to comment.