Skip to content

Commit

Permalink
Backport v3.x
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdoug committed Jul 14, 2019
1 parent 8f914b3 commit 081a6e2
Show file tree
Hide file tree
Showing 33 changed files with 1,005 additions and 184 deletions.
54 changes: 39 additions & 15 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,61 @@
group: travis_latest
language: php

env:
- TEST_SUITE=unit
- TEST_SUITE=functional
- TEST_SUITE=efficiency
php:
- 7.3
- 7.2
- 7.1
- 7.0
- 5.6
- 5.5
- 5.4
- nightly

matrix:
include:
- php: 5.4
env: lowest=1
- php: 5.5
- php: 5.6
- php: 7.0
- php: 7.1
- php: 7.2
- php: 7.3
allow_failures:
- php: nightly

cache:
directories:
- $HOME/.cache/composer/files
- $HOME/.composer/cache
- $HOME/.cache/composer

install:
- phpenv config-rm xdebug.ini || true;

- composer validate --strict;
- |
sed -i 's/"bin-compat" : "full"/"bin-compat" : "auto"/g' composer.json;
- |
if [ "$csfixer" != "1" ]; then
composer remove --dev friendsofphp/php-cs-fixer;
fi;
- |
if [ "$lowest" = "1" ]; then
composer update --prefer-lowest --prefer-stable;
else
composer update;
fi;
before_script:
script:
- |
if [ "$TRAVIS_PHP_VERSION" != "5.4" ] && [ "$TRAVIS_PHP_VERSION" != "5.5" ] && [ "$TRAVIS_PHP_VERSION" != "5.6" ]; then
echo "memory_limit = 3072M" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini;
if [ "$csfixer" = "1" ]; then
vendor/bin/php-cs-fixer fix --verbose --dry-run --diff --diff-format=udiff --allow-risky=yes;
fi;
- |
if [ "${TEST_SUITE}" = "unit" ]; then
php vendor/bin/phpunit --exclude-group efficiency;
elif [ "${TEST_SUITE}" = "efficiency" ]; then
php vendor/bin/phpunit --group efficiency;
elif [ "${TEST_SUITE}" = "functional" ]; then
php vendor/bin/behat --strict;
else
php vendor/bin/behat --strict;
php vendor/bin/phpunit;
fi;
script:
- php vendor/bin/phpunit;
- php vendor/bin/behat --strict;
11 changes: 10 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,23 @@
"license": "MIT",
"require": {
"php": ">=5.4.0",
"psr/log": "^1.0"
"psr/log": "^1.0",
"ext-json": "*"
},
"require-dev": {
"behat/behat": "^3.4",
"leanphp/behat-code-coverage": "^3.0",
"monolog/monolog": "^1.0",
"phpunit/phpunit": "^4.8.35||^5.0||^6.0||^7.0"
},
"config": {
"preferred-install": {
"*": "dist"
},
"sort-packages": true,
"bin-compat" : "full",
"optimize-autoloader": true
},
"autoload": {
"psr-4": {
"DVDoug\\BoxPacker\\": "src/",
Expand Down
87 changes: 79 additions & 8 deletions docs/advanced-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ alternative/additional sizes of box.

At a high level, the ``getVolumeUtilisation()`` method exists which calculates how full the box is as a percentage of volume.

Lower-level methods are also available for examining this data in detail either using ``getUsed[Width|Length|Depth()``
(a hypothetical box placed around the items) or ``getRemaining[Width|Length|Depth()`` (the difference between the dimensions of
Lower-level methods are also available for examining this data in detail either using ``getUsed[Width|Length|Depth()]``
(a hypothetical box placed around the items) or ``getRemaining[Width|Length|Depth()]`` (the difference between the dimensions of
the actual box and the hypothetical box).

.. note::
Expand All @@ -37,7 +37,7 @@ Custom Constraints
------------------

For more advanced use cases where greater control over the contents of each box is required (e.g. legal limits on the number of
hazardous items per box, or perhaps fragile items requiring an extra-strong outer box) you may implement the ``BoxPacker\ConstrainedItem``
hazardous items per box, or perhaps fragile items requiring an extra-strong outer box) you may implement the ``BoxPacker\ConstrainedPlacementItem``
interface which contains an additional callback method allowing you to decide whether to allow an item may be packed into a box
or not.

Expand All @@ -54,17 +54,32 @@ Example - only allow 2 batteries per box
use DVDoug\BoxPacker\Item;
use DVDoug\BoxPacker\ItemList;
class LithiumBattery implements ConstrainedItem
class LithiumBattery implements ConstrainedPlacementItem
{
/**
* @param ItemList $alreadyPackedItems
* @param TestBox $box
* Max 2 batteries per box.
*
* @param Box $box
* @param PackedItemList $alreadyPackedItems
* @param int $proposedX
* @param int $proposedY
* @param int $proposedZ
* @param int $width
* @param int $length
* @param int $depth
* @return bool
*/
public function canBePackedInBox(ItemList $alreadyPackedItems, Box $box)
{
public function canBePacked(
Box $box,
PackedItemList $alreadyPackedItems,
int $proposedX,
int $proposedY,
int $proposedZ,
int $width,
int $length,
int $depth
) {
$batteriesPacked = 0;
foreach ($alreadyPackedItems as $packedItem) {
if ($packedItem instanceof LithiumBattery) {
Expand All @@ -80,3 +95,59 @@ Example - only allow 2 batteries per box
}
}
Example - don't allow batteries to be stacked
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: php
<?php
use DVDoug\BoxPacker\Box;
use DVDoug\BoxPacker\Item;
use DVDoug\BoxPacker\ItemList;
class LithiumBattery implements ConstrainedPlacementItem
{
/**
* Batteries cannot be stacked on top of each other.
*
* @param Box $box
* @param PackedItemList $alreadyPackedItems
* @param int $proposedX
* @param int $proposedY
* @param int $proposedZ
* @param int $width
* @param int $length
* @param int $depth
* @return bool
*/
public function canBePacked(
Box $box,
PackedItemList $alreadyPackedItems,
int $proposedX,
int $proposedY,
int $proposedZ,
int $width,
int $length,
int $depth
) {
$alreadyPackedType = array_filter(
iterator_to_array($alreadyPackedItems, false),
function (Item $item) {
return $item->getDescription() === 'Battery';
}
);
/** @var PackedItem $alreadyPacked */
foreach ($alreadyPackedType as $alreadyPacked) {
if (
$alreadyPacked->getZ() + $alreadyPacked->getDepth() === $proposedZ &&
$proposedX >= $alreadyPacked->getX() && $proposedX <= ($alreadyPacked->getX() + $alreadyPacked->getWidth()) &&
$proposedY >= $alreadyPacked->getY() && $proposedY <= ($alreadyPacked->getY() + $alreadyPacked->getLength())) {
return false;
}
}
return true;
}
}
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

# General information about the project.
project = u'BoxPacker'
copyright = u'2018, Doug Wright'
copyright = u'2019, Doug Wright'
author = u'Doug Wright'

# The version info for the project you're documenting, acts as replacement for
Expand Down
1 change: 0 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ BoxPacker is licensed under the `MIT license`_.
installation
principles
getting-started
advanced-usage
weight-distribution
advanced-usage
whatsnew
2 changes: 1 addition & 1 deletion license.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (C) 2012-2018 Doug Wright
Copyright (C) 2012-2019 Doug Wright

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@

<php>
<ini name="date.timezone" value="UTC"/>
<ini name="error_reporting" value="2147483647"/>
</php>
</phpunit>
29 changes: 25 additions & 4 deletions src/BoxList.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,33 @@ class BoxList extends \SplMinHeap
*/
public function compare($boxA, $boxB)
{
if ($boxB->getInnerVolume() > $boxA->getInnerVolume()) {
$boxAVolume = $boxA->getInnerWidth() * $boxA->getInnerLength() * $boxA->getInnerDepth();
$boxBVolume = $boxB->getInnerWidth() * $boxB->getInnerLength() * $boxB->getInnerDepth();

// try smallest box first
if ($boxBVolume > $boxAVolume) {
return 1;
} elseif ($boxB->getInnerVolume() < $boxA->getInnerVolume()) {
}
if ($boxAVolume > $boxBVolume) {
return -1;
}

// smallest empty weight
if ($boxB->getEmptyWeight() > $boxA->getEmptyWeight()) {
return 1;
}
if ($boxA->getEmptyWeight() > $boxB->getEmptyWeight()) {
return -1;
} else {
return 0;
}

// maximum weight capacity as fallback decider
if ($boxB->getMaxWeight() > $boxA->getMaxWeight()) {
return 1;
}
if ($boxA->getMaxWeight() > $boxB->getMaxWeight()) {
return -1;
}

return 0;
}
}
2 changes: 1 addition & 1 deletion src/ConstrainedItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
*
* @author Doug Wright
*/

namespace DVDoug\BoxPacker;

/**
* An item to be packed where additional constraints need to be considered. Only implement this interface if you actually
* need this additional functionality as it will slow down the packing algorithm.
*
* @deprecated use ConstrainedPlacementItem instead which has additional flexibility
* @author Doug Wright
*/
interface ConstrainedItem extends Item
Expand Down
40 changes: 40 additions & 0 deletions src/ConstrainedPlacementItem.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
/**
* Box packing (3D bin packing, knapsack problem).
*
* @author Doug Wright
*/
namespace DVDoug\BoxPacker;

/**
* An item to be packed where additional constraints need to be considered. Only implement this interface if you actually
* need this additional functionality as it will slow down the packing algorithm.
*
* @author Doug Wright
*/
interface ConstrainedPlacementItem extends Item
{
/**
* Hook for user implementation of item-specific constraints, e.g. max <x> batteries per box.
*
* @param Box $box
* @param PackedItemList $alreadyPackedItems
* @param int $proposedX
* @param int $proposedY
* @param int $proposedZ
* @param int $width
* @param int $length
* @param int $depth
* @return bool
*/
public function canBePacked(
Box $box,
PackedItemList $alreadyPackedItems,
$proposedX,
$proposedY,
$proposedZ,
$width,
$length,
$depth
);
}
22 changes: 22 additions & 0 deletions src/ItemList.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,26 @@ public function topN($n)
return $topNList;
}

/**
* Remove item from list.
*
* @param Item $item
*/
public function remove(Item $item)
{
$workingSet = [];
while (!$this->isEmpty()) {
$workingSet[] = $this->extract();
}

$removed = false; // there can be multiple identical items, ensure that only 1 is removed
foreach ($workingSet as $workingSetItem) {
if (!$removed && $workingSetItem === $item) {
$removed = true;
} else {
$this->insert($workingSetItem);
}
}

}
}
Loading

0 comments on commit 081a6e2

Please sign in to comment.