PackedBoxList::getMeanItemWeight()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 9
ccs 4
cts 4
cp 1
crap 2
rs 10
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 JsonSerializable;
16
use Traversable;
17
18
use function count;
19
use function json_encode;
20
use function reset;
21
use function round;
22
use function usort;
23
use function array_map;
24
use function iterator_to_array;
25
use function spl_object_id;
26
27
use const JSON_THROW_ON_ERROR;
28
use const JSON_NUMERIC_CHECK;
29
use const JSON_UNESCAPED_UNICODE;
30
use const JSON_UNESCAPED_SLASHES;
31
32
/**
33
 * List of packed boxes.
34
 */
35
class PackedBoxList implements IteratorAggregate, Countable, JsonSerializable
36
{
37
    /**
38
     * @var PackedBox[]
39
     */
40
    private array $list = [];
41
42
    private bool $isSorted = false;
43 58
44
    public function __construct(private readonly PackedBoxSorter $sorter = new DefaultPackedBoxSorter())
45 58
    {
46
    }
47
48
    /**
49
     * @return Traversable<PackedBox>
50 35
     */
51
    public function getIterator(): Traversable
52 35
    {
53 35
        if (!$this->isSorted) {
54 35
            usort($this->list, $this->sorter->compare(...));
55
            $this->isSorted = true;
56
        }
57 35
58
        return new ArrayIterator($this->list);
59
    }
60
61
    /**
62
     * Number of items in list.
63 46
     */
64
    public function count(): int
65 46
    {
66
        return count($this->list);
67
    }
68 54
69
    public function insert(PackedBox $item): void
70 54
    {
71 54
        $this->list[] = $item;
72
        $this->isSorted = false;
73
    }
74
75
    /**
76
     * Do a bulk insert.
77
     *
78
     * @internal
79
     *
80
     * @param PackedBox[] $boxes
81 14
     */
82
    public function insertFromArray(array $boxes): void
83 14
    {
84 14
        foreach ($boxes as $box) {
85
            $this->insert($box);
86
        }
87
    }
88
89
    /**
90
     * @internal
91 11
     */
92
    public function top(): PackedBox
93 11
    {
94 10
        if (!$this->isSorted) {
95 10
            usort($this->list, $this->sorter->compare(...));
96
            $this->isSorted = true;
97
        }
98 11
99
        return reset($this->list);
100
    }
101
102
    /**
103
     * Calculate the average (mean) weight of the boxes.
104 15
     */
105
    public function getMeanWeight(): float
106 15
    {
107
        $meanWeight = 0;
108 15
109 15
        foreach ($this->list as $box) {
110
            $meanWeight += $box->getWeight();
111
        }
112 15
113
        return $meanWeight / count($this->list);
114
    }
115
116
    /**
117
     * Calculate the average (mean) weight of the boxes.
118 13
     */
119
    public function getMeanItemWeight(): float
120 13
    {
121
        $meanWeight = 0;
122 13
123 13
        foreach ($this->list as $box) {
124
            $meanWeight += $box->getItemWeight();
125
        }
126 13
127
        return $meanWeight / count($this->list);
128
    }
129
130
    /**
131
     * Calculate the variance in weight between these boxes.
132 14
     */
133
    public function getWeightVariance(): float
134 14
    {
135
        $mean = $this->getMeanWeight();
136 14
137 14
        $weightVariance = 0;
138 14
        foreach ($this->list as $box) {
139
            $weightVariance += ($box->getWeight() - $mean) ** 2;
140
        }
141 14
142
        return round($weightVariance / count($this->list), 1);
143
    }
144
145
    /**
146
     * Get volume utilisation of the set of packed boxes.
147 1
     */
148
    public function getVolumeUtilisation(): float
149 1
    {
150 1
        $itemVolume = 0;
151
        $boxVolume = 0;
152 1
153 1
        foreach ($this as $box) {
154
            $boxVolume += $box->getInnerVolume();
155 1
156 1
            foreach ($box->items as $item) {
157
                $itemVolume += ($item->item->getWidth() * $item->item->getLength() * $item->item->getDepth());
158
            }
159
        }
160 1
161
        return round($itemVolume / $boxVolume * 100, 1);
162
    }
163
164
    /**
165
     * Create a custom website visualiser URL for this packing.
166 1
     */
167
    public function generateVisualisationURL(): string
168 1
    {
169 1
        $items = [];
170 1
        foreach ($this->list as $packedBox) {
171
            $items = [...$items, ...$packedBox->items->asItemArray()];
172 1
        }
173 1
        $dedupedItems = $splIdToIntMap = [];
174 1
        $splIdIndex = 0;
175 1
        foreach ($items as $item) {
176 1
            if (!isset($splIdToIntMap[spl_object_id($item)])) {
177
                $splIdToIntMap[spl_object_id($item)] = $splIdIndex++;
178 1
            }
179
            $dedupedItems[$splIdToIntMap[spl_object_id($item)]] = $item;
180
        }
181 1
182 1
        foreach ($dedupedItems as $item) {
183
            $data['items'][$splIdToIntMap[spl_object_id($item)]] = [$item->getDescription(), $item->getWidth(), $item->getLength(), $item->getDepth()];
184
        }
185 1
186 1
        $data['boxes'] = [];
187 1
        foreach ($this->list as $packedBox) {
188 1
            $data['boxes'][] = [
189 1
                $packedBox->box->getReference(),
190 1
                $packedBox->box->getInnerWidth(),
191 1
                $packedBox->box->getInnerLength(),
192 1
                $packedBox->box->getInnerDepth(),
193 1
                array_map(
194 1
                    fn (PackedItem $item) => [$splIdToIntMap[spl_object_id($item->item)], $item->x, $item->y, $item->z, $item->width, $item->length, $item->depth],
195 1
                    iterator_to_array($packedBox->items)
196 1
                ),
197
            ];
198
        }
199 1
200
        return 'https://boxpacker.io/en/master/visualiser.html?packing=' . json_encode($data, flags: JSON_THROW_ON_ERROR | JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data seems to be defined by a foreach iteration on line 182. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
201
    }
202 1
203
    public function jsonSerialize(): array
204 1
    {
205
        return $this->list;
206
    }
207
}
208