Passed
Push — master ( 9957e8...ede80c )
by Doug
02:03
created

src/PackedBoxList.php (1 issue)

Labels
Severity
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 Countable;
13
use IteratorAggregate;
14
use Traversable;
15
16
/**
17
 * List of packed boxes.
18
 *
19
 * @author Doug Wright
20
 */
21
class PackedBoxList implements IteratorAggregate, Countable
22
{
23
    /**
24
     * List containing boxes.
25
     *
26
     * @var Box[]
27
     */
28
    private $list = [];
29
30
    /**
31
     * Has this list already been sorted?
32
     *
33
     * @var bool
34
     */
35
    private $isSorted = false;
36
37
    /**
38
     * @return Traversable
39
     */
40 8
    public function getIterator(): Traversable
41
    {
42 8
        if (!$this->isSorted) {
43 8
            usort($this->list, [$this, 'compare']);
44 8
            $this->isSorted = true;
45
        }
46
47 8
        return new ArrayIterator($this->list);
48
    }
49
50
    /**
51
     * Number of items in list.
52
     *
53
     * @return int
54
     */
55 9
    public function count(): int
56
    {
57 9
        return count($this->list);
58
    }
59
60
    /**
61
     * @param PackedBox $item
62
     */
63 12
    public function insert(PackedBox $item)
64
    {
65 12
        $this->list[] = $item;
66 12
    }
67
68
    /**
69
     * Do a bulk insert.
70
     *
71
     * @internal
72
     *
73
     * @param PackedBox[] $boxes
74
     */
75 2
    public function insertFromArray(array $boxes): void
76
    {
77 2
        foreach ($boxes as $box) {
78 2
            $this->insert($box);
79
        }
80 2
    }
81
82
    /**
83
     * @internal
84
     *
85
     * @return PackedBox
86
     */
87 1
    public function top(): PackedBox
88
    {
89 1
        if (!$this->isSorted) {
90 1
            usort($this->list, [$this, 'compare']);
91 1
            $this->isSorted = true;
92
        }
93
94 1
        return reset($this->list);
95
    }
96
97
    /**
98
     * @param PackedBox $boxA
99
     * @param PackedBox $boxB
100
     *
101
     * @return int
102
     */
103 2
    private function compare(PackedBox $boxA, PackedBox $boxB): int
104
    {
105 2
        $choice = $boxB->getItems()->count() <=> $boxA->getItems()->count();
106 2
        if ($choice === 0) {
107 2
            $choice = $boxB->getInnerVolume() <=> $boxA->getInnerVolume();
108
        }
109 2
        if ($choice === 0) {
110 2
            $choice = $boxA->getWeight() <=> $boxB->getWeight();
111
        }
112
113 2
        return $choice;
114
    }
115
116
    /**
117
     * Calculate the average (mean) weight of the boxes.
118
     *
119
     * @return float
120
     */
121 3
    public function getMeanWeight(): float
122
    {
123 3
        $meanWeight = 0;
124
125
        /** @var PackedBox $box */
126 3
        foreach ($this->list as $box) {
127 3
            $meanWeight += $box->getWeight();
0 ignored issues
show
The method getWeight() does not exist on DVDoug\BoxPacker\Box. Did you maybe mean getMaxWeight()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

127
            $meanWeight += $box->/** @scrutinizer ignore-call */ getWeight();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
128
        }
129
130 3
        return $meanWeight / count($this->list);
131
    }
132
133
    /**
134
     * Calculate the variance in weight between these boxes.
135
     *
136
     * @return float
137
     */
138 2
    public function getWeightVariance(): float
139
    {
140 2
        $mean = $this->getMeanWeight();
141
142 2
        $weightVariance = 0;
143
        /** @var PackedBox $box */
144 2
        foreach ($this->list as $box) {
145 2
            $weightVariance += ($box->getWeight() - $mean) ** 2;
146
        }
147
148 2
        return round($weightVariance / count($this->list), 1);
149
    }
150
151
    /**
152
     * Get volume utilisation of the set of packed boxes.
153
     *
154
     * @return float
155
     */
156 1
    public function getVolumeUtilisation(): float
157
    {
158 1
        $itemVolume = 0;
159 1
        $boxVolume = 0;
160
161
        /** @var PackedBox $box */
162 1
        foreach ($this as $box) {
163 1
            $boxVolume += $box->getInnerVolume();
164
165
            /** @var PackedItem $item */
166 1
            foreach ($box->getItems() as $item) {
167 1
                $itemVolume += ($item->getItem()->getWidth() * $item->getItem()->getLength() * $item->getItem()->getDepth());
168
            }
169
        }
170
171 1
        return round($itemVolume / $boxVolume * 100, 1);
172
    }
173
}
174