File Coverage

blib/lib/Color/TupleEncode/2Way.pm
Criterion Covered Total %
statement 61 67 91.0
branch 21 36 58.3
condition n/a
subroutine 12 12 100.0
pod n/a
total 94 115 81.7


line stmt bran cond sub pod time code
1             package Color::TupleEncode::2Way;
2              
3 8     8   45 use warnings FATAL=>"all";
  8         15  
  8         393  
4 8     8   46 use strict;
  8         16  
  8         240  
5              
6 8     8   43 use Carp;
  8         13  
  8         503  
7             #use Color::TupleEncode;
8              
9             # ................................................................
10             # If you need min/max/average
11             # use Math::VecStat qw(min max average);
12              
13             # ................................................................
14             # If you need floating point modulo
15             # use POSIX qw(fmod);
16              
17 8     8   45 use Readonly;
  8         42  
  8         8765  
18              
19             # use Smart::Comments;
20              
21             =head1 NAME
22              
23             Color::TupleEncode::2Way - a utility class for C that
24             implements color encoding of a 2-tuple C<(x,y)> to a color
25              
26             =head1 VERSION
27              
28             Version 0.11
29              
30             =cut
31              
32             our $VERSION = '0.11';
33              
34             =head1 SYNOPSIS
35              
36             This is a utility module used by L. To use
37             this module as the encoding method, pass the C directly or as an option in C or set with C.
38             C
39              
40             %options = (-method=>"Color::TupleEncode::2Way");
41            
42             $encoder = Color::TupleEncode(options=>\%options);
43              
44             # using the direct setter
45              
46             $encoder->set_method("Color::TupleEncode::2Way");
47            
48             # setting method as an option individually
49              
50             $convert->set_options(-method=>"Color::TupleEncode::2Way");
51              
52             This module is not designed to be used directly.
53              
54             =head1 ENCODING ALGORITHM
55              
56             This class encodes a 2-tuple C<(x,y)> to a HSV color C<(h,s,v)>. The following parameters are supported
57              
58             # for hue default
59             -hzero 180
60             -orientation 1
61              
62             # for saturation (e.g. -saturation=>{power=>1,rmin=>0}
63             -rmin 0
64             -power 1
65             -min 1
66             -max 0
67              
68             # for value (e.g. -value=>{power=>2,rmin=>1}
69             -rmin 1
70             -power 2
71             -min 1
72             -max 0
73              
74             Options are set using
75              
76             %options => {-hzero=>0, -orientation=>1, -saturation => { rmin => 0 } }
77              
78             $encoder = Color::TupleEncode(method=>"Color::TupleEncode::2Way",
79             options=>\%options)
80              
81             or
82              
83             $encoder->set_options( -hzero => 0)
84             $encoder->set_options( -hzero => 0, -orientation =>1 )
85             $encoder->set_options( -hzero => 0, -orientation =>1, -saturation => { rmin => 0} )
86              
87             See C for a chart of encoded colors.
88              
89             The color components are calculated as follows.
90              
91             =head2 Hue
92              
93             Hue is defined based on the ratio of the 2-tuple components, C.
94              
95             r = x/y
96              
97             hue = hzero + 180 if y = 0
98              
99             hue = hzero - orient * 180 * (1-r) if r <= 1
100            
101             hue = hzero + orient * 180 * (1-1/r) if r > 1
102              
103             All hue values are modulo 360.
104              
105             This method maps the C<(x,y)> pair onto the color wheel as
106             follows. First, a reference hue C is chosen. Next, the mapping
107             orientation is selected using C. Assuming that C,
108             and given the ratio C, when C the hue lies in the
109             interval C<[hzero-180,hzero]>. Thus hue progresses in the
110             counter-clockwise direction along the color wheel from C when
111             C to C when C.
112              
113             When C>1>, the hue lines in the interval C<[hzero,hzero+180]>
114             and hue progresses clock-wise.
115              
116             If C, the direction of hue progression is reversed.
117              
118             For example, if C and C (cyan),
119              
120             hue color r = x/y
121            
122             0 red 0
123             45 orange 0.25
124             90 lime 0.5
125             135 green 0.75
126             hzero 180 cyan 1
127             240 blue 1.5
128             270 violet 2
129             300 purple 3
130             315 purple 4
131             0 red INF, NaN (y=0)
132              
133             =head2 Saturation
134              
135             The saturation is calculated using the size of the 2-tuple, C. Depending on the value of C,
136              
137             r = sqrt ( x**2 + y **2 )
138              
139             -r/power
140             saturation = 1 - 2 if power > 0
141              
142             saturation = 1 if power = 0
143              
144             The default limits on saturation are C at C and C
145             at C. The default rate of decrease is C. Thus, for
146             every unit change in C, saturation is decreased by 50%. Use the
147             C option to change the rate of change. In general, saturation
148             will change by a factor of C<2> for every C units of C. That
149             is,
150              
151             r saturation
152             power = 1 power = 2 power = 3
153             0 1 1 1
154             1 0.5 0.707 0.794
155             2 0.25 0.5 0.63
156             3 0.125 0.354 0.5
157             4 0.063 0.25 0.397
158            
159             If C, saturation will be assigned the value it would have at C if C 0>.
160             However, keep in mind the effect of C, described below.
161              
162             Saturation can be interpolated within C<[min,max]> by setting the C<-min> and C<-max> options.
163              
164             $convert->set_options(-saturation=>{min=>0.8,max=>0.2})
165              
166             In this example, saturation will be C<0.8> at C= 0> and will start decreasing at C towards C<0.2> at C.
167              
168             You can set the minimum value of the tuple component at which saturation begins to change. Use C option,
169              
170             $convert->set_options(-saturation=>{min=>0.8,max=>0.2,rmin=>1})
171              
172             In this example, saturation will be C<0.8> at C= 1>, will start decreasing at C towards C<0.2> at C.
173              
174             If C is set and C, then saturation will be C for C= rmin> and C for C rmin>.
175              
176             =head2 Value
177              
178             The value is calculated using the same formula as for saturation.
179              
180             By setting different C values for saturation and value components, you can control the range of C over which the encoding acts. For example,
181              
182             $convert->set_options(-saturation=>{rmin=>0},-value=>{rmin=>1})
183              
184             will result in saturation changing for C 0> and value only for
185             C 1>. For C= 1>, value will be at its C setting.
186              
187             =head1 EXPORT
188              
189             Exports nothing.
190              
191             Use L and set the encoding method to
192             C<"Color::TupleEncode::2Way"> to use this module.
193              
194             =cut
195              
196             Readonly::Scalar our $TUPLE_SIZE => 2;
197             Readonly::Array our @OPTIONS_OK => (qw(-orientation -hzero -saturation -value));
198             Readonly::Hash our %OPTIONS_DEFAULT => (-hzero=>180,
199             -orientation=>1,
200             -saturation => {power=>1,min=>1,max=>0,rmin=>0},
201             -value => {power=>2,min=>1,max=>0,rmin=>1});
202              
203             sub _component_power_scale {
204 60     60   96 my ($value,$options,$component_name,$min,$max,$power) = @_;
205 60 50       209 $min = $options->{min} if defined $options->{min};
206 60 50       858 $max = $options->{max} if defined $options->{max};
207 60 50       702 confess "Option for $component_name minimum must be between [0,1] (saw $min), e.g. use -$component_name=>{rmin=>0.25}." if $min < 0;
208 60 50       108 confess "Option for $component_name maximum must be between [0,1] (saw $max), e.g. use -$component_name=>{compmax=>0.75}." if $max > 1;
209 60 50       164 $power = defined $options->{power} ? $options->{power} : $power;
210 60 50       729 confess "Power for $component_name must be non-negative (saw $power), e.g. use -$component_name=>{power=>1}" if $power < 0;
211 60 50       165 my $rmin = defined $options->{rmin} ? $options->{rmin} : 0;
212 60         580 my $component;
213 60 50       144 if(! defined $power) {
    50          
214 0         0 confess "The option -power is not defined for $component_name. It is required to compute the value. Try -$component_name=>{power=>3}.";
215             } elsif ($power == 0) {
216 0 0       0 if($value <= $rmin) {
217 0         0 $component = $min;
218             } else {
219 0         0 $component = $max;
220             }
221             } else {
222             ### $component_name
223             ### $value
224             ### $rmin
225 60 100       97 if($value < $rmin) {
226             ### below rmin
227 2         4 $component = $min;
228             } else {
229 58         187 my $f = 1 - 2 ** ( - ($value-$rmin) / $power );
230             ### $value
231             ### $f
232 58         95 $component = _interpolate($f,$min,$max);
233             }
234             ### $component
235             }
236 60 50       122 $component = 0 if $component < 0;
237 60 50       102 $component = 1 if $component > 1;
238 60         305 return $component;
239             }
240              
241             =for comment
242             Given a 2-tuple, return the corresponding color saturation (in the range [min,max]).
243              
244             =cut
245              
246             sub _get_saturation {
247 30     30   55 my ($self) = shift;
248 30         93 my $options = $self->get_options(-saturation);
249 30         83 my ($a,$b) = $self->get_tuple;
250 30         50 my ($min,$max,$power) = (1,0,1);
251 30         82 my $r = sqrt($a**2 + $b**2);
252 30         51 my $component = _component_power_scale($r,$options,"saturation",$min,$max,$power);
253             }
254              
255             =for comment
256             Given a 2-tuple, return the corresponding color saturation (in the range [min,max]).
257              
258             =cut
259              
260             sub _get_value {
261 30     30   46 my ($self) = shift;
262 30         103 my $options = $self->get_options(-value);
263 30         83 my ($a,$b) = $self->get_tuple;
264 30         78 my ($min,$max,$power) = (1,0,2);
265 30         72 my $r = sqrt($a**2 + $b**2);
266 30         49 my $component = _component_power_scale($r,$options,"value",$min,$max,$power);
267 30         252 return $component;
268             }
269              
270             =for comment
271             Given a data triplet, return the corresponding color hue (in the range [0,360)).
272              
273             =cut
274              
275             sub _get_hue {
276 30     30   47 my $self = shift;
277 30         83 my ($a,$b) = $self->get_tuple;
278              
279 30 100       90 my $ratio = $b ? abs($a/$b) : undef;
280 30         29 my $h;
281 30         95 my $hzero = $self->get_options(-hzero);
282 30         106 my $orient = $self->get_options(-orientation);
283 30 100       62 if(defined $ratio) {
284             # if the ratio is negative, the hue will be in the first
285             # half of the color wheel, starting at hue $hzero and
286             # progressing counterclockwise
287 20 50       38 if($ratio <= 1) {
288 20         41 $h = $hzero - $orient * 180*(1-$ratio);
289             }
290             # if the ratio is positive, the hue will be in the second
291             # half of the color wheel, starting at hue $hzero and
292             # progressing clockwise
293             else {
294 0         0 $h = $hzero + $orient * 180*(1-1/$ratio);
295             }
296             }
297             else {
298             # If the ratio is not defined ($b = 0), set it to hue $hzero
299 10         15 $h = $hzero + 180;
300             }
301 30         241 return $h % 360;
302             }
303              
304             sub _interpolate {
305 58     58   74 my ($x,$min,$max) = @_;
306              
307 58 100       130 if($x <= 0) {
    50          
308 6         14 return $min;
309             }
310             elsif ($x >= 1) {
311 0         0 return $max;
312             }
313             else {
314 52         82 my $xi = $min + $x * ($max-$min);
315 52         107 return $xi;
316             }
317             }
318              
319             =for comment
320             Returns the tuple size for this encoding.
321              
322             =cut
323              
324             sub _get_tuple_size {
325 48     48   127 return $TUPLE_SIZE;
326             }
327              
328             =for comment
329             Returns a list of options that this implementation understands.
330              
331             =cut
332              
333             sub _get_ok_options {
334 585     585   1420 return @OPTIONS_OK;
335             }
336              
337             =for comment
338             Returns a hash of default options for this implementation
339              
340             =cut
341              
342             sub _get_default_options {
343 36     36   97 return %OPTIONS_DEFAULT;
344             }
345              
346             =pod
347              
348             =head1 IMPLEMENTING AN ENCODING CLASS
349              
350             The encoding class must implement the following functions. Given a C object C<$obj>,
351              
352             =head2 C<$value = _get_value( $obj )>
353              
354             =head2 C<$saturation = _get_saturation( $obj )>
355              
356             =head2 C<$hue = _get_hue( $obj )>
357              
358             =head2 C<$size = _get_tuple_size()>
359              
360             =head2 C<@opt_ok =_get_ok_options()>
361              
362             =head2 C<%opt_def = _get_default_options()>
363              
364             =head1 AUTHOR
365              
366             Martin Krzywinski, C<< >>
367              
368             The 2-tuple color encoding implemented in this module was created by the author.
369              
370             =head1 BUGS
371              
372             Please report any bugs or feature requests to C, or through
373             the web interface at L. I will be notified, and then you'll
374             automatically be notified of progress on your bug as I make changes.
375              
376             =head1 SUPPORT
377              
378             You can find documentation for this module with the perldoc command.
379              
380             perldoc Color::TupleEncode
381              
382             You can also look for information at:
383              
384             =over 4
385              
386             =item * RT: CPAN's request tracker
387              
388             L
389              
390             =item * AnnoCPAN: Annotated CPAN documentation
391              
392             L
393              
394             =item * CPAN Ratings
395              
396             L
397              
398             =item * Search CPAN
399              
400             L
401              
402             =back
403              
404             =head1 SEE ALSO
405              
406             =over
407              
408             =item Color::TupleEncode
409              
410             Driver module. This is the module that provides an API for the color encoding. See L.
411              
412             =item Color::TupleEncode::Baran
413              
414             Encodes a 3-tuple to a color using the scheme described in
415              
416             Visualization of three-way comparisons of omics data
417             Richard Baran Martin Robert, Makoto Suematsu, Tomoyoshi Soga1 and Masaru Tomita
418             BMC Bioinformatics 2007, 8:72 doi:10.1186/1471-2105-8-72
419              
420             This publication can be accessed at L
421              
422             =item Color::TupleEncode::2Way
423              
424             A template class for implementing an encoding scheme.
425              
426             =back
427              
428             =head1 LICENSE AND COPYRIGHT
429              
430             Copyright 2010 Martin Krzywinski.
431              
432             This program is free software; you can redistribute it and/or modify it
433             under the terms of either: the GNU General Public License as published
434             by the Free Software Foundation; or the Artistic License.
435              
436             See http://dev.perl.org/licenses/ for more information.
437              
438             =cut
439              
440             1; # End of Color::TupleEncode::2Way