Passed
Pull Request — master (#294)
by
unknown
18:38 queued 14:21
created

PackedBoxList::getVolumeUtilisation()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 7
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 14
ccs 0
cts 8
cp 0
crap 12
rs 10
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 ArrayIterator;
12
use function count;
13
use Countable;
14
use IteratorAggregate;
15
use function json_encode;
16
use JsonSerializable;
17
use function reset;
18
use function round;
19
use Traversable;
20
use function urlencode;
21
use function usort;
22
23
/**
24
 * List of packed boxes.
25
 */
26
class PackedBoxList implements IteratorAggregate, Countable, JsonSerializable
27
{
28
    /** @var PackedBox[] */
29
    private array $list = [];
30
31
    private bool $isSorted = false;
32
33
    private PackedBoxSorter $sorter;
34
35 24
    public function __construct(?PackedBoxSorter $sorter = null)
36
    {
37 24
        $this->sorter = $sorter ?: new DefaultPackedBoxSorter();
38
    }
39
40
    /**
41
     * @return Traversable<PackedBox>
42
     */
43 24
    public function getIterator(): Traversable
44
    {
45 24
        if (!$this->isSorted) {
46 24
            usort($this->list, [$this->sorter, 'compare']);
47 24
            $this->isSorted = true;
48
        }
49
50 24
        return new ArrayIterator($this->list);
51
    }
52
53
    /**
54
     * Number of items in list.
55
     */
56 24
    public function count(): int
57
    {
58 24
        return count($this->list);
59
    }
60
61 22
    public function insert(PackedBox $item): void
62
    {
63 22
        $this->list[] = $item;
64 22
        $this->isSorted = false;
65
    }
66
67
    /**
68
     * Do a bulk insert.
69
     *
70
     * @internal
71
     *
72
     * @param PackedBox[] $boxes
73
     */
74 4
    public function insertFromArray(array $boxes): void
75
    {
76 4
        foreach ($boxes as $box) {
77 4
            $this->insert($box);
78
        }
79
    }
80
81
    /**
82
     * @internal
83
     */
84
    public function top(): PackedBox
85
    {
86
        if (!$this->isSorted) {
87
            usort($this->list, [$this->sorter, 'compare']);
88
            $this->isSorted = true;
89
        }
90
91
        return reset($this->list);
92
    }
93
94
    /**
95
     * Calculate the average (mean) weight of the boxes.
96
     */
97 4
    public function getMeanWeight(): float
98
    {
99 4
        $meanWeight = 0;
100
101 4
        foreach ($this->list as $box) {
102 4
            $meanWeight += $box->getWeight();
103
        }
104
105 4
        return $meanWeight / count($this->list);
106
    }
107
108
    /**
109
     * Calculate the average (mean) weight of the boxes.
110
     */
111 4
    public function getMeanItemWeight(): float
112
    {
113 4
        $meanWeight = 0;
114
115 4
        foreach ($this->list as $box) {
116 4
            $meanWeight += $box->getItemWeight();
117
        }
118
119 4
        return $meanWeight / count($this->list);
120
    }
121
122
    /**
123
     * Calculate the variance in weight between these boxes.
124
     */
125 4
    public function getWeightVariance(): float
126
    {
127 4
        $mean = $this->getMeanWeight();
128
129 4
        $weightVariance = 0;
130 4
        foreach ($this->list as $box) {
131 4
            $weightVariance += ($box->getWeight() - $mean) ** 2;
132
        }
133
134 4
        return round($weightVariance / count($this->list), 1);
135
    }
136
137
    /**
138
     * Get volume utilisation of the set of packed boxes.
139
     */
140
    public function getVolumeUtilisation(): float
141
    {
142
        $itemVolume = 0;
143
        $boxVolume = 0;
144
145
        foreach ($this as $box) {
146
            $boxVolume += $box->getInnerVolume();
147
148
            foreach ($box->getItems() as $item) {
149
                $itemVolume += ($item->getItem()->getWidth() * $item->getItem()->getLength() * $item->getItem()->getDepth());
150
            }
151
        }
152
153
        return round($itemVolume / $boxVolume * 100, 1);
154
    }
155
156
    /**
157
     * Create a custom website visualiser URL for this packing.
158
     */
159
    public function generateVisualisationURL(): string
160
    {
161
        return 'https://boxpacker.io/en/master/visualiser.html?packing=' . urlencode(json_encode($this));
162
    }
163
164
    public function jsonSerialize(): array
165
    {
166
        return $this->list;
167
    }
168
}
169