line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Box::Calc::Box; |
2
|
|
|
|
|
|
|
$Box::Calc::Box::VERSION = '1.0200'; |
3
|
9
|
|
|
9
|
|
84008
|
use strict; |
|
9
|
|
|
|
|
23
|
|
|
9
|
|
|
|
|
324
|
|
4
|
9
|
|
|
9
|
|
68
|
use warnings; |
|
9
|
|
|
|
|
16
|
|
|
9
|
|
|
|
|
269
|
|
5
|
9
|
|
|
9
|
|
1904
|
use Moose; |
|
9
|
|
|
|
|
888306
|
|
|
9
|
|
|
|
|
67
|
|
6
|
9
|
|
|
9
|
|
64283
|
use Storable qw(dclone); |
|
9
|
|
|
|
|
28992
|
|
|
9
|
|
|
|
|
782
|
|
7
|
|
|
|
|
|
|
with 'Box::Calc::Role::Container'; |
8
|
|
|
|
|
|
|
with 'Box::Calc::Role::Mailable'; |
9
|
9
|
|
|
9
|
|
5394
|
use Box::Calc::Layer; |
|
9
|
|
|
|
|
2825
|
|
|
9
|
|
|
|
|
339
|
|
10
|
9
|
|
|
9
|
|
7554
|
use Data::GUID; |
|
9
|
|
|
|
|
39824
|
|
|
9
|
|
|
|
|
47
|
|
11
|
9
|
|
|
9
|
|
1563
|
use List::Util qw/sum/; |
|
9
|
|
|
|
|
19
|
|
|
9
|
|
|
|
|
546
|
|
12
|
9
|
|
|
9
|
|
43
|
use Log::Any qw($log); |
|
9
|
|
|
|
|
17
|
|
|
9
|
|
|
|
|
89
|
|
13
|
9
|
|
|
9
|
|
1501
|
use Data::Dumper; |
|
9
|
|
|
|
|
17
|
|
|
9
|
|
|
|
|
7962
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 NAME |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Box::Calc::Box - The container in which we pack items. |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 VERSION |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
version 1.0200 |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 SYNOPSIS |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
my $box = Box::Calc::Box->new(name => 'Big Box', x => 12, y => 12, z => 18, weight => 20); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 METHODS |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head2 new(params) |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
Constructor. |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
B<NOTE:> All boxes automatically have one empty L<Box::Calc::Layer> added to them. |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=over |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=item params |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=over |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=item name |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
An identifying name for your box. |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=item x |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
The interior width of your box. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=item y |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
The interior length of your box. |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=item z |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
The interior thickness of your box. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=item weight |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
The weight of your box. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=back |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=back |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head2 fill_weight() |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
Returns the weight of the items in this box. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=cut |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
has fill_weight => ( |
72
|
|
|
|
|
|
|
is => 'rw', |
73
|
|
|
|
|
|
|
default => 0, |
74
|
|
|
|
|
|
|
isa => 'Num', |
75
|
|
|
|
|
|
|
); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head2 fill_x() |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Returns how full the box is in the C<x> dimension. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=cut |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub fill_x { |
84
|
21
|
|
|
21
|
1
|
38
|
my $self = shift; |
85
|
21
|
|
|
|
|
40
|
my $value = 0; |
86
|
21
|
|
|
|
|
41
|
foreach my $layer (@{$self->layers}) { |
|
21
|
|
|
|
|
1094
|
|
87
|
273
|
100
|
|
|
|
687
|
$value = $layer->fill_x if $layer->fill_x > $value; |
88
|
|
|
|
|
|
|
} |
89
|
21
|
|
|
|
|
145
|
return sprintf ("%.4f", $value); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=head2 fill_y() |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
Returns how full the box is in the C<y> dimension. |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=cut |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub fill_y { |
99
|
21
|
|
|
21
|
1
|
37
|
my $self = shift; |
100
|
21
|
|
|
|
|
37
|
my $value = 0; |
101
|
21
|
|
|
|
|
36
|
foreach my $layer (@{$self->layers}) { |
|
21
|
|
|
|
|
597
|
|
102
|
273
|
100
|
|
|
|
696
|
$value = $layer->fill_y if $layer->fill_y > $value; |
103
|
|
|
|
|
|
|
} |
104
|
21
|
|
|
|
|
126
|
return sprintf ("%.4f", $value); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=head2 fill_z() |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Returns how full the box is in the C<z> dimension. |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=cut |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub fill_z { |
114
|
18373
|
|
|
18373
|
1
|
25285
|
my $self = shift; |
115
|
18373
|
|
|
|
|
20352
|
my $value = 0; |
116
|
18373
|
|
|
|
|
20950
|
foreach my $layer (@{$self->layers}) { |
|
18373
|
|
|
|
|
505547
|
|
117
|
343897
|
|
|
|
|
843968
|
$value += $layer->fill_z; |
118
|
|
|
|
|
|
|
} |
119
|
18373
|
|
|
|
|
582921
|
return sprintf ("%.4f", $value); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head2 id() |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Returns a generated unique id for this box. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=cut |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
has id => ( |
129
|
|
|
|
|
|
|
is => 'ro', |
130
|
|
|
|
|
|
|
default => sub { Data::GUID->new->as_string }, |
131
|
|
|
|
|
|
|
); |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=head2 name() |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
Returns the name of the box. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=cut |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
has name => ( |
140
|
|
|
|
|
|
|
is => 'ro', |
141
|
|
|
|
|
|
|
isa => 'Str', |
142
|
|
|
|
|
|
|
required => 1, |
143
|
|
|
|
|
|
|
); |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=head2 layers() |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Returns an array reference of the L<Box::Calc::Layer>s in this box. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=cut |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
has layers => ( |
152
|
|
|
|
|
|
|
is => 'rw', |
153
|
|
|
|
|
|
|
isa => 'ArrayRef[Box::Calc::Layer]', |
154
|
|
|
|
|
|
|
default => sub { [] }, |
155
|
|
|
|
|
|
|
traits => ['Array'], |
156
|
|
|
|
|
|
|
handles => { |
157
|
|
|
|
|
|
|
count_layers => 'count', |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
); |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head2 void_weight() |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
Returns the weight assigned to the void space left in the box due to void space filler such as packing peanuts. Defaults to 70% of the box weight. |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=cut |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
has void_weight => ( |
168
|
|
|
|
|
|
|
is => 'rw', |
169
|
|
|
|
|
|
|
lazy => 1, |
170
|
|
|
|
|
|
|
default => sub { |
171
|
|
|
|
|
|
|
my $self = shift; |
172
|
|
|
|
|
|
|
return $self->weight * 0.7; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
); |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=head2 calculate_weight() |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Calculates and returns the weight of all the layers in this box, including the weight of this box and any packing filler (see L<void_weight>). |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=cut |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub calculate_weight { |
183
|
17842
|
|
|
17842
|
1
|
22735
|
my $self = shift; |
184
|
17842
|
|
|
|
|
543424
|
return $self->weight + $self->void_weight + $self->fill_weight; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 create_layer() |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
Adds a new L<Box::Calc::Layer> to this box. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=cut |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub create_layer { |
194
|
544
|
|
|
544
|
1
|
1226
|
my $self = shift; |
195
|
544
|
|
|
|
|
794
|
push @{$self->layers}, Box::Calc::Layer->new( max_x => $self->x, max_y => $self->y, ); |
|
544
|
|
|
|
|
15842
|
|
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub BUILD { |
199
|
34
|
|
|
34
|
0
|
50
|
my $self = shift; |
200
|
34
|
|
|
|
|
115
|
$self->create_layer; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head2 pack_item(item) |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Add a L<Box::Calc::Item> to this box. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Returns 1 on success or 0 on failure. |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=over |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=item item |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
The L<Box::Calc::Item> instance you want to add to this box. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=back |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=cut |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub pack_item { |
220
|
17820
|
|
|
17820
|
1
|
54095
|
my ($self, $item, $count) = @_; |
221
|
17820
|
|
100
|
|
|
53580
|
$count ||= 1; |
222
|
17820
|
50
|
|
|
|
34701
|
if ($count > 99) { |
223
|
0
|
|
|
|
|
0
|
$log->warn($item->{name}.' is causing infinite recursion in Box::Calc'); |
224
|
0
|
|
|
|
|
0
|
$log->debug(Dumper($item)); |
225
|
0
|
|
|
|
|
0
|
return 0; |
226
|
|
|
|
|
|
|
} |
227
|
17820
|
100
|
|
|
|
537229
|
if ($item->weight + $self->calculate_weight >= $self->max_weight) { |
228
|
1
|
|
|
|
|
8
|
$log->info($item->{name}.' would make this box weigh too much, requesting new box.'); |
229
|
1
|
|
|
|
|
28
|
return 0; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
# item height > ( box height - box fill + the height of the current layer ) |
232
|
17819
|
100
|
|
|
|
544966
|
if ($item->z > $self->z - $self->fill_z + $self->layers->[-1]->fill_z) { |
233
|
1
|
|
|
|
|
11
|
$log->info($item->{name}.' would make the layer too tall to fit in the box, requesting new box.'); |
234
|
1
|
|
|
|
|
33
|
return 0; |
235
|
|
|
|
|
|
|
} |
236
|
17818
|
100
|
|
|
|
535410
|
if ($self->layers->[-1]->pack_item($item)) { |
237
|
17287
|
|
|
|
|
520287
|
$self->fill_weight($self->fill_weight + $item->weight); |
238
|
17287
|
|
|
|
|
83792
|
return 1; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
else { |
241
|
531
|
100
|
|
|
|
17068
|
if ($item->z > $self->z - $self->fill_z) { |
242
|
21
|
|
|
|
|
131
|
$log->info($item->{name}.' is too big to create another layer in this box, requesting another box.'); |
243
|
21
|
|
|
|
|
373
|
return 0; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
else { |
246
|
510
|
|
|
|
|
1945
|
$self->create_layer; |
247
|
510
|
|
|
|
|
2523
|
return $self->pack_item($item, $count + 1); |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=head2 packing_list() |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
Returns a scalar with the weight of the box and a hash reference of all the items in this box. |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=cut |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub packing_list { |
259
|
15
|
|
|
15
|
1
|
38681
|
my $self = shift; |
260
|
15
|
|
|
|
|
447
|
my $weight = $self->weight; |
261
|
15
|
|
|
|
|
31
|
my $list = {}; |
262
|
15
|
|
|
|
|
20
|
foreach my $layer (@{$self->layers}) { |
|
15
|
|
|
|
|
404
|
|
263
|
264
|
|
|
|
|
689
|
$layer->packing_list(\$weight, $list) |
264
|
|
|
|
|
|
|
} |
265
|
15
|
|
|
|
|
64
|
return ($weight, $list); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=head2 packing_instructions() |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
Returns a description of the box. Example: |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
{ |
273
|
|
|
|
|
|
|
x => 5, |
274
|
|
|
|
|
|
|
y => 6, |
275
|
|
|
|
|
|
|
z => 3, |
276
|
|
|
|
|
|
|
fill_x => 4, |
277
|
|
|
|
|
|
|
fill_y => '5.1', |
278
|
|
|
|
|
|
|
fill_z => 2, |
279
|
|
|
|
|
|
|
name => 'The Big Box', |
280
|
|
|
|
|
|
|
layers => [ ... ], |
281
|
|
|
|
|
|
|
id => 'xxx', |
282
|
|
|
|
|
|
|
weight => '6', |
283
|
|
|
|
|
|
|
calculated_weight => '12.35', |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=cut |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub packing_instructions { |
289
|
20
|
|
|
20
|
1
|
43
|
my $self = shift; |
290
|
|
|
|
|
|
|
return { |
291
|
|
|
|
|
|
|
x => $self->x, |
292
|
|
|
|
|
|
|
y => $self->y, |
293
|
|
|
|
|
|
|
z => $self->z, |
294
|
|
|
|
|
|
|
fill_x => $self->fill_x, |
295
|
|
|
|
|
|
|
fill_y => $self->fill_y, |
296
|
|
|
|
|
|
|
fill_z => $self->fill_z, |
297
|
|
|
|
|
|
|
name => $self->name, |
298
|
|
|
|
|
|
|
id => $self->id, |
299
|
|
|
|
|
|
|
weight => $self->weight, |
300
|
|
|
|
|
|
|
calculated_weight => $self->calculate_weight, |
301
|
|
|
|
|
|
|
used_volume => $self->used_volume, |
302
|
|
|
|
|
|
|
volume => $self->volume, |
303
|
20
|
|
|
|
|
853
|
layers => [map { $_->packing_instructions } @{ $self->layers }], |
|
272
|
|
|
|
|
721
|
|
|
20
|
|
|
|
|
584
|
|
304
|
|
|
|
|
|
|
}; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub used_volume { |
308
|
20
|
|
|
20
|
0
|
31
|
my $self = shift; |
309
|
20
|
|
|
|
|
33
|
return sum map { $_->used_volume } @{ $self->layers }; |
|
272
|
|
|
|
|
722
|
|
|
20
|
|
|
|
|
530
|
|
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub volume { |
313
|
|
|
|
|
|
|
return $_[0]->fill_x * $_[0]->fill_y * $_[0]->fill_z; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
9
|
|
|
9
|
|
49
|
no Moose; |
|
9
|
|
|
|
|
18
|
|
|
9
|
|
|
|
|
78
|
|
317
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |