Skip to content

Commit

Permalink
Add method to override weight distribution box threshold
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdoug committed Sep 7, 2017
1 parent f6da7f6 commit 8d94cfc
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 3 deletions.
26 changes: 24 additions & 2 deletions Packer.php
Expand Up @@ -20,7 +20,11 @@ class Packer implements LoggerAwareInterface
{
use LoggerAwareTrait;

const MAX_BOXES_TO_BALANCE_WEIGHT = 12;
/**
* Number of boxes at which balancing weight is deemed not worth it
* @var int
*/
protected $maxBoxesToBalanceWeight = 12;

/**
* List of items to be packed
Expand Down Expand Up @@ -93,6 +97,24 @@ public function setBoxes(BoxList $boxList)
$this->boxes = clone $boxList;
}

/**
* Number of boxes at which balancing weight is deemed not worth the extra computation time
* @return int
*/
public function getMaxBoxesToBalanceWeight()
{
return $this->maxBoxesToBalanceWeight;
}

/**
* Number of boxes at which balancing weight is deemed not worth the extra computation time
* @param int $maxBoxesToBalanceWeight
*/
public function setMaxBoxesToBalanceWeight($maxBoxesToBalanceWeight)
{
$this->maxBoxesToBalanceWeight = $maxBoxesToBalanceWeight;
}

/**
* Pack items into boxes
*
Expand All @@ -103,7 +125,7 @@ public function pack()
$packedBoxes = $this->doVolumePacking();

//If we have multiple boxes, try and optimise/even-out weight distribution
if ($packedBoxes->count() > 1 && $packedBoxes->count() < static::MAX_BOXES_TO_BALANCE_WEIGHT) {
if ($packedBoxes->count() > 1 && $packedBoxes->count() <= $this->maxBoxesToBalanceWeight) {
$redistributor = new WeightRedistributor($this->boxes);
$redistributor->setLogger($this->logger);
$packedBoxes = $redistributor->redistributeWeight($packedBoxes);
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Expand Up @@ -22,5 +22,6 @@ BoxPacker is licensed under the `MIT license`_.
installation
principles
getting-started
weight-distribution
advanced-usage
whatsnew
33 changes: 33 additions & 0 deletions docs/weight-distribution.rst
@@ -0,0 +1,33 @@
Weight distribution
===================

If you are shipping a large number of items to a single customer as many businesses do, it might be that more than one box is
required to accommodate all of the items. A common scenario which you'll have probably encountered when receiving your own
deliveries is that the first box(es) will be absolutely full as the warehouse operative will have tried to fit in as much as
possible. The last box by comparison will be virtually empty and mostly filled with protective inner packing.

There's nothing intrinsically wrong with this, but it can be a bit annoying for e.g. couriers and customers to receive e.g.
a 20kg box which requires heavy lifting alongside a similarly sized box that weighs hardly anything at all. If you have to send
two boxes anyway, it would be much better in such a situation to have e.g. an 11kg box and a 10kg box instead.

Happily, this smoothing out of weight is handled automatically for you by BoxPacker - once the initial dimension-only packing
is completed, a second pass is made that reallocates items from heavier boxes into any lighter ones that have space.

For most use-cases the benefits are worth the extra computation time - however if a single "packing" for your scenarios
involves a very large number of permutations e.g. thousands of items, you may wish to tune this behaviour.

By default, the weight distribution pass is made whenever the items fit into 12 boxes or less. To reduce (or increase) the
threshold, call ``setMaxBoxesToBalanceWeight()``

.. code-block:: php
<?php
use DVDoug\BoxPacker\Packer;
$packer = new Packer();
$packer->setMaxBoxesToBalanceWeight(3);
.. note::

A threshold value of either 0 or 1 will disable the weight distribution pass completely
39 changes: 38 additions & 1 deletion tests/PackerTest.php
Expand Up @@ -396,7 +396,8 @@ public function testIssue52C()
self::assertEquals(4, $box2->getUsedDepth());
}

public function testIssue79() {
public function testIssue79()
{
$packer = new Packer();
$packer->addBox(new TestBox('Bundle', 75, 15, 15, 0, 75, 15, 15, 30));
$packer->addItem(new TestItem('Item 1', 14, 12, 2, 2, true));
Expand All @@ -412,6 +413,42 @@ public function testIssue79() {
self::assertEquals(2, $box->getUsedDepth());
}

public function testCanSetMaxBoxesToWeightBalance()
{
$packer = new Packer();
$packer->setMaxBoxesToBalanceWeight(3);
$this->assertEquals(3, $packer->getMaxBoxesToBalanceWeight());
}

public function testWeightRedistributionActivatesUnderLimit()
{
$packer = new Packer();
$packer->addBox(new TestBox('Box', 1, 1, 3, 0, 1, 1, 3, 3));
$packer->addItem(new TestItem('Item', 1, 1, 1, 1, false), 4);
$packedBoxes = $packer->pack();

$box1 = $packedBoxes->extract();
$box2 = $packedBoxes->extract();

$this->assertEquals(2, $box1->getItems()->count());
$this->assertEquals(2, $box2->getItems()->count());
}

public function testWeightRedistributionDoesNotActivateOverLimit()
{
$packer = new Packer();
$packer->addBox(new TestBox('Box', 1, 1, 3, 0, 1, 1, 3, 3));
$packer->addItem(new TestItem('Item', 1, 1, 1, 1, false), 4);
$packer->setMaxBoxesToBalanceWeight(1);
$packedBoxes = $packer->pack();

$box1 = $packedBoxes->extract();
$box2 = $packedBoxes->extract();

$this->assertEquals(3, $box1->getItems()->count());
$this->assertEquals(1, $box2->getItems()->count());
}

/**
* @dataProvider getSamples
* @coversNothing
Expand Down

0 comments on commit 8d94cfc

Please sign in to comment.