1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Box packing (3D bin packing, knapsack problem). |
4
|
|
|
* |
5
|
|
|
* @author Doug Wright |
6
|
|
|
*/ |
7
|
|
|
declare(strict_types=1); |
8
|
|
|
|
9
|
|
|
namespace DVDoug\BoxPacker; |
10
|
|
|
|
11
|
|
|
use function max; |
12
|
|
|
use function min; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* A packed layer. |
16
|
|
|
* @internal |
17
|
|
|
*/ |
18
|
|
|
class PackedLayer |
19
|
|
|
{ |
20
|
|
|
/** |
21
|
|
|
* @var PackedItem[] |
22
|
|
|
*/ |
23
|
|
|
protected array $items = []; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Add a packed item to this layer. |
27
|
|
|
*/ |
28
|
95 |
|
public function insert(PackedItem $packedItem): void |
29
|
|
|
{ |
30
|
95 |
|
$this->items[] = $packedItem; |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Get the packed items. |
35
|
|
|
* |
36
|
|
|
* @return PackedItem[] |
37
|
|
|
*/ |
38
|
97 |
|
public function getItems(): array |
39
|
|
|
{ |
40
|
97 |
|
return $this->items; |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Calculate footprint area of this layer. |
45
|
|
|
* |
46
|
|
|
* @return int mm^2 |
47
|
|
|
*/ |
48
|
46 |
|
public function getFootprint(): int |
49
|
|
|
{ |
50
|
46 |
|
return $this->getWidth() * $this->getLength(); |
51
|
|
|
} |
52
|
|
|
|
53
|
1 |
|
public function getStartX(): int |
54
|
|
|
{ |
55
|
1 |
|
if (!$this->items) { |
|
|
|
|
56
|
|
|
return 0; |
57
|
|
|
} |
58
|
|
|
|
59
|
1 |
|
$values = []; |
60
|
1 |
|
foreach ($this->items as $item) { |
61
|
1 |
|
$values[] = $item->getX(); |
62
|
|
|
} |
63
|
|
|
|
64
|
1 |
|
return min($values); |
65
|
|
|
} |
66
|
|
|
|
67
|
95 |
|
public function getEndX(): int |
68
|
|
|
{ |
69
|
95 |
|
if (!$this->items) { |
|
|
|
|
70
|
|
|
return 0; |
71
|
|
|
} |
72
|
|
|
|
73
|
95 |
|
$values = []; |
74
|
95 |
|
foreach ($this->items as $item) { |
75
|
95 |
|
$values[] = $item->getX() + $item->getWidth(); |
76
|
|
|
} |
77
|
|
|
|
78
|
95 |
|
return max($values); |
79
|
|
|
} |
80
|
|
|
|
81
|
46 |
|
public function getWidth(): int |
82
|
|
|
{ |
83
|
46 |
|
if (!$this->items) { |
|
|
|
|
84
|
|
|
return 0; |
85
|
|
|
} |
86
|
|
|
|
87
|
46 |
|
$start = []; |
88
|
46 |
|
$end = []; |
89
|
46 |
|
foreach ($this->items as $item) { |
90
|
46 |
|
$start[] = $item->getX(); |
91
|
46 |
|
$end[] = $item->getX() + $item->getWidth(); |
92
|
|
|
} |
93
|
|
|
|
94
|
46 |
|
return max($end) - min($start); |
95
|
|
|
} |
96
|
|
|
|
97
|
1 |
|
public function getStartY(): int |
98
|
|
|
{ |
99
|
1 |
|
if (!$this->items) { |
|
|
|
|
100
|
|
|
return 0; |
101
|
|
|
} |
102
|
|
|
|
103
|
1 |
|
$values = []; |
104
|
1 |
|
foreach ($this->items as $item) { |
105
|
1 |
|
$values[] = $item->getY(); |
106
|
|
|
} |
107
|
|
|
|
108
|
1 |
|
return min($values); |
109
|
|
|
} |
110
|
|
|
|
111
|
95 |
|
public function getEndY(): int |
112
|
|
|
{ |
113
|
95 |
|
if (!$this->items) { |
|
|
|
|
114
|
93 |
|
return 0; |
115
|
|
|
} |
116
|
|
|
|
117
|
95 |
|
$values = []; |
118
|
95 |
|
foreach ($this->items as $item) { |
119
|
95 |
|
$values[] = $item->getY() + $item->getLength(); |
120
|
|
|
} |
121
|
|
|
|
122
|
95 |
|
return max($values); |
123
|
|
|
} |
124
|
|
|
|
125
|
46 |
|
public function getLength(): int |
126
|
|
|
{ |
127
|
46 |
|
if (!$this->items) { |
|
|
|
|
128
|
|
|
return 0; |
129
|
|
|
} |
130
|
|
|
|
131
|
46 |
|
$start = []; |
132
|
46 |
|
$end = []; |
133
|
46 |
|
foreach ($this->items as $item) { |
134
|
46 |
|
$start[] = $item->getY(); |
135
|
46 |
|
$end[] = $item->getY() + $item->getLength(); |
136
|
|
|
} |
137
|
|
|
|
138
|
46 |
|
return max($end) - min($start); |
139
|
|
|
} |
140
|
|
|
|
141
|
94 |
|
public function getStartZ(): int |
142
|
|
|
{ |
143
|
94 |
|
if (!$this->items) { |
|
|
|
|
144
|
|
|
return 0; |
145
|
|
|
} |
146
|
|
|
|
147
|
94 |
|
$values = []; |
148
|
94 |
|
foreach ($this->items as $item) { |
149
|
94 |
|
$values[] = $item->getZ(); |
150
|
|
|
} |
151
|
|
|
|
152
|
94 |
|
return min($values); |
153
|
|
|
} |
154
|
|
|
|
155
|
1 |
|
public function getEndZ(): int |
156
|
|
|
{ |
157
|
1 |
|
if (!$this->items) { |
|
|
|
|
158
|
|
|
return 0; |
159
|
|
|
} |
160
|
|
|
|
161
|
1 |
|
$values = []; |
162
|
1 |
|
foreach ($this->items as $item) { |
163
|
1 |
|
$values[] = $item->getZ() + $item->getDepth(); |
164
|
|
|
} |
165
|
|
|
|
166
|
1 |
|
return max($values); |
167
|
|
|
} |
168
|
|
|
|
169
|
95 |
|
public function getDepth(): int |
170
|
|
|
{ |
171
|
95 |
|
if (!$this->items) { |
|
|
|
|
172
|
|
|
return 0; |
173
|
|
|
} |
174
|
|
|
|
175
|
95 |
|
$start = []; |
176
|
95 |
|
$end = []; |
177
|
95 |
|
foreach ($this->items as $item) { |
178
|
95 |
|
$start[] = $item->getZ(); |
179
|
95 |
|
$end[] = $item->getZ() + $item->getDepth(); |
180
|
|
|
} |
181
|
|
|
|
182
|
95 |
|
return max($end) - min($start); |
183
|
|
|
} |
184
|
|
|
|
185
|
1 |
|
public function getWeight(): int |
186
|
|
|
{ |
187
|
1 |
|
$weight = 0; |
188
|
1 |
|
foreach ($this->items as $item) { |
189
|
1 |
|
$weight += $item->getItem()->getWeight(); |
190
|
|
|
} |
191
|
|
|
|
192
|
1 |
|
return $weight; |
193
|
|
|
} |
194
|
|
|
|
195
|
94 |
|
public function merge(self $otherLayer): void |
196
|
|
|
{ |
197
|
94 |
|
foreach ($otherLayer->items as $packedItem) { |
198
|
22 |
|
$this->items[] = $packedItem; |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.