line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package FuseBead::From::PNG; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
439
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
22
|
|
4
|
1
|
|
|
1
|
|
3
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
25
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
BEGIN { |
7
|
1
|
|
|
1
|
|
13
|
$FuseBead::From::PNG::VERSION = '0.02'; |
8
|
|
|
|
|
|
|
} |
9
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
183
|
use Image::PNG::Libpng qw(:all); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
use Image::PNG::Const qw(:all); |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use FuseBead::From::PNG::Const qw(:all); |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
use FuseBead::From::PNG::Bead; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use Data::Debug; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
use Memoize; |
20
|
|
|
|
|
|
|
memoize('_find_bead_color', INSTALL => '_find_bead_color_fast'); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
sub new { |
23
|
|
|
|
|
|
|
my $class = shift; |
24
|
|
|
|
|
|
|
my %args = ref $_[0] eq 'HASH' ? %{$_[0]} : @_; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $hash = {}; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$hash->{'filename'} = $args{'filename'}; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
$hash->{'unit_size'} = $args{'unit_size'} || 1; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# mirror plans compared to image by default |
33
|
|
|
|
|
|
|
$hash->{'mirror'} = defined $args{'mirror'} ? $args{'mirror'} |
34
|
|
|
|
|
|
|
? 1 |
35
|
|
|
|
|
|
|
: 0 |
36
|
|
|
|
|
|
|
: 1; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# White list default |
39
|
|
|
|
|
|
|
$hash->{'whitelist'} = ($args{'whitelist'} && ref($args{'whitelist'}) eq 'ARRAY' && scalar(@{$args{'whitelist'}}) > 0) ? $args{'whitelist'} : undef; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# Black list default |
42
|
|
|
|
|
|
|
$hash->{'blacklist'} = ($args{'blacklist'} && ref($args{'blacklist'}) eq 'ARRAY' && scalar(@{$args{'blacklist'}}) > 0) ? $args{'blacklist'} : undef; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
my $self = bless ($hash, ref ($class) || $class); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
return $self; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub bead_dimensions { |
50
|
|
|
|
|
|
|
my $self = shift; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
return $self->{'bead_dimensions'} ||= do { |
53
|
|
|
|
|
|
|
my $hash = {}; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
for my $type (qw/imperial metric/) { |
56
|
|
|
|
|
|
|
my $bead_diameter = |
57
|
|
|
|
|
|
|
FuseBead::From::PNG::Const->BEAD_DIAMETER |
58
|
|
|
|
|
|
|
* ($type eq 'imperial' ? FuseBead::From::PNG::Const->MILLIMETER_TO_INCH : 1); |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
$hash->{$type} = { |
61
|
|
|
|
|
|
|
bead_diameter => $bead_diameter, |
62
|
|
|
|
|
|
|
}; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
$hash; |
66
|
|
|
|
|
|
|
}; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub bead_colors { |
70
|
|
|
|
|
|
|
my $self = shift; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
return $self->{'bead_colors'} ||= do { |
73
|
|
|
|
|
|
|
my $hash = {}; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
for my $color ( BEAD_COLORS ) { |
76
|
|
|
|
|
|
|
my ($n_key, $hex_key, $r_key, $g_key, $b_key) = ( |
77
|
|
|
|
|
|
|
$color . '_NAME', |
78
|
|
|
|
|
|
|
$color . '_HEX_COLOR', |
79
|
|
|
|
|
|
|
$color . '_RGB_COLOR_RED', |
80
|
|
|
|
|
|
|
$color . '_RGB_COLOR_GREEN', |
81
|
|
|
|
|
|
|
$color . '_RGB_COLOR_BLUE', |
82
|
|
|
|
|
|
|
); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
no strict 'refs'; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
$hash->{ $color } = { |
87
|
|
|
|
|
|
|
'cid' => $color, |
88
|
|
|
|
|
|
|
'name' => FuseBead::From::PNG::Const->$n_key, |
89
|
|
|
|
|
|
|
'hex_color' => FuseBead::From::PNG::Const->$hex_key, |
90
|
|
|
|
|
|
|
'rgb_color' => [ |
91
|
|
|
|
|
|
|
FuseBead::From::PNG::Const->$r_key, |
92
|
|
|
|
|
|
|
FuseBead::From::PNG::Const->$g_key, |
93
|
|
|
|
|
|
|
FuseBead::From::PNG::Const->$b_key, |
94
|
|
|
|
|
|
|
], |
95
|
|
|
|
|
|
|
}; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
$hash; |
99
|
|
|
|
|
|
|
}; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub beads { |
103
|
|
|
|
|
|
|
my $self = shift; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
return $self->{'beads'} ||= do { |
106
|
|
|
|
|
|
|
my $hash = {}; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
for my $color ( BEAD_COLORS ) { |
109
|
|
|
|
|
|
|
my $bead = FuseBead::From::PNG::Bead->new( color => $color ); |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
$hash->{ $bead->identifier } = $bead; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
$hash; |
115
|
|
|
|
|
|
|
}; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub png { |
119
|
|
|
|
|
|
|
my $self = shift; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
return $self->{'png'} ||= do { |
122
|
|
|
|
|
|
|
my $png = read_png_file($self->{'filename'}, transforms => PNG_TRANSFORM_STRIP_ALPHA); |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
$png; |
125
|
|
|
|
|
|
|
}; |
126
|
|
|
|
|
|
|
}; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub png_info { |
129
|
|
|
|
|
|
|
my $self = shift; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
return $self->{'png_info'} ||= $self->png->get_IHDR; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub bead_row_length { |
135
|
|
|
|
|
|
|
my $self = shift; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
return $self->{'bead_row_length'} ||= $self->png_info->{'width'} / $self->{'unit_size'}; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub bead_col_height { |
141
|
|
|
|
|
|
|
my $self = shift; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
return $self->{'bead_col_height'} ||= $self->png_info->{'height'} / $self->{'unit_size'}; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub process { |
147
|
|
|
|
|
|
|
my $self = shift; |
148
|
|
|
|
|
|
|
my %args = ref $_[0] eq 'HASH' ? %{$_[0]} : @_; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
my $tally = { |
151
|
|
|
|
|
|
|
beads => {}, |
152
|
|
|
|
|
|
|
plan => [], |
153
|
|
|
|
|
|
|
}; |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
if ($self->{'filename'}) { |
156
|
|
|
|
|
|
|
my @blocks = $self->_png_blocks_of_color; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
my @units = $self->_approximate_bead_colors( blocks => \@blocks ); |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
my @beads = $self->_generate_bead_list(units => \@units); |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
$tally->{'plan'} = [ map { $_->flatten } @beads ]; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
my %list; |
165
|
|
|
|
|
|
|
for my $bead (@beads) { |
166
|
|
|
|
|
|
|
if(! exists $list{ $bead->identifier }) { |
167
|
|
|
|
|
|
|
$list{ $bead->identifier } = $bead->flatten; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
delete $list{ $bead->identifier }{'meta'}; # No need for meta in bead list |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
$list{ $bead->identifier }{'quantity'} = 1; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
else { |
174
|
|
|
|
|
|
|
$list{ $bead->identifier }{'quantity'}++; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
$tally->{'beads'} = \%list; |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
$tally->{'info'} = $self->_plan_info(); |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
if ($args{'view'}) { |
184
|
|
|
|
|
|
|
my $view = $args{'view'}; |
185
|
|
|
|
|
|
|
my $module = "FuseBead::From::PNG::View::$view"; |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
$tally = eval { |
188
|
|
|
|
|
|
|
(my $file = $module) =~ s|::|/|g; |
189
|
|
|
|
|
|
|
require $file . '.pm'; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
$module->new($self)->print($tally); |
192
|
|
|
|
|
|
|
}; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
die "Failed to format as a view ($view). $@" if $@; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
return $tally; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub mirror { |
201
|
|
|
|
|
|
|
my $self = shift; |
202
|
|
|
|
|
|
|
my $arg = shift; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
if (defined $arg) { |
205
|
|
|
|
|
|
|
$self->{'mirror'} = $arg ? 1 : 0; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
return $self->{'mirror'}; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub whitelist { shift->{'whitelist'} } |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub has_whitelist { |
214
|
|
|
|
|
|
|
my $self = shift; |
215
|
|
|
|
|
|
|
my $allowed = shift; # arrayref listing filters we can use |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
my $found = 0; |
218
|
|
|
|
|
|
|
for my $filter ( values %{ $self->_list_filters($allowed) } ) { |
219
|
|
|
|
|
|
|
$found += scalar( grep { /$filter/ } @{ $self->whitelist || [] } ); |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
return $found; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub is_whitelisted { |
226
|
|
|
|
|
|
|
my $self = shift; |
227
|
|
|
|
|
|
|
my $val = shift; |
228
|
|
|
|
|
|
|
my $allowed = shift; # arrayref listing filters we can use |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
return 1 if ! $self->has_whitelist($allowed); # return true if there is no whitelist |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
for my $entry ( @{ $self->whitelist || [] } ) { |
233
|
|
|
|
|
|
|
for my $filter( values %{ $self->_list_filters($allowed) } ) { |
234
|
|
|
|
|
|
|
next unless $entry =~ /$filter/; # if there is at least a letter at the beginning then this entry has a color we can check |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
my $capture = $entry; |
237
|
|
|
|
|
|
|
$capture =~ s/$filter/$1/; |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
return 1 if $val eq $capture; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
return 0; # value is not in whitelist |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub blacklist { shift->{'blacklist'} } |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub has_blacklist { |
249
|
|
|
|
|
|
|
my $self = shift; |
250
|
|
|
|
|
|
|
my $allowed = shift; # optional filter restriction |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
my $found = 0; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
for my $filter ( values %{ $self->_list_filters($allowed) } ) { |
255
|
|
|
|
|
|
|
$found += scalar( grep { /$filter/ } @{ $self->blacklist || [] } ); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
return $found; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub is_blacklisted { |
262
|
|
|
|
|
|
|
my $self = shift; |
263
|
|
|
|
|
|
|
my $val = shift; |
264
|
|
|
|
|
|
|
my $allowed = shift; # optional filter restriction |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
return 0 if ! $self->has_blacklist($allowed); # return false if there is no blacklist |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
for my $entry ( @{ $self->blacklist || [] } ) { |
269
|
|
|
|
|
|
|
for my $filter( values %{ $self->_list_filters($allowed) } ) { |
270
|
|
|
|
|
|
|
next unless $entry =~ /$filter/; # if there is at least a letter at the beginning then this entry has a color we can check |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
my $capture = $1 || $entry; |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
return 1 if $val eq $capture; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
return 0; # value is not in blacklist |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub _png_blocks_of_color { |
282
|
|
|
|
|
|
|
my $self = shift; |
283
|
|
|
|
|
|
|
my %args = ref $_[0] eq 'HASH' ? %{$_[0]} : @_; |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
my @blocks; |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
return @blocks unless $self->{'filename'}; # No file, no blocks |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
my $pixel_bytecount = 3; |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
my $y = -1; |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
for my $pixel_row ( @{$self->png->get_rows} ) { |
294
|
|
|
|
|
|
|
$y++; |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
next unless ($y % $self->{'unit_size'}) == 0; |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
my $row = $y / $self->{'unit_size'}; # get actual row of blocks we are current on |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
my @values = unpack 'C*', $pixel_row; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
my $row_width = ( scalar(@values) / $pixel_bytecount ) / $self->{'unit_size'}; |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
for (my $col = 0; $col < $row_width; $col++) { |
305
|
|
|
|
|
|
|
my ($r, $g, $b) = ( |
306
|
|
|
|
|
|
|
$values[ ($self->{'unit_size'} * $pixel_bytecount * $col) ], |
307
|
|
|
|
|
|
|
$values[ ($self->{'unit_size'} * $pixel_bytecount * $col) + 1 ], |
308
|
|
|
|
|
|
|
$values[ ($self->{'unit_size'} * $pixel_bytecount * $col) + 2 ] |
309
|
|
|
|
|
|
|
); |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
$blocks[ ($row * $row_width) + $col ] = { |
312
|
|
|
|
|
|
|
r => $r, |
313
|
|
|
|
|
|
|
g => $g, |
314
|
|
|
|
|
|
|
b => $b, |
315
|
|
|
|
|
|
|
}; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
return @blocks; |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub _color_score { |
323
|
|
|
|
|
|
|
my $self = shift; |
324
|
|
|
|
|
|
|
my ($c1, $c2) = @_; |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
return abs( $c1->[0] - $c2->[0] ) + abs( $c1->[1] - $c2->[1] ) + abs( $c1->[2] - $c2->[2] ); |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub _find_bead_color { |
330
|
|
|
|
|
|
|
my $self = shift; |
331
|
|
|
|
|
|
|
my $rgb = [ @_ ]; |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
my @optimal_color = |
334
|
|
|
|
|
|
|
map { $_->{'cid'} } |
335
|
|
|
|
|
|
|
sort { $a->{'score'} <=> $b->{'score'} } |
336
|
|
|
|
|
|
|
map { |
337
|
|
|
|
|
|
|
+{ |
338
|
|
|
|
|
|
|
cid => $_->{'cid'}, |
339
|
|
|
|
|
|
|
score => $self->_color_score($rgb, $_->{'rgb_color'}), |
340
|
|
|
|
|
|
|
}; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
values %{ $self->bead_colors }; |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
my ($optimal_color) = grep { |
345
|
|
|
|
|
|
|
$self->is_whitelisted( $_, 'color' ) |
346
|
|
|
|
|
|
|
&& ! $self->is_blacklisted( $_, 'color' ) |
347
|
|
|
|
|
|
|
} @optimal_color; # first color in list that passes whitelist and blacklist should be the optimal color for tested block |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
return $optimal_color; |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub _approximate_bead_colors { |
353
|
|
|
|
|
|
|
my $self = shift; |
354
|
|
|
|
|
|
|
my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_; |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
die 'blocks not valid' unless $args{'blocks'} && ref( $args{'blocks'} ) eq 'ARRAY'; |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
my @colors; |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
for my $block (@{ $args{'blocks'} }) { |
361
|
|
|
|
|
|
|
push @colors, $self->_find_bead_color_fast( $block->{'r'}, $block->{'g'}, $block->{'b'} ); |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
return @colors; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub _generate_bead_list { |
368
|
|
|
|
|
|
|
my $self = shift; |
369
|
|
|
|
|
|
|
my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_; |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
die 'units not valid' unless $args{'units'} && ref( $args{'units'} ) eq 'ARRAY'; |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
my @beads = $self->_bead_list($args{'units'}); |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
return @beads; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub _bead_list { |
379
|
|
|
|
|
|
|
my $self = shift; |
380
|
|
|
|
|
|
|
my @units = ref($_[0]) eq 'ARRAY' ? @{ $_[0] } : @_; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
my $unit_count = scalar(@units); |
383
|
|
|
|
|
|
|
my $row_width = $self->bead_row_length; |
384
|
|
|
|
|
|
|
my $bead_ref = -1; # artificial auto-incremented id |
385
|
|
|
|
|
|
|
my @beads; |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
for (my $y = 0; $y < ($unit_count / $row_width); $y++) { |
388
|
|
|
|
|
|
|
my @row = splice @units, 0, $row_width; |
389
|
|
|
|
|
|
|
my $x = 0; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# mirror each row as it is set in the plan if we are mirroring the output |
392
|
|
|
|
|
|
|
@row = reverse @row if $self->mirror; |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
for my $color ( @row ) { |
395
|
|
|
|
|
|
|
push @beads, FuseBead::From::PNG::Bead->new( |
396
|
|
|
|
|
|
|
color => $color, |
397
|
|
|
|
|
|
|
meta => { |
398
|
|
|
|
|
|
|
x => $x, |
399
|
|
|
|
|
|
|
y => $y, |
400
|
|
|
|
|
|
|
ref => ++$bead_ref, |
401
|
|
|
|
|
|
|
}, |
402
|
|
|
|
|
|
|
); |
403
|
|
|
|
|
|
|
$x++; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
return @beads; |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
sub _list_filters { |
411
|
|
|
|
|
|
|
my $self = shift; |
412
|
|
|
|
|
|
|
my $allowed = $_[0] && ref($_[0]) eq 'ARRAY' ? $_[0] |
413
|
|
|
|
|
|
|
: ($_[0]) ? [ shift ] |
414
|
|
|
|
|
|
|
: []; # optional filter restriction |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
my $filters = { |
417
|
|
|
|
|
|
|
color => qr{^([A-Z_]+)$}i, |
418
|
|
|
|
|
|
|
bead => qr{^([A-Z_]+)$}i, |
419
|
|
|
|
|
|
|
}; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
$filters = +{ map { $_ => $filters->{$_} } @$allowed } if scalar @$allowed; |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
return $filters; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub _plan_info { |
427
|
|
|
|
|
|
|
my $self = shift; |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
my %info; |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
for my $type (qw/metric imperial/) { |
432
|
|
|
|
|
|
|
$info{$type} = { |
433
|
|
|
|
|
|
|
length => $self->bead_row_length * $self->bead_dimensions->{$type}->{'bead_diameter'}, |
434
|
|
|
|
|
|
|
height => $self->bead_col_height * $self->bead_dimensions->{$type}->{'bead_diameter'}, |
435
|
|
|
|
|
|
|
}; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
return \%info; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=pod |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=head1 NAME |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
FuseBead::From::PNG - Convert PNGs into plans to build a two dimensional fuse bead replica. |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=head1 SYNOPSIS |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
use FuseBead::From::PNG; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
my $object = FuseBead::From::PNG; |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
$object->bead_tally(); |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=head1 DESCRIPTION |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
Convert a PNG into a block list and plans to build a fuse bead replica of the PNG. This is for projects that use fuse bead such as perler or hama. |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
The RGB values where obtained from Bead List with RGB Values (https://docs.google.com/spreadsheets/d/1f988o68HDvk335xXllJD16vxLBuRcmm3vg6U9lVaYpA/edit#gid=0). |
460
|
|
|
|
|
|
|
Which was posted in the bead color subreddit beadsprites (https://www.reddit.com/r/beadsprites) under this post Bead List with RGB Values (https://www.reddit.com/r/beadsprites/comments/291495/bead_list_with_rgb_values/). |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
The generate_instructions.pl script under bin/ has been setup to optimally be used the 22k bucket of beads from Perler. (http://www.perler.com/22000-beads-multi-mix-_17000/17000.html) |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
$hash->{'filename'} = $args{'filename'}; |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
$hash->{'unit_size'} = $args{'unit_size'} || 1; |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# White list default |
469
|
|
|
|
|
|
|
$hash->{'whitelist'} = ($args{'whitelist'} && ref($args{'whitelist'}) eq 'ARRAY' && scalar(@{$args{'whitelist'}}) > 0) ? $args{'whitelist'} : undef; |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
# Black list default |
472
|
|
|
|
|
|
|
$hash->{'blacklist'} = ($args{'blacklist'} && ref($args{'blacklist'}) eq 'ARRAY' && scalar(@{$args{'blacklist'}}) > 0) ? $args{'blacklist'} : undef; |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=head1 USAGE |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
=head2 new |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Usage : ->new() |
479
|
|
|
|
|
|
|
Purpose : Returns FuseBead::From::PNG object |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Returns : FuseBead::From::PNG object |
482
|
|
|
|
|
|
|
Argument : |
483
|
|
|
|
|
|
|
filename - Optional. The file name of the PNG to process. Optional but if not provided, can't process the png. |
484
|
|
|
|
|
|
|
e.g. filename => '/location/of/the.png' |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
unit_size - Optional. The size of pixels squared to determine a single unit of a bead. Defaults to 1. |
487
|
|
|
|
|
|
|
e.g. unit_size => 2 # pixelated colors are 2x2 in size |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
whitelist - Optional. Array ref of colors, dimensions or color and dimensions that are allowed in the final plan output. |
490
|
|
|
|
|
|
|
e.g. whitelist => [ 'BLACK', 'WHITE', '1x1x1', '1x2x1', '1x4x1', 'BLACK_1x6x1' ] |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
blacklist - Optional. Array ref of colors, dimensions or color and dimensions that are not allowed in the final plan output. |
493
|
|
|
|
|
|
|
e.g. blacklist => [ 'RED', '1x10x1', '1x12x1', '1x16x1', 'BLUE_1x8x1' ] |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
Throws : |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
Comment : |
498
|
|
|
|
|
|
|
See Also : |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=head2 bead_dimensions |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
Usage : ->bead_dimensions() |
503
|
|
|
|
|
|
|
Purpose : returns a hashref with bead dimension information in millimeters (metric) or inches (imperial) |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
Returns : hashref with bead dimension information, millimeters is default |
506
|
|
|
|
|
|
|
Argument : $type - if set to imperial then dimension information is returned in inches |
507
|
|
|
|
|
|
|
Throws : |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
Comment : |
510
|
|
|
|
|
|
|
See Also : |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=head2 bead_colors |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
Usage : ->bead_colors() |
515
|
|
|
|
|
|
|
Purpose : returns bead color constants consolidated as a hash. |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Returns : hashref with color constants keyed by the official color name in key form. |
518
|
|
|
|
|
|
|
Argument : |
519
|
|
|
|
|
|
|
Throws : |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
Comment : |
522
|
|
|
|
|
|
|
See Also : |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head2 beads |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
Usage : ->beads() |
527
|
|
|
|
|
|
|
Purpose : Returns a list of all possible bead beads |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
Returns : Hash ref with L objects keyed by their identifier |
530
|
|
|
|
|
|
|
Argument : |
531
|
|
|
|
|
|
|
Throws : |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
Comment : |
534
|
|
|
|
|
|
|
See Also : |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=head2 png |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
Usage : ->png() |
539
|
|
|
|
|
|
|
Purpose : Returns Image::PNG::Libpng object. |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
Returns : Returns Image::PNG::Libpng object. See L for more details. |
542
|
|
|
|
|
|
|
Argument : |
543
|
|
|
|
|
|
|
Throws : |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
Comment : |
546
|
|
|
|
|
|
|
See Also : |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=head2 png_info |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
Usage : ->png_info() |
551
|
|
|
|
|
|
|
Purpose : Returns png IHDR info from the Image::PNG::Libpng object |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
Returns : A hash of values containing information abou the png such as width and height. See get_IHDR in L for more details. |
554
|
|
|
|
|
|
|
Argument : filename => the PNG to load and part |
555
|
|
|
|
|
|
|
unit_size => the pixel width and height of one unit, blocks are generally identified as Nx1 blocks where N is the number of units of the same color |
556
|
|
|
|
|
|
|
Throws : |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
Comment : |
559
|
|
|
|
|
|
|
See Also : |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=head2 bead_row_length |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
Usage : ->bead_row_length() |
564
|
|
|
|
|
|
|
Purpose : Return the width of one row of beads. Since a bead list is a single dimension array this is useful to figure out whict row a bead is on. |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
Returns : The length of a row of beads (image width / unit size) |
567
|
|
|
|
|
|
|
Argument : |
568
|
|
|
|
|
|
|
Throws : |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
Comment : |
571
|
|
|
|
|
|
|
See Also : |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=head2 bead_col_height |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
Usage : ->bead_col_height() |
576
|
|
|
|
|
|
|
Purpose : Return the height in beads. |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
Returns : The height of a col of beads (image height / unit size) |
579
|
|
|
|
|
|
|
Argument : |
580
|
|
|
|
|
|
|
Throws : |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
Comment : |
583
|
|
|
|
|
|
|
See Also : |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=head2 process |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
Usage : ->process() |
588
|
|
|
|
|
|
|
Purpose : Convert a provided PNG into a list of bead blocks that will allow building of a two dimensional bead replica. |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
Returns : Hashref containing information about particular bead beads found to be needed based on the provided PNG. |
591
|
|
|
|
|
|
|
Also included is the build order for those beads. |
592
|
|
|
|
|
|
|
Argument : view => 'a view' - optionally format the return data. options include: JSON and HTML |
593
|
|
|
|
|
|
|
Throws : |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
Comment : |
596
|
|
|
|
|
|
|
See Also : |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=head2 mirror |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
Usage : ->mirror() |
601
|
|
|
|
|
|
|
Purpose : Getter / Setter for the mirror option. Set to 1 (true) by default. This option will mirror the image when displaying it as plans. The reason is then the mirror of the image is what is placed on the peg board so that side can be ironed and, when turned over, the image is represented in it's proper orientation. |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
Returns : Either 1 or 0 |
604
|
|
|
|
|
|
|
Argument : a true or false value that will set whether the plans are mirrored to the image or not |
605
|
|
|
|
|
|
|
Throws : |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
Comment : |
608
|
|
|
|
|
|
|
See Also : |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=head2 whitelist |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
Usage : ->whitelist() |
613
|
|
|
|
|
|
|
Purpose : return any whitelist settings stored in this object |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
Returns : an arrayref of whitelisted colors and/or blocks, or undef |
616
|
|
|
|
|
|
|
Argument : |
617
|
|
|
|
|
|
|
Throws : |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
Comment : |
620
|
|
|
|
|
|
|
See Also : |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head2 has_whitelist |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
Usage : ->has_whitelist(), ->has_whitelist($filter) |
625
|
|
|
|
|
|
|
Purpose : return a true value if there is a whitelist with at least one entry in it based on the allowed filters, otherwise a false value is returned |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
Returns : 1 or 0 |
628
|
|
|
|
|
|
|
Argument : $filter - optional scalar containing the filter to restrict test to |
629
|
|
|
|
|
|
|
Throws : |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
Comment : |
632
|
|
|
|
|
|
|
See Also : |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=head2 is_whitelisted |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
Usage : ->is_whitelisted($value), ->is_whitelisted($value, $filter) |
637
|
|
|
|
|
|
|
Purpose : return a true if the value is whitelisted, otherwise false is returned |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
Returns : 1 or 0 |
640
|
|
|
|
|
|
|
Argument : $value - the value to test, $filter - optional scalar containing the filter to restrict test to |
641
|
|
|
|
|
|
|
Throws : |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
Comment : |
644
|
|
|
|
|
|
|
See Also : |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
=head2 blacklist |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
Usage : ->blacklist |
649
|
|
|
|
|
|
|
Purpose : return any blacklist settings stored in this object |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
Returns : an arrayref of blacklisted colors and/or blocks, or undef |
652
|
|
|
|
|
|
|
Argument : |
653
|
|
|
|
|
|
|
Throws : |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
Comment : |
656
|
|
|
|
|
|
|
See Also : |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=head2 has_blacklist |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
Usage : ->has_blacklist(), ->has_whitelist($filter) |
661
|
|
|
|
|
|
|
Purpose : return a true value if there is a blacklist with at least one entry in it based on the allowed filters, otherwise a false value is returned |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
Returns : 1 or 0 |
664
|
|
|
|
|
|
|
Argument : $filter - optional scalar containing the filter to restrict test to |
665
|
|
|
|
|
|
|
Throws : |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
Comment : |
668
|
|
|
|
|
|
|
See Also : |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=head2 is_blacklisted |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
Usage : ->is_blacklisted($value), ->is_whitelisted($value, $filter) |
673
|
|
|
|
|
|
|
Purpose : return a true if the value is blacklisted, otherwise false is returned |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
Returns : 1 or 0 |
676
|
|
|
|
|
|
|
Argument : $value - the value to test, $filter - optional scalar containing the filter to restrict test to |
677
|
|
|
|
|
|
|
Throws : |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Comment : |
680
|
|
|
|
|
|
|
See Also : |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
=head2 _png_blocks_of_color |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
Usage : ->_png_blocks_of_color() |
685
|
|
|
|
|
|
|
Purpose : Convert a provided PNG into a list of rgb values based on [row][color]. Size of blocks are determined by 'unit_size' |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
Returns : A list of hashes contain r, g and b values. e.g. ( { r => #, g => #, b => # }, { ... }, ... ) |
688
|
|
|
|
|
|
|
Argument : |
689
|
|
|
|
|
|
|
Throws : |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
Comment : |
692
|
|
|
|
|
|
|
See Also : |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=head2 _color_score |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
Usage : ->_color_score() |
697
|
|
|
|
|
|
|
Purpose : returns a score indicating the likeness of one color to another. The lower the number the closer the colors are to each other. |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
Returns : returns a positive integer score |
700
|
|
|
|
|
|
|
Argument : $c1 - array ref with rgb color values in that order |
701
|
|
|
|
|
|
|
$c2 - array ref with rgb color values in that order |
702
|
|
|
|
|
|
|
Throws : |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
Comment : |
705
|
|
|
|
|
|
|
See Also : |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
=head2 _find_bead_color |
708
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
Usage : ->_find_bead_color |
710
|
|
|
|
|
|
|
Purpose : given an rgb params, finds the optimal bead color |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
Returns : A bead color common name key that can then reference bead color information using L |
713
|
|
|
|
|
|
|
Argument : $r - the red value of a color |
714
|
|
|
|
|
|
|
$g - the green value of a color |
715
|
|
|
|
|
|
|
$b - the blue value of a color |
716
|
|
|
|
|
|
|
Throws : |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
Comment : this subroutine is memoized as the name _find_bead_color_fast |
719
|
|
|
|
|
|
|
See Also : |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
=head2 _approximate_bead_colors |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
Usage : ->_approximate_bead_colors() |
724
|
|
|
|
|
|
|
Purpose : Generate a list of bead colors based on a list of blocks ( array of hashes containing rgb values ) |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
Returns : A list of bead color common name keys that can then reference bead color information using L |
727
|
|
|
|
|
|
|
Argument : |
728
|
|
|
|
|
|
|
Throws : |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
Comment : |
731
|
|
|
|
|
|
|
See Also : |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
=head2 _generate_bead_list |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Usage : ->_approximate_bead_colors() |
736
|
|
|
|
|
|
|
Purpose : Generate a list of bead colors based on a list of blocks ( array of hashes containing rgb values ) for either knob orientation (calls _knob_forward_bead_list or _knob_up_bead_list) |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
Returns : A list of bead color common name keys that can then reference bead color information using L |
739
|
|
|
|
|
|
|
Argument : |
740
|
|
|
|
|
|
|
Throws : |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
Comment : |
743
|
|
|
|
|
|
|
See Also : |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
=head2 _bead_list |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
Usage : ->_bead_list() |
748
|
|
|
|
|
|
|
Purpose : Generate a list of bead colors based on a list of blocks ( array of hashes containing rgb values ) for knob up orientation |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
Returns : A list of bead color common name keys that can then reference bead color information using L |
751
|
|
|
|
|
|
|
Argument : |
752
|
|
|
|
|
|
|
Throws : |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
Comment : |
755
|
|
|
|
|
|
|
See Also : |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
=head2 _list_filters |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
Usage : ->_list_filters() |
760
|
|
|
|
|
|
|
Purpose : return whitelist/blacklist filters |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
Returns : an hashref of filters |
763
|
|
|
|
|
|
|
Argument : an optional filter restriction to limit set of filters returned to just one |
764
|
|
|
|
|
|
|
Throws : |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
Comment : |
767
|
|
|
|
|
|
|
See Also : |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
=head1 BUGS |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
=head1 SUPPORT |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=head1 AUTHOR |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
Travis Chase |
776
|
|
|
|
|
|
|
CPAN ID: GAUDEON |
777
|
|
|
|
|
|
|
gaudeon@cpan.org |
778
|
|
|
|
|
|
|
https://github.com/gaudeon/FuseBead-From-Png |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=head1 COPYRIGHT |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
This program is free software licensed under the... |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
The MIT License |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
The full text of the license can be found in the |
787
|
|
|
|
|
|
|
LICENSE file included with this module. |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
=head1 SEE ALSO |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
perl(1). |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
=cut |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
1; |