dvdoug /
BoxPacker
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * Box packing (3D bin packing, knapsack problem). |
||
| 5 | * |
||
| 6 | * @author Doug Wright |
||
| 7 | */ |
||
| 8 | declare(strict_types=1); |
||
| 9 | |||
| 10 | namespace DVDoug\BoxPacker; |
||
| 11 | |||
| 12 | use ArrayIterator; |
||
| 13 | use Countable; |
||
| 14 | use IteratorAggregate; |
||
| 15 | use Traversable; |
||
| 16 | |||
| 17 | use function array_map; |
||
| 18 | use function count; |
||
| 19 | use function usort; |
||
| 20 | |||
| 21 | /** |
||
| 22 | * List of packed items, ordered by volume. |
||
| 23 | */ |
||
| 24 | class PackedItemList implements Countable, IteratorAggregate |
||
| 25 | { |
||
| 26 | /** |
||
| 27 | * @var PackedItem[] |
||
| 28 | */ |
||
| 29 | private array $list = []; |
||
| 30 | |||
| 31 | private int $weight = 0; |
||
| 32 | |||
| 33 | private int $volume = 0; |
||
| 34 | |||
| 35 | private bool $isSorted = false; |
||
| 36 | 98 | ||
| 37 | public function insert(PackedItem $item): void |
||
| 38 | 98 | { |
|
| 39 | 98 | $this->list[] = $item; |
|
| 40 | 98 | $this->weight += $item->item->getWeight(); |
|
| 41 | $this->volume += $item->width * $item->length * $item->depth; |
||
| 42 | } |
||
| 43 | |||
| 44 | /** |
||
| 45 | * @return Traversable<PackedItem> |
||
| 46 | 87 | */ |
|
| 47 | public function getIterator(): Traversable |
||
| 48 | 87 | { |
|
| 49 | 87 | if (!$this->isSorted) { |
|
| 50 | 87 | usort($this->list, $this->compare(...)); |
|
| 51 | $this->isSorted = true; |
||
| 52 | } |
||
| 53 | 87 | ||
| 54 | return new ArrayIterator($this->list); |
||
| 55 | } |
||
| 56 | |||
| 57 | /** |
||
| 58 | * Number of items in list. |
||
| 59 | 101 | */ |
|
| 60 | public function count(): int |
||
| 61 | 101 | { |
|
| 62 | return count($this->list); |
||
| 63 | } |
||
| 64 | |||
| 65 | /** |
||
| 66 | * Get copy of this list as a standard PHP array. |
||
| 67 | * |
||
| 68 | * @internal |
||
| 69 | * |
||
| 70 | * @return Item[] |
||
| 71 | 9 | */ |
|
| 72 | public function asItemArray(): array |
||
| 73 | 9 | { |
|
| 74 | return array_map(fn (PackedItem $packedItem) => $packedItem->item, $this->list); |
||
| 75 | } |
||
| 76 | |||
| 77 | /** |
||
| 78 | * Get total volume of these items. |
||
| 79 | 32 | */ |
|
| 80 | public function getVolume(): int |
||
| 81 | 32 | { |
|
| 82 | return $this->volume; |
||
| 83 | } |
||
| 84 | |||
| 85 | /** |
||
| 86 | * Get total weight of these items. |
||
| 87 | 101 | */ |
|
| 88 | public function getWeight(): int |
||
| 89 | 101 | { |
|
| 90 | return $this->weight; |
||
| 91 | } |
||
| 92 | 74 | ||
| 93 | private function compare(PackedItem $itemA, PackedItem $itemB): int |
||
|
0 ignored issues
–
show
|
|||
| 94 | 74 | { |
|
| 95 | 74 | $itemAVolume = $itemA->item->getWidth() * $itemA->item->getLength() * $itemA->item->getDepth(); |
|
| 96 | $itemBVolume = $itemB->item->getWidth() * $itemB->item->getLength() * $itemB->item->getDepth(); |
||
| 97 | 74 | ||
| 98 | return ($itemBVolume <=> $itemAVolume) ?: ($itemB->item->getWeight() <=> $itemA->item->getWeight()); |
||
| 99 | } |
||
| 100 | } |
||
| 101 |
This check looks for private methods that have been defined, but are not used inside the class.