/** * Constructor */ public function __construct(Box $box, ItemList $items) { $this->logger = new NullLogger(); $this->box = $box; $this->items = $items; $this->depthLeft = $this->box->getInnerDepth(); $this->remainingWeight = $this->box->getMaxWeight() - $this->box->getEmptyWeight(); $this->widthLeft = $this->box->getInnerWidth(); $this->lengthLeft = $this->box->getInnerLength(); }
/** * Pack as many items as possible into specific given box * @param Box $aBox * @param ItemList $aItems * @return PackedBox packed box */ public function packIntoBox(Box $aBox, ItemList $aItems) { $this->logger->log(LogLevel::DEBUG, "[EVALUATING BOX] {$aBox->getReference()}"); $packedItems = new ItemList(); $remainingDepth = $aBox->getInnerDepth(); $remainingWeight = $aBox->getMaxWeight() - $aBox->getEmptyWeight(); $remainingWidth = $aBox->getInnerWidth(); $remainingLength = $aBox->getInnerLength(); $layerWidth = $layerLength = $layerDepth = 0; while (!$aItems->isEmpty()) { $itemToPack = $aItems->top(); if ($itemToPack->getDepth() > $remainingDepth || $itemToPack->getWeight() > $remainingWeight) { break; } $this->logger->log(LogLevel::DEBUG, "evaluating item {$itemToPack->getDescription()}"); $this->logger->log(LogLevel::DEBUG, "remaining width: {$remainingWidth}, length: {$remainingLength}, depth: {$remainingDepth}"); $this->logger->log(LogLevel::DEBUG, "layerWidth: {$layerWidth}, layerLength: {$layerLength}, layerDepth: {$layerDepth}"); $itemWidth = $itemToPack->getWidth(); $itemLength = $itemToPack->getLength(); $fitsSameGap = min($remainingWidth - $itemWidth, $remainingLength - $itemLength); $fitsRotatedGap = min($remainingWidth - $itemLength, $remainingLength - $itemWidth); if ($fitsSameGap >= 0 || $fitsRotatedGap >= 0) { $packedItems->insert($aItems->extract()); $remainingWeight -= $itemToPack->getWeight(); if ($fitsRotatedGap < 0 || $fitsSameGap >= 0 && $fitsSameGap <= $fitsRotatedGap || !$aItems->isEmpty() && $aItems->top() == $itemToPack && $remainingLength >= 2 * $itemLength) { $this->logger->log(LogLevel::DEBUG, "fits (better) unrotated"); $remainingLength -= $itemLength; $layerLength += $itemLength; $layerWidth = max($itemWidth, $layerWidth); } else { $this->logger->log(LogLevel::DEBUG, "fits (better) rotated"); $remainingLength -= $itemWidth; $layerLength += $itemWidth; $layerWidth = max($itemLength, $layerWidth); } $layerDepth = max($layerDepth, $itemToPack->getDepth()); //greater than 0, items will always be less deep //allow items to be stacked in place within the same footprint up to current layerdepth $maxStackDepth = $layerDepth - $itemToPack->getDepth(); while (!$aItems->isEmpty()) { $potentialStackItem = $aItems->top(); if ($potentialStackItem->getDepth() <= $maxStackDepth && $potentialStackItem->getWeight() <= $remainingWeight && $potentialStackItem->getWidth() <= $itemToPack->getWidth() && $potentialStackItem->getLength() <= $itemToPack->getLength()) { $remainingWeight -= $potentialStackItem->getWeight(); $maxStackDepth -= $potentialStackItem->getDepth(); $packedItems->insert($aItems->extract()); } else { break; } } } else { if ($remainingWidth >= min($itemWidth, $itemLength) && $layerDepth > 0 && $layerWidth > 0 && $layerLength > 0) { $this->logger->log(LogLevel::DEBUG, "No more fit in lengthwise, resetting for new row"); $remainingLength += $layerLength; $remainingWidth -= $layerWidth; $layerWidth = $layerLength = 0; continue; } if ($remainingLength < min($itemWidth, $itemLength) || $layerDepth == 0) { $this->logger->log(LogLevel::DEBUG, "doesn't fit on layer even when empty"); break; } $remainingWidth = $layerWidth ? min(floor($layerWidth * 1.1), $aBox->getInnerWidth()) : $aBox->getInnerWidth(); $remainingLength = $layerLength ? min(floor($layerLength * 1.1), $aBox->getInnerLength()) : $aBox->getInnerLength(); $remainingDepth -= $layerDepth; $layerWidth = $layerLength = $layerDepth = 0; $this->logger->log(LogLevel::DEBUG, "doesn't fit, so starting next vertical layer"); } } $this->logger->log(LogLevel::DEBUG, "done with this box"); return new PackedBox($aBox, $packedItems, $remainingWidth, $remainingLength, $remainingDepth, $remainingWeight); }
/** * Pack as many items as possible into specific given box * @param Box $box * @param ItemList $items * @return PackedBox packed box */ public function packIntoBox(Box $box, ItemList $items) { $this->logger->log(LogLevel::DEBUG, "[EVALUATING BOX] {$box->getReference()}"); $packedItems = new ItemList(); $remainingDepth = $box->getInnerDepth(); $remainingWeight = $box->getMaxWeight() - $box->getEmptyWeight(); $remainingWidth = $box->getInnerWidth(); $remainingLength = $box->getInnerLength(); $layerWidth = $layerLength = $layerDepth = 0; while (!$items->isEmpty()) { $itemToPack = $items->top(); //skip items that are simply too large if ($this->isItemTooLargeForBox($itemToPack, $remainingDepth, $remainingWeight)) { $items->extract(); continue; } $this->logger->log(LogLevel::DEBUG, "evaluating item {$itemToPack->getDescription()}"); $this->logger->log(LogLevel::DEBUG, "remaining width: {$remainingWidth}, length: {$remainingLength}, depth: {$remainingDepth}"); $this->logger->log(LogLevel::DEBUG, "layerWidth: {$layerWidth}, layerLength: {$layerLength}, layerDepth: {$layerDepth}"); $itemWidth = $itemToPack->getWidth(); $itemLength = $itemToPack->getLength(); if ($this->fitsGap($itemToPack, $remainingWidth, $remainingLength)) { $packedItems->insert($items->extract()); $remainingWeight -= $itemToPack->getWeight(); $nextItem = !$items->isEmpty() ? $items->top() : null; if ($this->fitsBetterRotated($itemToPack, $nextItem, $remainingWidth, $remainingLength)) { $this->logger->log(LogLevel::DEBUG, "fits (better) unrotated"); $remainingLength -= $itemLength; $layerLength += $itemLength; $layerWidth = max($itemWidth, $layerWidth); } else { $this->logger->log(LogLevel::DEBUG, "fits (better) rotated"); $remainingLength -= $itemWidth; $layerLength += $itemWidth; $layerWidth = max($itemLength, $layerWidth); } $layerDepth = max($layerDepth, $itemToPack->getDepth()); //greater than 0, items will always be less deep //allow items to be stacked in place within the same footprint up to current layerdepth $maxStackDepth = $layerDepth - $itemToPack->getDepth(); while (!$items->isEmpty() && $this->canStackItemInLayer($itemToPack, $items->top(), $maxStackDepth, $remainingWeight)) { $remainingWeight -= $items->top()->getWeight(); $maxStackDepth -= $items->top()->getDepth(); $packedItems->insert($items->extract()); } } else { if ($remainingWidth >= min($itemWidth, $itemLength) && $this->isLayerStarted($layerWidth, $layerLength, $layerDepth)) { $this->logger->log(LogLevel::DEBUG, "No more fit in lengthwise, resetting for new row"); $remainingLength += $layerLength; $remainingWidth -= $layerWidth; $layerWidth = $layerLength = 0; continue; } elseif ($remainingLength < min($itemWidth, $itemLength) || $layerDepth == 0) { $this->logger->log(LogLevel::DEBUG, "doesn't fit on layer even when empty"); $items->extract(); continue; } $remainingWidth = $layerWidth ? min(floor($layerWidth * 1.1), $box->getInnerWidth()) : $box->getInnerWidth(); $remainingLength = $layerLength ? min(floor($layerLength * 1.1), $box->getInnerLength()) : $box->getInnerLength(); $remainingDepth -= $layerDepth; $layerWidth = $layerLength = $layerDepth = 0; $this->logger->log(LogLevel::DEBUG, "doesn't fit, so starting next vertical layer"); } } $this->logger->log(LogLevel::DEBUG, "done with this box"); return new PackedBox($box, $packedItems, $remainingWidth, $remainingLength, $remainingDepth, $remainingWeight); }