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 JsonSerializable; |
||
| 13 | use Stringable; |
||
| 14 | |||
| 15 | use function atan; |
||
| 16 | use function min; |
||
| 17 | use function sort; |
||
| 18 | |||
| 19 | /** |
||
| 20 | * An item to be packed. |
||
| 21 | */ |
||
| 22 | class OrientatedItem implements JsonSerializable, Stringable |
||
| 23 | { |
||
| 24 | public readonly int $surfaceFootprint; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * @var array<string, bool> |
||
| 28 | */ |
||
| 29 | protected static array $stabilityCache = []; |
||
| 30 | |||
| 31 | /** |
||
| 32 | * @var int[] |
||
| 33 | */ |
||
| 34 | protected array $dimensionsAsArray; |
||
| 35 | 102 | ||
| 36 | public function __construct( |
||
| 37 | public readonly Item $item, |
||
| 38 | public readonly int $width, |
||
| 39 | public readonly int $length, |
||
| 40 | public readonly int $depth |
||
| 41 | 102 | ) { |
|
| 42 | $this->surfaceFootprint = $width * $length; |
||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
| 43 | 102 | ||
| 44 | 102 | $this->dimensionsAsArray = [$width, $length, $depth]; |
|
| 45 | sort($this->dimensionsAsArray); |
||
| 46 | } |
||
| 47 | |||
| 48 | /** |
||
| 49 | * Is this item stable (low centre of gravity), calculated as if the tipping point is >15 degrees. |
||
| 50 | * |
||
| 51 | * N.B. Assumes equal weight distribution. |
||
| 52 | 86 | */ |
|
| 53 | public function isStable(): bool |
||
| 54 | 86 | { |
|
| 55 | $cacheKey = $this->width . '|' . $this->length . '|' . $this->depth; |
||
| 56 | 86 | ||
| 57 | return static::$stabilityCache[$cacheKey] ?? (static::$stabilityCache[$cacheKey] = atan(min($this->length, $this->width) / ($this->depth ?: 1)) > 0.261); |
||
| 58 | } |
||
| 59 | |||
| 60 | /** |
||
| 61 | * Is the supplied item the same size as this one? |
||
| 62 | * |
||
| 63 | * @internal |
||
| 64 | 93 | */ |
|
| 65 | public function isSameDimensions(Item $item): bool |
||
| 66 | 93 | { |
|
| 67 | 56 | if ($item === $this->item) { |
|
| 68 | return true; |
||
| 69 | } |
||
| 70 | 63 | ||
| 71 | 63 | $itemDimensions = [$item->getWidth(), $item->getLength(), $item->getDepth()]; |
|
| 72 | sort($itemDimensions); |
||
| 73 | 63 | ||
| 74 | return $this->dimensionsAsArray === $itemDimensions; |
||
| 75 | } |
||
| 76 | 1 | ||
| 77 | public function jsonSerialize(): array |
||
| 78 | 1 | { |
|
| 79 | 1 | return [ |
|
| 80 | 1 | 'item' => $this->item, |
|
| 81 | 1 | 'width' => $this->width, |
|
| 82 | 1 | 'length' => $this->length, |
|
| 83 | 1 | 'depth' => $this->depth, |
|
| 84 | ]; |
||
| 85 | } |
||
| 86 | 1 | ||
| 87 | public function __toString(): string |
||
| 88 | 1 | { |
|
| 89 | return $this->width . '|' . $this->length . '|' . $this->depth; |
||
| 90 | } |
||
| 91 | } |
||
| 92 |