File Coverage

blib/lib/Acme/Colour.pm
Criterion Covered Total %
statement 108 108 100.0
branch 38 38 100.0
condition n/a
subroutine 18 18 100.0
pod 5 5 100.0
total 169 169 100.0


line stmt bran cond sub pod time code
1             package Acme::Colour;
2 2     2   35049 use strict;
  2         5  
  2         70  
3 2     2   11 use warnings;
  2         4  
  2         55  
4 2     2   1733 use Error;
  2         10393  
  2         11  
5 2     2   1948 use Graphics::ColorNames;
  2         13761  
  2         186  
6 2     2   23 use List::Util qw(max min);
  2         5  
  2         270  
7 2     2   12 use vars qw($VERSION);
  2         15  
  2         168  
8              
9             $VERSION = '1.06';
10              
11             use overload
12 2         26 '""' => \&colour,
13             '+' => \&_oadd,
14 2     2   13 '-' => \&_osub;
  2         4  
15              
16             my ( %r, %g, %b );
17              
18             sub import {
19 2     2   2181 my $class = shift;
20 2         6 my $hash = {@_};
21              
22 2         11 $class->_build_colours();
23              
24 2 100       293 if ( $hash->{constants} ) {
25 1         9 overload::constant( 'q' => \&_createnew );
26             } else {
27              
28             # do nothing for now
29             }
30             }
31              
32             sub _build_colours {
33 2     2   14 my $class = shift;
34              
35 2 100       11 if ( scalar( keys %r ) == 0 ) {
36 1         6 tie my %COLOURS, 'Graphics::ColorNames', 'X';
37              
38 1         7675 foreach my $colour ( keys %COLOURS ) {
39 665 100       5150 next if $colour =~ /\d/;
40 429         3090 my ( $r, $g, $b )
41 143         554 = map { hex($_) / 255 }
42             ( $COLOURS{$colour}
43             =~ /^\#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/i
44             );
45 143         425 $r{$colour} = $r;
46 143         221 $g{$colour} = $g;
47 143         251 $b{$colour} = $b;
48              
49             # print "$colour: $r/$g/$b\n";
50             }
51             }
52             }
53              
54             sub _createnew {
55 17     17   70 my $colour = shift;
56 17         23 my $interp = shift;
57              
58 17 100       39 if ( exists $r{$interp} ) {
59 8         23 return Acme::Colour->new($interp);
60             } else {
61 9         2297 return $interp;
62             }
63             }
64              
65             sub _oadd {
66 1     1   60 my $a = shift;
67 1         3 my $b = shift;
68 1         7 $a->add($b);
69 1         5 return $a;
70             }
71              
72             sub _osub {
73 1     1   4 my $a = shift;
74 1         2 my $b = shift;
75 1         6 $a->mix($b);
76 1         5 return $a;
77             }
78              
79             sub new {
80 23     23 1 868 my ( $class, $colour ) = @_;
81              
82 23         46 my $self = {};
83 23         54 bless $self, $class;
84              
85 23 100       57 if ( defined $colour ) {
86 22 100       64 unless ( exists $r{$colour} ) {
87 1         24 throw Error::Simple("Colour $colour is unknown");
88             }
89 21         107 $self->{colour} = $colour;
90             } else {
91 1         6 $self->{colour} = $self->default;
92             }
93              
94 22         208 return $self;
95             }
96              
97             sub default {
98 1     1 1 47 return "white";
99             }
100              
101             sub colour {
102 112     112 1 5575 my $self = shift;
103 112         462 return $self->{colour};
104             }
105              
106             sub add {
107 20     20 1 147 my $self = shift;
108 20         35 my $add = shift;
109 20         25 my $factor = shift;
110 20 100       62 $factor = 1 unless defined $factor;
111              
112 20         46 my $colour = $self->colour;
113              
114 20 100       99 throw Error::Simple("Colour $colour is unknown")
115             unless exists $r{$colour};
116 19 100       49 throw Error::Simple("Colour $add is unknown")
117             unless exists $r{$add};
118              
119 18         75 my ( $r1, $g1, $b1 ) = ( $r{$colour}, $g{$colour}, $b{$colour} );
120 18         57 my ( $r2, $g2, $b2 ) = ( $r{$add}, $g{$add}, $b{$add} );
121 18         37 $r1 += $r2 * $factor;
122 18         23 $g1 += $g2 * $factor;
123 18         25 $b1 += $b2 * $factor;
124 18 100       40 $r1 = 1 if $r1 > 1;
125 18 100       36 $g1 = 1 if $g1 > 1;
126 18 100       32 $b1 = 1 if $b1 > 1;
127 18         41 my $closest = $self->_closest( $r1, $g1, $b1 );
128 18         64 $self->{colour} = $closest;
129             }
130              
131             sub mix {
132 19     19 1 504 my $self = shift;
133 19         31 my $add = shift;
134 19         23 my $factor = shift;
135 19 100       52 $factor = 1 unless defined $factor;
136              
137 19         46 my $colour = $self->colour;
138              
139 19 100       98 throw Error::Simple("Colour $colour is unknown")
140             unless exists $r{$colour};
141 18 100       48 throw Error::Simple("Colour $add is unknown")
142             unless exists $r{$add};
143              
144 17         35 my ( $r1, $g1, $b1 ) = ( $r{$colour}, $g{$colour}, $b{$colour} );
145 17         37 my ( $r2, $g2, $b2 ) = ( $r{$add}, $g{$add}, $b{$add} );
146              
147 17         45 ( $r1, $g1, $b1 ) = ( 1 - $r1, 1 - $g1, 1 - $b1 );
148 17         40 ( $r2, $g2, $b2 ) = ( 1 - $r2, 1 - $g2, 1 - $b2 );
149              
150 17         29 $r1 += $r2 * $factor;
151 17         24 $g1 += $g2 * $factor;
152 17         19 $b1 += $b2 * $factor;
153 17 100       40 $r1 = 1 if $r1 > 1;
154 17 100       31 $g1 = 1 if $g1 > 1;
155 17 100       26 $b1 = 1 if $b1 > 1;
156              
157 17         39 ( $r1, $g1, $b1 ) = ( 1 - $r1, 1 - $g1, 1 - $b1 );
158              
159 17         38 my $closest = $self->_closest( $r1, $g1, $b1 );
160 17         94 $self->{colour} = $closest;
161             }
162              
163             sub _closest {
164 35     35   53 my ( $self, $r1, $g1, $b1 ) = @_;
165              
166 35         39 my $bestdelta = 100;
167 35         29 my $closest;
168 35         2730 foreach my $colour ( sort keys %r ) {
169 5005         7881 my ( $r2, $g2, $b2 ) = ( $r{$colour}, $g{$colour}, $b{$colour} );
170 5005         9300 my $delta
171             = sqrt( ( $r1 - $r2 )**2 + ( $g1 - $g2 )**2 + ( $b1 - $b2 )**2 );
172 5005 100       9957 if ( $delta < $bestdelta ) {
173 217         244 $closest = $colour;
174 217         339 $bestdelta = $delta;
175             }
176             }
177 35         313 return $closest;
178             }
179              
180             1;
181              
182             __END__