Skip to content

Commit

Permalink
Backport 2.x-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdoug committed Sep 15, 2019
1 parent 6152c16 commit dc5871e
Show file tree
Hide file tree
Showing 15 changed files with 277 additions and 95 deletions.
3 changes: 2 additions & 1 deletion .scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ build:
environment:
# Languages
php:
version: "7.1"
version: "7.3"

tests:
override:
-
command: 'vendor/bin/phpunit --coverage-clover=build/phpunit.clover --exclude-group efficiency'
idle_timeout: 3600
coverage:
file: 'build/phpunit.clover'
format: 'clover'
Expand Down
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ env:
- TEST_SUITE=functional
- TEST_SUITE=efficiency
php:
- 7.4snapshot
- 7.3
- 7.2
- 7.1
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"require-dev": {
"behat/behat": "^3.4",
"dvdoug/behat-code-coverage": "^3.0||^4.0",
"monolog/monolog": "^1.0",
"monolog/monolog": "^1.0||^2.0",
"phpunit/phpunit": "^4.8.35||^5.0||^6.0||^7.0||^8.0"
},
"config": {
Expand Down
12 changes: 6 additions & 6 deletions src/BoxList.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,20 @@ public function compare($boxA, $boxB)
}

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

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

return 0;
}
Expand Down
15 changes: 15 additions & 0 deletions src/ItemList.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ public function compare($itemA, $itemB)
}
}

/**
* Do a bulk create.
*
* @param Item[] $items
* @return ItemList
*/
public static function fromArray(array $items)
{
$list = new static();
foreach ($items as $item) {
$list->insert($item);
}
return $list;
}

/**
* Get copy of this list as a standard PHP array.
*
Expand Down
38 changes: 16 additions & 22 deletions src/OrientatedItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*
* @author Doug Wright
*/

namespace DVDoug\BoxPacker;

use JsonSerializable;
Expand Down Expand Up @@ -37,9 +36,14 @@ class OrientatedItem implements JsonSerializable
protected $depth;

/**
* @var float[]
* @var int
*/
protected $surfaceFootprint;

/**
* @var bool[]
*/
protected static $tippingPointCache = [];
protected static $stabilityCache = [];

/**
* Constructor.
Expand All @@ -55,6 +59,7 @@ public function __construct(Item $item, $width, $length, $depth)
$this->width = $width;
$this->length = $length;
$this->depth = $depth;
$this->surfaceFootprint = $width * $length;
}

/**
Expand Down Expand Up @@ -104,24 +109,7 @@ public function getDepth()
*/
public function getSurfaceFootprint()
{
return $this->width * $this->length;
}

/**
* @return float
*/
public function getTippingPoint()
{
$cacheKey = $this->width . '|' . $this->length . '|' . $this->depth;

if (isset(static::$tippingPointCache[$cacheKey])) {
$tippingPoint = static::$tippingPointCache[$cacheKey];
} else {
$tippingPoint = atan(min($this->length, $this->width) / ($this->depth ?: 1));
static::$tippingPointCache[$cacheKey] = $tippingPoint;
}

return $tippingPoint;
return $this->surfaceFootprint;
}

/**
Expand All @@ -133,7 +121,13 @@ public function getTippingPoint()
*/
public function isStable()
{
return $this->getTippingPoint() > 0.261;
$cacheKey = $this->width . '|' . $this->length . '|' . $this->depth;

if (!isset(static::$stabilityCache[$cacheKey])) {
static::$stabilityCache[$cacheKey] = (atan(min($this->length, $this->width) / ($this->depth ?: 1)) > 0.261);
}

return static::$stabilityCache[$cacheKey];
}

/**
Expand Down
36 changes: 25 additions & 11 deletions src/OrientatedItemFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;

/**
* Figure out orientations for an item and a given set of dimensions.
Expand All @@ -30,6 +31,7 @@ class OrientatedItemFactory implements LoggerAwareInterface
public function __construct(Box $box)
{
$this->box = $box;
$this->logger = new NullLogger();
}

/**
Expand Down Expand Up @@ -149,27 +151,39 @@ public function getPossibleOrientations(
$z,
PackedItemList $prevPackedItemList
) {
$orientations = [];
$orientations = $orientationsDimensions = [];

$isSame = false;
if ($prevItem) {
$itemADimensions = [$item->getWidth(), $item->getLength(), $item->getDepth()];
$itemBDimensions = [$prevItem->getWidth(), $prevItem->getLength(), $prevItem->getDepth()];
sort($itemADimensions);
sort($itemBDimensions);
$isSame = ($itemADimensions === $itemBDimensions);
}

//Special case items that are the same as what we just packed - keep orientation
if ($prevItem && $this->isSameDimensions($prevItem->getItem(), $item)) {
$orientations[] = new OrientatedItem($item, $prevItem->getWidth(), $prevItem->getLength(), $prevItem->getDepth());
if ($isSame && $prevItem) {
$orientationsDimensions[] = [$prevItem->getWidth(), $prevItem->getLength(), $prevItem->getDepth()];
} else {
//simple 2D rotation
$orientations[] = new OrientatedItem($item, $item->getWidth(), $item->getLength(), $item->getDepth());
$orientations[] = new OrientatedItem($item, $item->getLength(), $item->getWidth(), $item->getDepth());
$orientationsDimensions[] = [$item->getWidth(), $item->getLength(), $item->getDepth()];
$orientationsDimensions[] = [$item->getLength(), $item->getWidth(), $item->getDepth()];
}

$orientations = array_unique($orientations);

//remove any that simply don't fit
$orientations = array_filter($orientations, function (OrientatedItem $i) use ($widthLeft, $lengthLeft, $depthLeft) {
return $i->getWidth() <= $widthLeft && $i->getLength() <= $lengthLeft && $i->getDepth() <= $depthLeft;
$orientationsDimensions = array_unique($orientationsDimensions, SORT_REGULAR);
$orientationsDimensions = array_filter($orientationsDimensions, static function (array $i) use ($widthLeft, $lengthLeft, $depthLeft) {
return $i[0] <= $widthLeft && $i[1] <= $lengthLeft && $i[2] <= $depthLeft;
});

foreach ($orientationsDimensions as $dimensions) {
$orientations[] = new OrientatedItem($item, $dimensions[0], $dimensions[1], $dimensions[2]);
}

if ($item instanceof ConstrainedPlacementItem) {
$box = $this->box;
$orientations = array_filter($orientations, function (OrientatedItem $i) use ($box, $x, $y, $z, $prevPackedItemList) {
$orientations = array_filter($orientations, static function (OrientatedItem $i) use ($box, $x, $y, $z, $prevPackedItemList) {
/** @var ConstrainedPlacementItem $constrainedItem */
$constrainedItem = $i->getItem();

Expand Down Expand Up @@ -285,7 +299,7 @@ function (OrientatedItem $orientation) {
*
* @return bool
*/
protected function isSameDimensions(Item $itemA, Item $itemB)
public function isSameDimensions(Item $itemA, Item $itemB)
{
$itemADimensions = [$itemA->getWidth(), $itemA->getLength(), $itemA->getDepth()];
$itemBDimensions = [$itemB->getWidth(), $itemB->getLength(), $itemB->getDepth()];
Expand Down
Loading

0 comments on commit dc5871e

Please sign in to comment.