File Coverage

blib/lib/Color/Model/Munsell.pm
Criterion Covered Total %
statement 94 115 81.7
branch 49 64 76.5
condition 6 9 66.6
subroutine 27 27 100.0
pod 20 20 100.0
total 196 235 83.4


line stmt bran cond sub pod time code
1             # =============================================================================
2             package Color::Model::Munsell;
3             # -----------------------------------------------------------------------------
4             $Color::Model::Munsell::VERSION = '0.02';
5             # -----------------------------------------------------------------------------
6 2     2   45299 use warnings;
  2         4  
  2         60  
7 2     2   9 use strict;
  2         3  
  2         69  
8              
9             =head1 NAME
10              
11             Color::Model::Munsell - Color model of Munsell color system
12              
13             =head1 SYNOPSIS
14              
15             Chromatic color;
16              
17             $mun = Color::Model::Munsell->new("9R 5.5/14");
18             $mun = Color::Model::Munsell->new("7PB", 4, 10);
19             print "$mum is chromatic color" if !$mun->isneutral;
20              
21             Neutral grays;
22              
23             $mun = Color::Model::Munsell->new("N 4.5");
24             $mun = Color::Model::Munsell->new("N", 9);
25             print "$mum is neutral color" if $mun->isneutral;
26              
27             =cut
28              
29             # =============================================================================
30 2     2   8 use Carp qw();
  2         7  
  2         40  
31 2     2   15 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
  2         3  
  2         140  
32 2     2   15 use base qw(Exporter);
  2         2  
  2         2961  
33             @EXPORT = qw( Munsell degree undegree );
34             @EXPORT_OK = qw( PUREWHITE PUREBLACK REALWHITE REALBLACK @hue_order %hue_number );
35             %EXPORT_TAGS = (
36             symbols => [ qw( @hue_order %hue_number ) ],
37             vertexes => [ qw( PUREWHITE PUREBLACK REALWHITE REALBLACK ) ],
38             all => [ @EXPORT, @EXPORT_OK ]
39             );
40              
41             our @hue_order = qw(R YR Y GY G BG B PB P RP);
42             our %hue_number = (
43             R => 0, YR => 1, Y => 2, GY => 3, G => 4,
44             BG => 5, B => 6, PB => 7, P => 8, RP => 9,
45             );
46              
47             our $ERROR;
48              
49             # =============================================================================
50              
51             =head1 CONSTRUCTORS
52              
53             =head2 new()
54              
55             # with Munsell color specifying
56             $m = Color::Model::Munsell->new("9R 5.5/14");
57             $m = Color::Model::Munsell->new("N 4.5");
58              
59             # with parapeters
60             $m = Color::Model::Munsell->new("7PB", 4, 10);
61             $m = Color::Model::Munsell->new("N", 9);
62              
63             If number part of hue is 0, it becomes 10.0 of previous hue color on the circle.
64              
65             Value(as Lightness) or chroma has thier range;
66             0 <= value <= 10.0 # if 0 or 10, it will be regraded as black or white
67             0 <= chroma # if 0, it will be gray
68              
69             and these number will be rounded up to the tenth's place with sprintf().
70              
71             If constructor returns undef, it means some error occurs. When this case, Check
72             $Color::Model::Munsell::ERROR that have a reason of an error.
73              
74             =cut
75              
76             sub new
77             {
78 216     216 1 81138 my $class = shift;
79              
80 216         276 my ($hue, $value, $chroma);
81 0         0 my ($chromatic, $hue_step, $hue_col);
82 216         317 $ERROR = '';
83              
84 216 100       466 if ( @_ == 1 ){
85 209         1190 ($hue, $value, $chroma) = split(/[ \/]+/, $_[0]);
86             } else {
87 7         20 ($hue, $value, $chroma) = @_;
88             }
89              
90             # - hue check
91 216 50       505 if ( defined($hue) ){
92 216         321 $hue = uc($hue);
93 216 100       1125 if ( $hue eq 'N' ){
    50          
94 5         7 $chromatic = 0;
95             }
96             elsif ( $hue =~ /^(\d+|\d+\.\d+)(R|YR|Y|GY|G|BG|B|PB|P|RP)$/ ){
97 211         570 ($hue_step,$hue_col) = ($1,$2);
98 211         1243 $hue_step = sprintf('%.1f',$hue_step);
99 211 50       615 if ( $hue_step > 10 ){
100 0         0 $ERROR = "Number of hue, \"$hue\", is grater than 10.0.";
101 0         0 return undef;
102             }
103 211 100       404 if ( $hue_step == 0 ){
104 1         3 $hue_step = 10.0;
105 1 50       7 $hue_col = $hue_col eq 'R'? 'RP': $hue_order[$hue_number{$hue_col}-1];
106             }
107 211         806 $hue = sprintf('%s%s',$hue_step+0,$hue_col);
108 211         368 $chromatic = 1;
109             }
110             else {
111 0         0 $ERROR = "Hue, \"$hue\" is not valid format.";
112 0         0 return undef;
113             }
114             } else {
115 0         0 $ERROR = "Hue is undefined.";
116 0         0 return undef;
117             }
118              
119             # - value check
120 216 50       375 if ( defined($value) ){
121 216 50       742 if ( $value =~ /^(\d+|\d+\.\d+)$/ ){
122 216         798 $value = sprintf('%.1f',$value);
123 216 50 100     1345 if ( $value>10 ){
    100          
124 0         0 $ERROR = "Value ($value) is out of range.";
125 0         0 return undef;
126             }
127             elsif ( $value == 0 or $value == 10 ){
128 4         6 $hue = 'N';
129 4         7 $chromatic = 0;
130             }
131             }
132             else {
133 0         0 $ERROR = "Value is not a valid number.";
134 0         0 return undef;
135             }
136             } else {
137 0         0 $ERROR = "Value is undefined.";
138 0         0 return undef;
139             }
140              
141             # - chroma check
142 216 100       457 if ( $chromatic ){
143 209 50       351 if ( defined($chroma) ){
144 209 50       655 if ( $chroma =~ /^(\d+|\d+\.\d+)$/ ){
145 209         905 $chroma = sprintf('%.1f',$chroma);
146 209 100       633 if ( $chroma == 0 ){
147 1         3 $hue = 'N';
148 1         3 $chromatic = 0;
149             }
150             }
151             else {
152 0         0 $ERROR = "Chroma is not a valid number.";
153 0         0 return undef;
154             }
155             }
156             else {
157 0         0 $ERROR = "Chroma is undefined.";
158 0         0 return undef;
159             }
160             }
161              
162 216 100       1488 my $self = {
    100          
    100          
163             hue => $hue,
164             hue_step => $chromatic? $hue_step: undef,
165             hue_col => $chromatic? $hue_col: undef,
166             value => $value,
167             chroma => $chromatic? $chroma: undef,
168             };
169 216         901 bless $self, $class;
170             }
171              
172              
173             =head2 Munsell()
174              
175             Munsell() is defalut exporting subroutine which calls new().
176              
177             $m = Munsell("9R 5.5/14");
178              
179             =cut
180              
181             sub Munsell
182             {
183 1     1 1 7 return __PACKAGE__->new(@_);
184             }
185              
186              
187             # =============================================================================
188              
189             =head1 CONSTANTS
190              
191             There are some constants which makes black or white object. Use tag ":vertexes"
192             or ":all".
193              
194             =over
195              
196             =item PUREWHITE()
197              
198             return an object of "N 10.0"
199              
200             =item PUREBLACK();
201              
202             return an object of "N 0.0"
203              
204             =item REALWHITE();
205              
206             return an object of "N 9.5"
207              
208             =item REALBLACK();
209              
210             return an object of "N 1.0"
211              
212             =back
213              
214             =cut
215              
216 1     1 1 7 sub PUREWHITE { __PACKAGE__->new('N 10.0') }
217 1     1 1 3 sub PUREBLACK { __PACKAGE__->new('N 0.0') }
218 1     1 1 6 sub REALWHITE { __PACKAGE__->new('N 9.5') }
219 1     1 1 8 sub REALBLACK { __PACKAGE__->new('N 1.0') }
220              
221              
222             =head1 METHODS
223              
224             Object C has methods below.
225              
226             =over
227              
228             =item code()
229              
230             Returns Munsell code like "5R 10/14" or "N 4.5".
231              
232             =item ischromatic()
233              
234             Returns 1 if object is chromatic, or 0.
235              
236             =item isneutral()
237              
238             Returns 1 if object is neutral color, or 0.
239              
240             =item isblack()
241              
242             Return 1 if value is equal or lesser than 1.0, or 0
243              
244             =item iswhite()
245              
246             Return 1 if value is equal or greater than 9.5, or 0
247              
248             =item hue()
249              
250             Returns hue code like "5R" or "N".
251              
252             =item hueCol()
253              
254             Returns color sign of hue; R,YR,Y,GY,G,BG,B,PB,P,RP or N
255              
256             =item hueStep();
257              
258             Returns number of hue (gray returns undef)
259              
260             =item value()
261              
262             Returns Munsell value.
263              
264             =item lightness()
265              
266             Same as value()
267              
268             =item chroma()
269              
270             Returns Munsell chroma.
271              
272             =item saturation()
273              
274             Same as chroma().
275              
276             =item degree()
277              
278             See degree() below.
279              
280             =back
281              
282             =cut
283              
284 2 100   2 1 13 sub ischromatic { defined($_[0]->{chroma})? 1:0; }
285 2 100   2 1 16 sub isneutral { defined($_[0]->{chroma})? 0:1; }
286 4     4 1 21 sub hue { $_[0]->{hue}; }
287 200     200 1 1663 sub hueCol { $_[0]->{hue_col}; }
288 200     200 1 603 sub hueStep { $_[0]->{hue_step}; }
289 2     2 1 14 sub value { $_[0]->{value}; }
290 1     1 1 7 sub lightness { $_[0]->{value}; }
291 1     1 1 7 sub chroma { $_[0]->{chroma}; }
292 1     1 1 7 sub saturation { $_[0]->{chroma}; }
293              
294             sub code
295             {
296 239     239 1 343 my $self = shift;
297 239 100       534 if ( defined($self->{chroma}) ){
298 228         2214 return sprintf('%s %s/%s',$self->{hue}, $self->{value}+0, $self->{chroma}+0);
299             } else {
300 11         103 return sprintf('N %.1f',$self->{value});
301             }
302             }
303              
304             sub isblack
305             {
306 2     2 1 4 my $self = shift;
307 2 100       14 return $self->{value} <= 1.0? 1: 0;
308             }
309              
310             sub iswhite
311             {
312 2     2 1 6 my $self = shift;
313 2 100       13 return $self->{value} >= 9.5? 1: 0;
314             }
315              
316              
317             # =============================================================================
318              
319             =head1 SUBROUTINES
320              
321             =head2 degree()
322              
323             print $m->degree();
324             print degree("7.5B");
325              
326             Subroutine or object method C return a serial hue number, considering
327             10.0RP is 0, 10R is 10, 10YR is 20, ..., and ends 9.9RP as 99.9.
328             This will be useful to get radians of Muncell color circle.
329             If object is neutral, this returns undef.
330              
331             =cut
332              
333             sub degree
334             {
335 209 50   209 1 946 unless ( @_ == 1 ){
336 0         0 Carp::croak('Usage: degree($huecode) or $m->degree()');
337             }
338 209         2428 my $self = shift;
339 209 100       525 if ( ref($self) eq __PACKAGE__ ){
340 207 50       531 return undef unless defined($self->{chroma});
341             } else {
342 2         7 $self = __PACKAGE__->new($self,1,1);
343 2 50       7 Carp::croak($ERROR) unless defined($self);
344             }
345 209 100       502 if ( $self->{hue} eq '10RP' ){
346 5         35 return 0;
347             } else {
348 204         1720 return $hue_number{$self->{hue_col}} * 10 + $self->{hue_step};
349             }
350             }
351              
352             =head2 undegree();
353              
354             print undegree();
355              
356             Function undegree() return a hue code from a serial hue number.
357              
358             =cut
359              
360             sub undegree
361             {
362 2 50   2 1 6 unless ( @_ == 1 ){
363 0         0 Carp::croak('Usage: undegree($degreenum)');
364             }
365 2         5 my $num = shift;
366 2 50 33     23 unless ( defined($num) && $num=~/^(\d+|\d+\.\d+)$/ ){
367 0         0 Carp::croak("Argument is not a valid number.");
368             }
369 2 50       6 if ( $num > 100.0 ){
370 0         0 Carp::croak("Given number is out of range(<=100).");
371             }
372 2         13 $num = sprintf('%.1f', $num);
373 2 100 66     14 if ( $num == 0 or $num == 100 ){
374 1         6 return '10RP';
375             } else {
376 1         4 my $col = int($num/10);
377 1         3 my $stp = $num - $col*10;
378 1         11 return sprintf('%s%s', $stp+0, $hue_order[$col]);
379             }
380             }
381              
382              
383             # =============================================================================
384              
385             =head1 OPERATOR OVERLOAD
386              
387             Stringify operator of this module, Color::Model::Munsell, is prepared. If
388             you join a object with some string, object will be Munsell code.
389              
390             $m = Color::Model::Munsell->new("9R", 5.5, 14);
391             print "$m is red"; # printing "9R 5.5/14 is red"
392              
393             =cut
394              
395             use overload
396 2         13 '""' => \&_stringify,
397 2     2   3045 'fallback' => undef;
  2         1918  
398              
399             sub _stringify {
400 233     233   469 my($object,$argument,$flip) = @_;
401 233         453 return $object->code;
402             }
403              
404              
405             =head1 BUGS
406              
407             Please report any bugs or feature requests to C, or through
408             the web interface at L. I will be notified, and then you'll
409             automatically be notified of progress on your bug as I make changes.
410              
411             =head1 AUTHOR
412              
413             Takahiro Onodera, C<< >>
414              
415             =head1 LICENSE AND COPYRIGHT
416              
417             Copyright 2010 T.Onodera.
418              
419             This program is free software; you can redistribute it and/or modify it
420             under the terms of either: the GNU General Public License as published
421             by the Free Software Foundation; or the Artistic License.
422              
423             See http://dev.perl.org/licenses/ for more information.
424              
425             =cut
426              
427             1; # End of Color::Model::Munsell