File Coverage

lib/Template/Colour/HSV.pm
Criterion Covered Total %
statement 93 138 67.3
branch 51 90 56.6
condition 3 9 33.3
subroutine 10 16 62.5
pod 1 15 6.6
total 158 268 58.9


line stmt bran cond sub pod time code
1             package Template::Colour::HSV;
2              
3             use Template::Colour::Class
4 3         47 version => 2.10,
5             debug => 0,
6             base => 'Template::Colour',
7             constants => 'ARRAY HASH SCHEME :HSV',
8             utils => 'is_object',
9             as_text => 'HTML',
10             is_true => 1,
11             throws => 'Colour.HSV',
12             methods => {
13             sat => \&saturation,
14             val => \&value,
15 3     3   19 };
  3         6  
16              
17              
18             sub new {
19 53     53 1 2784 my ($proto, @args) = @_;
20 53         61 my ($class, $self);
21              
22 53 100       124 if ($class = ref $proto) {
23 4         14 $self = bless [@$proto], $class;
24             }
25             else {
26 49         169 $self = bless [0, 0, 0], $proto;
27             }
28 53 50       214 $self->hsv(@args) if @args;
29 51         385 return $self;
30             }
31              
32             sub copy {
33 4     4 0 19 my $self = shift;
34 4 100 66     22 my $args = @_ && ref $_[0] eq HASH ? shift : { @_ };
35              
36             # default HSV to $self values. Note that we use the longer
37             # form of 'saturation' and 'value', allowing the user to
38             # specify the shorter form of 'sat' or 'val' which gets
39             # detected before the longer 'saturation' and 'value' in
40             # the hsv() method below
41             $args->{ hue } = $self->[HUE]
42 4 100       22 unless defined $args->{ hue };
43             $args->{ saturation } = $self->[SAT]
44 4 50       20 unless defined $args->{ saturation };
45             $args->{ value } = $self->[VAL]
46 4 50       18 unless defined $args->{ value };
47              
48 4         11 $self->new($args);
49             }
50              
51             sub hsv {
52 53     53 0 72 my $self = shift;
53 53         61 my $hsv;
54              
55 53 100       166 if (@_ == 1) {
    100          
    50          
    50          
56             # single argument is a list or hash ref
57 9         14 $hsv = shift;
58             }
59             elsif (@_ == 3) {
60             # three arguments provide hue, saturation, and value components
61 42         115 $hsv = [ @_ ];
62             }
63             elsif (@_ == 6) {
64             # list of six items is hue => $h, saturation => $s, value => $v
65 0         0 $hsv = { @_ };
66             }
67             elsif (@_) {
68             # any other number of arguments is an error
69 2         34 return $self->error_msg( bad_param => hsv => join(', ', @_) );
70             }
71             else {
72             # return $self when called with no arguments
73 0         0 return $self;
74             }
75              
76             # at this point $hsv is a reference to a list or hash, or hsv value
77              
78 51 100       212 if (UNIVERSAL::isa($hsv, HASH)) {
    50          
79             # convert hash ref to list
80 6 100       22 $hsv->{ sat } = $hsv->{ saturation } unless exists $hsv->{ sat };
81 6 100       21 $hsv->{ val } = $hsv->{ value } unless exists $hsv->{ val };
82 18 50       51 $hsv = [ map {
83 6         11 defined $hsv->{ $_ }
84             ? $hsv->{ $_ }
85             : return $self->error_msg( no_param => hsv => $_ );
86             } qw( hue sat val ) ];
87             }
88             elsif (UNIVERSAL::isa($hsv, ARRAY)) {
89             # $hsv list is ok as it is
90             }
91             else {
92             # anything else is Not Allowed
93 0         0 return $self->error_msg( bad_param => hsv => $hsv );
94             }
95              
96 51         142 $self->hue($hsv->[HUE]);
97 51         143 $self->sat($hsv->[SAT]);
98 51         127 $self->val($hsv->[VAL]);
99              
100 51         83 return $self;
101             }
102              
103             sub hue {
104 62     62 0 562 my $self = shift;
105 62 100       113 if (@_) {
106 52         80 my $hue = shift;
107 52         612 $self->[HUE] = $hue % 360;
108 52         83 delete $self->[SCHEME];
109             }
110 62         116 return $self->[HUE];
111             }
112              
113             sub saturation {
114 80     80 0 162 my $self = shift;
115 80 100       169 if (@_) {
116 72         72 my $sat = shift;
117 72 50       132 $sat = 0 if $sat < 0;
118 72 100       120 $sat = 255 if $sat > 255;
119 72         98 $self->[SAT] = $sat;
120 72         85 delete $self->[SCHEME];
121             }
122 80         123 return $self->[SAT];
123             }
124              
125             sub value {
126 80     80 0 171 my $self = shift;
127 80 100       147 if (@_) {
128 72         68 my $val = shift;
129 72 50       163 $val = 0 if $val < 0;
130 72 100       116 $val = 255 if $val > 255;
131 72         89 $self->[VAL] = $val;
132 72         81 delete $self->[SCHEME];
133             }
134 80         116 return $self->[VAL];
135             }
136              
137             sub update {
138 0     0 0 0 my $self = shift;
139 0 0 0     0 my $args = @_ && ref $_[0] eq HASH ? shift : { @_ };
140 0         0 my $value;
141            
142 0 0       0 $args->{ sat } = $args->{ saturation } unless exists $args->{ sat };
143 0 0       0 $args->{ val } = $args->{ value } unless exists $args->{ val };
144            
145             $self->hue($value)
146 0 0       0 if defined ($value = $args->{ hue });
147              
148             $self->saturation($value)
149 0 0       0 if defined ($value = $args->{ sat });
150            
151             $self->value($value)
152 0 0       0 if defined ($value = $args->{ val });
153              
154 0         0 delete $self->[SCHEME];
155 0         0 return $self;
156             }
157              
158             sub adjust {
159 21     21 0 23 my $self = shift;
160 21 50 33     113 my $args = @_ && ref $_[0] eq HASH ? shift : { @_ };
161 21         20 my $delta;
162            
163 21 50       46 $args->{ sat } = $args->{ saturation } unless exists $args->{ sat };
164 21 50       53 $args->{ val } = $args->{ value } unless exists $args->{ val };
165            
166 21 50       36 if ($delta = $args->{ hue }) {
167 0 0       0 $delta = int($delta * 3.59 + 0.5)
168             if $delta =~ s/(\d+)%$/$1/; # 0-100% -> 0-359
169 0         0 $self->hue($self->[HUE] + $delta)
170             }
171              
172 21 50       44 if ($delta = $args->{ sat }) {
173 21 50       139 $delta = int($delta * 2.55 + 0.5)
174             if $delta =~ s/(\d+)%$/$1/; # 0-100% -> 0-255
175 21         48 $self->sat($self->[SAT] + $delta);
176             }
177            
178 21 50       50 if ($delta = $args->{ val }) {
179 21 50       93 $delta = int($delta * 2.55 + 0.5)
180             if $delta =~ s/(\d+)%$/$1/; # 0-100% -> 0-255
181 21         46 $self->val($self->[VAL] + $delta);
182             }
183              
184 21         22 delete $self->[SCHEME];
185 21         125 return $self;
186             }
187              
188             sub rgb {
189 21     21 0 551 my ($self, @args) = @_;
190 21         25 my $rgb;
191              
192             # generate RGB values from current HSV if no arguments provided
193 21 50       53 unless (@args) {
194 21         43 my ($h, $s, $v) = @$self;
195 21         29 my ($r, $g, $b);
196              
197 21 100       44 if ($s == 0) {
198             # TODO: make this truly achromatic
199 4         13 @args = ($v) x 3;
200             }
201             else {
202             # normalise saturation from range 0-255 to 0-1
203 17         32 $s /= 255;
204              
205 17         19 $h /= 60; ## sector 0 to 5
206 17         83 my $i = POSIX::floor( $h );
207 17         30 my $f = $h - $i; ## factorial part of h
208 17         40 my $p = $v * ( 1 - $s );
209 17         33 my $q = $v * ( 1 - $s * $f );
210 17         31 my $t = $v * ( 1 - $s * ( 1 - $f ) );
211              
212 17 100       61 if ($i == 0) { $r = $v; $g = $t; $b = $p }
  7 50       11  
  7 50       11  
  7 50       11  
    0          
213 0         0 elsif ($i == 1) { $r = $q; $g = $v; $b = $p }
  0         0  
  0         0  
214 0         0 elsif ($i == 2) { $r = $p; $g = $v; $b = $t }
  0         0  
  0         0  
215 10         14 elsif ($i == 3) { $r = $p; $g = $q; $b = $v }
  10         12  
  10         19  
216 0         0 elsif ($i == 4) { $r = $t; $g = $p; $b = $v }
  0         0  
  0         0  
217 0         0 else { $r = $v; $g = $p; $b = $q }
  0         0  
  0         0  
218              
219 17         40 @args = map { int } ($r, $g, $b);
  51         111  
220             }
221             }
222              
223 21         94 return $self->RGB(@args);
224             }
225              
226             sub hex {
227 0     0 0 0 my $self = shift;
228 0         0 $self->rgb->hex;
229             }
230              
231             sub HEX {
232 0     0 0 0 my $self = shift;
233 0         0 $self->rgb->HEX;
234             }
235              
236             sub html {
237 0     0 0 0 my $self = shift;
238 0         0 $self->rgb->html;
239             }
240              
241             sub HTML {
242 0     0 0 0 my $self = shift;
243 0         0 $self->rgb->HTML;
244             }
245              
246             sub range {
247 0     0 0 0 my $self = shift;
248 0         0 my $steps = shift;
249 0         0 my $target = $self->SUPER::new(@_)->hsv();
250 0         0 my $dhue = ($target->[HUE] - $self->[HUE]) / $steps;
251 0         0 my $dsat = ($target->[SAT] - $self->[SAT]) / $steps;
252 0         0 my $dval = ($target->[VAL] - $self->[VAL]) / $steps;
253 0         0 my ($n, @range);
254            
255 0         0 for ($n = 0; $n <= $steps; $n++) {
256 0         0 push(@range, $self->copy->adjust({
257             hue => $dhue * $n,
258             sat => $dsat * $n,
259             val => $dval * $n,
260             }));
261             }
262 0 0       0 return wantarray ? @range : \@range;
263             }
264              
265             sub percent {
266 1     1 0 3 my $self = shift;
267 1         9 sprintf(
268             '%d/%d%%/%d%%',
269             $self->[HUE],
270             $self->[SAT] * 100 / 255,
271             $self->[VAL] * 100 / 255,
272             );
273             }
274              
275             1;
276              
277             __END__