| blib/lib/Color/Scheme.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 206 | 207 | 99.5 |
| branch | 43 | 66 | 65.1 |
| condition | 7 | 9 | 77.7 |
| subroutine | 37 | 37 | 100.0 |
| pod | 10 | 10 | 100.0 |
| total | 303 | 329 | 92.1 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | 4 | 4 | 70477 | use strict; | |||
| 4 | 9 | ||||||
| 4 | 152 | ||||||
| 2 | 4 | 4 | 17 | use warnings; | |||
| 4 | 5 | ||||||
| 4 | 185 | ||||||
| 3 | package Color::Scheme; | ||||||
| 4 | # ABSTRACT: generate pleasant color schemes | ||||||
| 5 | $Color::Scheme::VERSION = '1.07'; | ||||||
| 6 | 4 | 4 | 20 | use Carp; | |||
| 4 | 4 | ||||||
| 4 | 321 | ||||||
| 7 | 4 | 4 | 20 | use List::Util 1.14 qw(min max); | |||
| 4 | 113 | ||||||
| 4 | 405 | ||||||
| 8 | 4 | 4 | 2094 | use POSIX 1.08 qw(floor); | |||
| 4 | 22648 | ||||||
| 4 | 24 | ||||||
| 9 | |||||||
| 10 | my %SCHEMES = ( map { $_, 1 } | ||||||
| 11 | qw( mono monochromatic contrast triade tetrade analogic ) ); | ||||||
| 12 | |||||||
| 13 | my %PRESETS = ( | ||||||
| 14 | |||||||
| 15 | # name => [ ? ] | ||||||
| 16 | default => [ -1, -1, 1, -0.7, 0.25, 1, 0.5, 1 ], | ||||||
| 17 | pastel => [ 0.5, -0.9, 0.5, 0.5, 0.1, 0.9, 0.75, 0.75 ], | ||||||
| 18 | soft => [ 0.3, -0.8, 0.3, 0.5, 0.1, 0.9, 0.5, 0.75 ], | ||||||
| 19 | light => [ 0.25, 1, 0.5, 0.75, 0.1, 1, 0.5, 1 ], | ||||||
| 20 | hard => [ 1, -1, 1, -0.6, 0.1, 1, 0.6, 1 ], | ||||||
| 21 | pale => [ 0.1, -0.85, 0.1, 0.5, 0.1, 1, 0.1, 0.75 ], | ||||||
| 22 | ); | ||||||
| 23 | |||||||
| 24 | my %COLOR_WHEEL = ( | ||||||
| 25 | |||||||
| 26 | # hue => [ red, green, blue, value ] | ||||||
| 27 | 0 => [ 255, 0, 0, 100 ], | ||||||
| 28 | 15 => [ 255, 51, 0, 100 ], | ||||||
| 29 | 30 => [ 255, 102, 0, 100 ], | ||||||
| 30 | 45 => [ 255, 128, 0, 100 ], | ||||||
| 31 | 60 => [ 255, 153, 0, 100 ], | ||||||
| 32 | 75 => [ 255, 178, 0, 100 ], | ||||||
| 33 | 90 => [ 255, 204, 0, 100 ], | ||||||
| 34 | 105 => [ 255, 229, 0, 100 ], | ||||||
| 35 | 120 => [ 255, 255, 0, 100 ], | ||||||
| 36 | 135 => [ 204, 255, 0, 100 ], | ||||||
| 37 | 150 => [ 153, 255, 0, 100 ], | ||||||
| 38 | 165 => [ 51, 255, 0, 100 ], | ||||||
| 39 | 180 => [ 0, 204, 0, 80 ], | ||||||
| 40 | 195 => [ 0, 178, 102, 70 ], | ||||||
| 41 | 210 => [ 0, 153, 153, 60 ], | ||||||
| 42 | 225 => [ 0, 102, 178, 70 ], | ||||||
| 43 | 240 => [ 0, 51, 204, 80 ], | ||||||
| 44 | 255 => [ 25, 25, 178, 70 ], | ||||||
| 45 | 270 => [ 51, 0, 153, 60 ], | ||||||
| 46 | 285 => [ 64, 0, 153, 60 ], | ||||||
| 47 | 300 => [ 102, 0, 153, 60 ], | ||||||
| 48 | 315 => [ 153, 0, 153, 60 ], | ||||||
| 49 | 330 => [ 204, 0, 153, 80 ], | ||||||
| 50 | 345 => [ 229, 0, 102, 90 ], | ||||||
| 51 | ); | ||||||
| 52 | |||||||
| 53 | 4 | 4 | 17 | sub _round { floor( 0.5 + shift ) } | |||
| 54 | |||||||
| 55 | #pod =head1 SYNOPSIS | ||||||
| 56 | #pod | ||||||
| 57 | #pod use Color::Scheme; | ||||||
| 58 | #pod | ||||||
| 59 | #pod my $scheme = Color::Scheme->new | ||||||
| 60 | #pod ->from_hex('ff0000') # or ->from_hue(0) | ||||||
| 61 | #pod ->scheme('analogic') | ||||||
| 62 | #pod ->distance(0.3) | ||||||
| 63 | #pod ->add_complement(1) | ||||||
| 64 | #pod ->variation('pastel') | ||||||
| 65 | #pod ->web_safe(1) | ||||||
| 66 | #pod | ||||||
| 67 | #pod my @list = $scheme->colors(); | ||||||
| 68 | #pod # @list = ( "999999","666699","ffffff","99cccc", | ||||||
| 69 | #pod # "999999","666699","ffffff","9999cc", | ||||||
| 70 | #pod # "669999","666699","ffffff","99cccc", | ||||||
| 71 | #pod # "cccccc","996666","ffffff","cccc99" ) | ||||||
| 72 | #pod | ||||||
| 73 | #pod my $set = $scheme->colorset(); | ||||||
| 74 | #pod # $set = [ [ "999999","666699","ffffff","99cccc", ], | ||||||
| 75 | #pod # [ "999999","666699","ffffff","9999cc", ], | ||||||
| 76 | #pod # [ "669999","666699","ffffff","99cccc", ], | ||||||
| 77 | #pod # [ "cccccc","996666","ffffff","cccc99" ] ] | ||||||
| 78 | #pod | ||||||
| 79 | #pod | ||||||
| 80 | #pod =head1 DESCRIPTION | ||||||
| 81 | #pod | ||||||
| 82 | #pod This module is a Perl implementation of Color Schemes | ||||||
| 83 | #pod 2 (L |
||||||
| 84 | #pod Start by visitng the Color Schemes 2 web site and playing with the colors. | ||||||
| 85 | #pod When you want to generate those schemes on the fly, begin using this module. | ||||||
| 86 | #pod The descriptions herein don't make too much sense without actually seeing the | ||||||
| 87 | #pod colorful results. | ||||||
| 88 | #pod | ||||||
| 89 | #pod Henceforth, paragraphs in quotes denote documentation copied from Color Schemes 2. | ||||||
| 90 | #pod | ||||||
| 91 | #pod "Important note: This tool I |
||||||
| 92 | #pod same HSV/HSB values ie. in Photoshop describe different colors!). The color | ||||||
| 93 | #pod wheel used here differs from the RGB spectre used on computer screens, it's | ||||||
| 94 | #pod more in accordance with the classical color theory. This is also why some | ||||||
| 95 | #pod colors (especially shades of blue) make less bright shades than the basic | ||||||
| 96 | #pod colors of the RGB-model. In plus, the RGB-model uses red-green-blue as primary | ||||||
| 97 | #pod colors, but the red-yellow-blue combination is used here. This deformation also | ||||||
| 98 | #pod causes incompatibility in color conversions from RGB-values. Therefore, the RGB | ||||||
| 99 | #pod input (eg. the HTML hex values like #F854A9) is not exact, the conversion is | ||||||
| 100 | #pod rough and sometimes may produce slightly different color." | ||||||
| 101 | #pod | ||||||
| 102 | #pod =method new | ||||||
| 103 | #pod | ||||||
| 104 | #pod The C |
||||||
| 105 | #pod | ||||||
| 106 | #pod =cut | ||||||
| 107 | |||||||
| 108 | sub new { | ||||||
| 109 | 6 | 6 | 1 | 7619 | my ( $class, @args ) = @_; | ||
| 110 | 6 | 50 | 19 | carp __PACKAGE__ . "::new() doesn't take any arguments" if @args; | |||
| 111 | |||||||
| 112 | 6 | 9 | my @colors; | ||||
| 113 | 6 | 36 | push @colors, Color::Scheme::mutablecolor->new(60) for 1 .. 4; | ||||
| 114 | |||||||
| 115 | 6 | 48 | return bless { | ||||
| 116 | col => \@colors, | ||||||
| 117 | scheme => 'mono', | ||||||
| 118 | distance => 0.5, | ||||||
| 119 | web_safe => 0, | ||||||
| 120 | add_complement => 0, | ||||||
| 121 | }, $class; | ||||||
| 122 | } | ||||||
| 123 | |||||||
| 124 | #pod =method colors | ||||||
| 125 | #pod | ||||||
| 126 | #pod Returns an array of 4, 8, 12 or 16 colors in C |
||||||
| 127 | #pod (without a leading "#") depending on the color scheme and addComplement | ||||||
| 128 | #pod parameter. For each set of four, the first is usually the most saturated color, | ||||||
| 129 | #pod the second a darkened version, the third a pale version and fourth | ||||||
| 130 | #pod a less-pale version. | ||||||
| 131 | #pod | ||||||
| 132 | #pod For example: With a contrast scheme, L<"colors()"> would return eight colors. | ||||||
| 133 | #pod Indexes 1 and 5 could be background colors, 2 and 6 could be foreground colors. | ||||||
| 134 | #pod | ||||||
| 135 | #pod Trust me, it's much better if you check out the Color Scheme web site, whose | ||||||
| 136 | #pod URL is listed in in L<"DESCRIPTION">. | ||||||
| 137 | #pod | ||||||
| 138 | #pod =cut | ||||||
| 139 | |||||||
| 140 | sub colors { | ||||||
| 141 | 35 | 35 | 1 | 10581 | my ($self) = @_; | ||
| 142 | 35 | 43 | my $used_colors = 1; | ||||
| 143 | 35 | 111 | my $h = $self->{col}->[0]->get_hue; | ||||
| 144 | |||||||
| 145 | my %dispatch = ( | ||||||
| 146 | 7 | 7 | 9 | mono => sub { }, | |||
| 147 | contrast => sub { | ||||||
| 148 | 2 | 2 | 3 | $used_colors = 2; | |||
| 149 | 2 | 9 | $self->{col}->[1]->set_hue($h); | ||||
| 150 | 2 | 8 | $self->{col}->[1]->rotate(180); | ||||
| 151 | }, | ||||||
| 152 | triade => sub { | ||||||
| 153 | 2 | 2 | 3 | $used_colors = 3; | |||
| 154 | 2 | 38 | my $dif = 60 * $self->{distance}; | ||||
| 155 | 2 | 7 | $self->{col}->[1]->set_hue($h); | ||||
| 156 | 2 | 8 | $self->{col}->[1]->rotate( 180 - $dif ); | ||||
| 157 | 2 | 6 | $self->{col}->[2]->set_hue($h); | ||||
| 158 | 2 | 7 | $self->{col}->[2]->rotate( 180 + $dif ); | ||||
| 159 | }, | ||||||
| 160 | tetrade => sub { | ||||||
| 161 | 6 | 6 | 9 | $used_colors = 4; | |||
| 162 | 6 | 14 | my $dif = 90 * $self->{distance}; | ||||
| 163 | 6 | 19 | $self->{col}->[1]->set_hue($h); | ||||
| 164 | 6 | 18 | $self->{col}->[1]->rotate(180); | ||||
| 165 | 6 | 15 | $self->{col}->[2]->set_hue($h); | ||||
| 166 | 6 | 17 | $self->{col}->[2]->rotate( 180 + $dif ); | ||||
| 167 | 6 | 16 | $self->{col}->[3]->set_hue($h); | ||||
| 168 | 6 | 14 | $self->{col}->[3]->rotate($dif); | ||||
| 169 | }, | ||||||
| 170 | analogic => sub { | ||||||
| 171 | 18 | 100 | 18 | 31 | $used_colors = $self->{add_complement} ? 4 : 3; | ||
| 172 | 18 | 32 | my $dif = 60 * $self->{distance}; | ||||
| 173 | 18 | 39 | $self->{col}->[1]->set_hue($h); | ||||
| 174 | 18 | 48 | $self->{col}->[1]->rotate($dif); | ||||
| 175 | 18 | 38 | $self->{col}->[2]->set_hue($h); | ||||
| 176 | 18 | 70 | $self->{col}->[2]->rotate( 360 - $dif ); | ||||
| 177 | 18 | 36 | $self->{col}->[3]->set_hue($h); | ||||
| 178 | 18 | 49 | $self->{col}->[3]->rotate(180); | ||||
| 179 | }, | ||||||
| 180 | 35 | 489 | ); | ||||
| 181 | 35 | 75 | $dispatch{monochromatic} = $dispatch{mono}; | ||||
| 182 | |||||||
| 183 | 35 | 50 | 85 | if ( exists $dispatch{ $self->{scheme} } ) { | |||
| 184 | 35 | 65 | $dispatch{ $self->{scheme} }->(); | ||||
| 185 | } | ||||||
| 186 | else { | ||||||
| 187 | 0 | 0 | croak "unknown color scheme name: " . $self->{scheme}; | ||||
| 188 | } | ||||||
| 189 | |||||||
| 190 | 35 | 37 | my @output; | ||||
| 191 | 35 | 77 | for my $i ( 0 .. $used_colors - 1 ) { | ||||
| 192 | 110 | 123 | for my $j ( 0 .. 3 ) { | ||||
| 193 | 440 | 731 | $output[ $i * 4 + $j ] | ||||
| 194 | = $self->{col}->[$i]->get_hex( $self->{web_safe}, $j ); | ||||||
| 195 | } | ||||||
| 196 | } | ||||||
| 197 | 35 | 700 | return @output; | ||||
| 198 | } | ||||||
| 199 | |||||||
| 200 | #pod =method colorset | ||||||
| 201 | #pod | ||||||
| 202 | #pod Returns a list of lists of the colors in groups of four. This method simply | ||||||
| 203 | #pod allows you to reference a color in the scheme by its group isntead of its | ||||||
| 204 | #pod absolute index in the list of colors. I am assuming that L<"colorset()"> | ||||||
| 205 | #pod will make it easier to use this module with the templating systems that are | ||||||
| 206 | #pod out there. | ||||||
| 207 | #pod | ||||||
| 208 | #pod For example, if you were to follow the synopsis, say you wanted to retrieve | ||||||
| 209 | #pod the two darkest colors from the first two groups of the scheme, which is | ||||||
| 210 | #pod typically the second color in the group. You could retrieve them with | ||||||
| 211 | #pod L<"colors()">: | ||||||
| 212 | #pod | ||||||
| 213 | #pod my $first_background = ($scheme->colors)[1]; | ||||||
| 214 | #pod my $second_background = ($scheme->colors)[5]; | ||||||
| 215 | #pod | ||||||
| 216 | #pod Or, with this method, | ||||||
| 217 | #pod | ||||||
| 218 | #pod my $first_background = $scheme->colorset->[0][1]; | ||||||
| 219 | #pod my $second_background = $scheme->colorset->[1][1]; | ||||||
| 220 | #pod | ||||||
| 221 | #pod =cut | ||||||
| 222 | |||||||
| 223 | sub colorset { | ||||||
| 224 | 6 | 6 | 1 | 9004 | my ($self) = @_; | ||
| 225 | 6 | 13 | my @flat_colors = $self->colors; | ||||
| 226 | 6 | 7 | my @grouped_colors; | ||||
| 227 | 6 | 90 | push @grouped_colors, [ splice @flat_colors, 0, 4 ] while @flat_colors; | ||||
| 228 | 6 | 28 | return \@grouped_colors; | ||||
| 229 | } | ||||||
| 230 | |||||||
| 231 | #pod =method from_hue | ||||||
| 232 | #pod | ||||||
| 233 | #pod $scheme->from_hue( $degrees ) | ||||||
| 234 | #pod | ||||||
| 235 | #pod Sets the base color hue, where C |
||||||
| 236 | #pod 359 and less than 0 wrap back around the wheel.) | ||||||
| 237 | #pod | ||||||
| 238 | #pod The default base hue is 0, or bright red. | ||||||
| 239 | #pod | ||||||
| 240 | #pod =cut | ||||||
| 241 | |||||||
| 242 | sub from_hue { | ||||||
| 243 | 10 | 10 | 1 | 26 | my ( $self, $h ) = @_; | ||
| 244 | 10 | 50 | 21 | croak "variation needs an argument" unless defined $h; | |||
| 245 | 10 | 43 | $self->{col}->[0]->set_hue($h); | ||||
| 246 | 10 | 15 | return $self; | ||||
| 247 | } | ||||||
| 248 | |||||||
| 249 | #pod =method from_hex | ||||||
| 250 | #pod | ||||||
| 251 | #pod $scheme->from_hex( $color ) | ||||||
| 252 | #pod | ||||||
| 253 | #pod Sets the base color to the given color, where C |
||||||
| 254 | #pod form RRGGBB. C |
||||||
| 255 | #pod | ||||||
| 256 | #pod The default base color is the equivalent of #ff0000, or bright red. | ||||||
| 257 | #pod | ||||||
| 258 | #pod =cut | ||||||
| 259 | |||||||
| 260 | sub from_hex { | ||||||
| 261 | 4 | 4 | 1 | 14 | my ( $self, $hex ) = @_; | ||
| 262 | 4 | 50 | 14 | croak "from_hex needs an argument" unless defined $hex; | |||
| 263 | 4 | 50 | 28 | croak "from_hex($hex) - argument must be in the form of RRGGBB" | |||
| 264 | unless $hex =~ / ^ ( [0-9A-F]{2} ) {3} $ /ismx; | ||||||
| 265 | |||||||
| 266 | 4 | 12 | $hex =~ m/(..)(..)(..)/; | ||||
| 267 | 4 | 9 | my ( $r, $g, $b ) = map {hex} ( $1, $2, $3 ); | ||||
| 12 | 30 | ||||||
| 268 | |||||||
| 269 | my $rgb2hsv = sub { | ||||||
| 270 | 100 | 100 | 86 | my ( $r, $g, $b ) = @_; | |||
| 271 | |||||||
| 272 | 100 | 140 | my $min = min( $r, $g, $b ); | ||||
| 273 | 100 | 109 | my $max = max( $r, $g, $b ); | ||||
| 274 | 100 | 90 | my $d = $max - $min; | ||||
| 275 | 100 | 71 | my $v = $max; | ||||
| 276 | |||||||
| 277 | 100 | 50 | 130 | my $s = | |||
| 278 | ( $d > 0 ) | ||||||
| 279 | ? ( $d / $max ) | ||||||
| 280 | : return ( 0, 0, $v ); | ||||||
| 281 | |||||||
| 282 | 100 | 100 | 182 | my $h = | |||
| 100 | |||||||
| 283 | ( $r == $max ) ? ( ( $g - $b ) / $d ) | ||||||
| 284 | : ( $g == $max ) ? ( 2 + ( $b - $r ) / $d ) | ||||||
| 285 | : ( 4 + ( $r - $g ) / $d ); | ||||||
| 286 | 100 | 78 | $h *= 60; | ||||
| 287 | 100 | 74 | $h %= 360; | ||||
| 288 | |||||||
| 289 | 100 | 181 | return ( $h, $s, $v ); | ||||
| 290 | 4 | 25 | }; | ||||
| 291 | |||||||
| 292 | 4 | 8 | my @hsv = $rgb2hsv->( map { $_ / 255 } ( $r, $g, $b ) ); | ||||
| 12 | 22 | ||||||
| 293 | 4 | 10 | my $h0 = $hsv[0]; | ||||
| 294 | 4 | 5 | my $h1 = 0; | ||||
| 295 | 4 | 5 | my $h2 = 1000; | ||||
| 296 | 4 | 5 | my ( $i1, $i2, $h, $s, $v ); | ||||
| 297 | |||||||
| 298 | 4 | 53 | foreach my $i ( sort keys %COLOR_WHEEL ) { | ||||
| 299 | 96 | 97 | my $c = $COLOR_WHEEL{$i}; | ||||
| 300 | 96 | 108 | my @hsv1 = $rgb2hsv->( map { $_ / 255 } @$c[ 0 .. 2 ] ); | ||||
| 288 | 386 | ||||||
| 301 | 96 | 95 | $h = $hsv1[0]; | ||||
| 302 | 96 | 100 | 66 | 281 | if ( $h >= $h1 and $h <= $h0 ) { | ||
| 303 | 8 | 11 | $h1 = $h; | ||||
| 304 | 8 | 8 | $i1 = $i; | ||||
| 305 | } | ||||||
| 306 | 96 | 100 | 100 | 233 | if ( $h <= $h2 and $h >= $h0 ) { | ||
| 307 | 5 | 7 | $h2 = $h; | ||||
| 308 | 5 | 9 | $i2 = $i; | ||||
| 309 | } | ||||||
| 310 | } | ||||||
| 311 | |||||||
| 312 | 4 | 100 | 66 | 25 | if ( $h2 == 0 or $h2 > 360 ) { | ||
| 313 | 3 | 5 | $h2 = 360; | ||||
| 314 | 3 | 6 | $i2 = 360; | ||||
| 315 | } | ||||||
| 316 | |||||||
| 317 | 4 | 100 | 16 | my $k = ( $h2 != $h1 ) ? ( $h0 - $h1 ) / ( $h2 - $h1 ) : 0; | |||
| 318 | 4 | 19 | $h = _round( $i1 + $k * ( $i2 - $i1 ) ); | ||||
| 319 | 4 | 6 | $h %= 360; | ||||
| 320 | 4 | 6 | $s = $hsv[1]; | ||||
| 321 | 4 | 6 | $v = $hsv[2]; | ||||
| 322 | |||||||
| 323 | 4 | 15 | $self->from_hue($h); | ||||
| 324 | 4 | 23 | $self->_set_variant_preset( | ||||
| 325 | [ $s, $v, $s, $v * 0.7, $s * 0.25, 1, $s * 0.5, 1 ] ); | ||||||
| 326 | |||||||
| 327 | 4 | 31 | return $self; | ||||
| 328 | } | ||||||
| 329 | |||||||
| 330 | #pod =method add_complement | ||||||
| 331 | #pod | ||||||
| 332 | #pod $scheme->add_complement( $bool ) | ||||||
| 333 | #pod | ||||||
| 334 | #pod If C<$bool> is true, an extra set of colors will be produced using the | ||||||
| 335 | #pod complement of the selected color. | ||||||
| 336 | #pod | ||||||
| 337 | #pod This only works with the analogic color scheme. The default is false. | ||||||
| 338 | #pod | ||||||
| 339 | #pod =cut | ||||||
| 340 | |||||||
| 341 | sub add_complement { | ||||||
| 342 | 3 | 3 | 1 | 5 | my ( $self, $b ) = @_; | ||
| 343 | 3 | 50 | 47 | croak "add_complement needs an argument" unless defined $b; | |||
| 344 | 3 | 3 | $self->{add_complement} = $b; | ||||
| 345 | 3 | 7 | return $self; | ||||
| 346 | } | ||||||
| 347 | |||||||
| 348 | #pod =method web_safe | ||||||
| 349 | #pod | ||||||
| 350 | #pod $scheme->web_safe( $bool ) | ||||||
| 351 | #pod | ||||||
| 352 | #pod Sets whether the colors returned by L<"colors()"> or L<"colorset()"> will be | ||||||
| 353 | #pod web-safe. | ||||||
| 354 | #pod | ||||||
| 355 | #pod The default is false. | ||||||
| 356 | #pod | ||||||
| 357 | #pod =cut | ||||||
| 358 | |||||||
| 359 | sub web_safe { | ||||||
| 360 | 3 | 3 | 1 | 4 | my ( $self, $b ) = @_; | ||
| 361 | 3 | 50 | 7 | croak "web_safe needs an argument" unless defined $b; | |||
| 362 | 3 | 6 | $self->{web_safe} = $b; | ||||
| 363 | 3 | 5 | return $self; | ||||
| 364 | } | ||||||
| 365 | |||||||
| 366 | #pod =method distance | ||||||
| 367 | #pod | ||||||
| 368 | #pod $scheme->distance( $float ) | ||||||
| 369 | #pod | ||||||
| 370 | #pod C<$float> must be a value from 0 to 1. You might use this with the L<"triade">, | ||||||
| 371 | #pod L<"tetrade"> or L<"analogic"> color schemes. | ||||||
| 372 | #pod | ||||||
| 373 | #pod The default is 0.5. | ||||||
| 374 | #pod | ||||||
| 375 | #pod =cut | ||||||
| 376 | |||||||
| 377 | sub distance { | ||||||
| 378 | 4 | 4 | 1 | 6 | my ( $self, $d ) = @_; | ||
| 379 | 4 | 50 | 10 | croak "distance needs an argument" unless defined $d; | |||
| 380 | 4 | 50 | 12 | croak "distance($d) - argument must be >= 0" if $d < 0; | |||
| 381 | 4 | 50 | 10 | croak "distance($d) - argument must be <= 1" if $d > 1; | |||
| 382 | 4 | 7 | $self->{distance} = $d; | ||||
| 383 | 4 | 8 | return $self; | ||||
| 384 | } | ||||||
| 385 | |||||||
| 386 | #pod =method scheme | ||||||
| 387 | #pod | ||||||
| 388 | #pod $scheme->scheme( $name ) | ||||||
| 389 | #pod | ||||||
| 390 | #pod C<$name> must be a valid color scheme name. See L<"COLOR SCHEMES">. The default | ||||||
| 391 | #pod is L<"mono">. | ||||||
| 392 | #pod | ||||||
| 393 | #pod =cut | ||||||
| 394 | |||||||
| 395 | sub scheme { | ||||||
| 396 | 11 | 11 | 1 | 20162 | my ( $self, $name ) = @_; | ||
| 397 | 11 | 50 | 36 | croak "scheme needs an argument" unless defined $name; | |||
| 398 | 11 | 50 | 44 | croak "'$name' isn't a valid scheme name" unless exists $SCHEMES{$name}; | |||
| 399 | 11 | 21 | $self->{scheme} = $name; | ||||
| 400 | 11 | 32 | return $self; | ||||
| 401 | } | ||||||
| 402 | |||||||
| 403 | #pod =method variation | ||||||
| 404 | #pod | ||||||
| 405 | #pod $scheme->variation( $name ) | ||||||
| 406 | #pod | ||||||
| 407 | #pod C<$name> must be a valid color variation name. See L<"COLOR VARIATIONS">. | ||||||
| 408 | #pod | ||||||
| 409 | #pod =cut | ||||||
| 410 | |||||||
| 411 | sub variation { | ||||||
| 412 | 7 | 7 | 1 | 15 | my ( $self, $v ) = @_; | ||
| 413 | 7 | 50 | 21 | croak "variation needs an argument" unless defined $v; | |||
| 414 | 7 | 50 | 20 | croak "'$v' isn't a valid variation name" unless exists $PRESETS{$v}; | |||
| 415 | 7 | 18 | $self->_set_variant_preset( $PRESETS{$v} ); | ||||
| 416 | 7 | 12 | return $self; | ||||
| 417 | } | ||||||
| 418 | |||||||
| 419 | sub _set_variant_preset { | ||||||
| 420 | 11 | 11 | 16 | my ( $self, $p ) = @_; | |||
| 421 | 11 | 51 | $self->{col}->[$_]->set_variant_preset($p) for 0 .. 3; | ||||
| 422 | } | ||||||
| 423 | |||||||
| 424 | package | ||||||
| 425 | Color::Scheme::mutablecolor; | ||||||
| 426 | |||||||
| 427 | 4 | 4 | 9511 | use Carp; | |||
| 4 | 9 | ||||||
| 4 | 226 | ||||||
| 428 | 4 | 4 | 23 | use List::Util qw(min max); | |||
| 4 | 4 | ||||||
| 4 | 220 | ||||||
| 429 | 4 | 4 | 16 | use POSIX qw(floor); | |||
| 4 | 5 | ||||||
| 4 | 16 | ||||||
| 430 | |||||||
| 431 | 3084 | 3084 | 7614 | sub _round { floor( 0.5 + shift ) } | |||
| 432 | |||||||
| 433 | sub new { | ||||||
| 434 | 24 | 24 | 29 | my ( $class, $hue ) = @_; | |||
| 435 | 24 | 50 | 44 | carp "no hue specified" unless defined $hue; | |||
| 436 | 24 | 136 | my $self = bless { | ||||
| 437 | hue => 0, | ||||||
| 438 | saturation => [], | ||||||
| 439 | value => [], | ||||||
| 440 | base_red => 0, | ||||||
| 441 | base_green => 0, | ||||||
| 442 | base_blue => 0, | ||||||
| 443 | base_saturation => 0, | ||||||
| 444 | base_value => 0, | ||||||
| 445 | }, $class; | ||||||
| 446 | 24 | 47 | $self->set_hue($hue); | ||||
| 447 | 24 | 52 | $self->set_variant_preset( $PRESETS{default} ); | ||||
| 448 | 24 | 59 | return $self; | ||||
| 449 | } | ||||||
| 450 | |||||||
| 451 | sub rotate { | ||||||
| 452 | 78 | 78 | 78 | my ( $self, $angle ) = @_; | |||
| 453 | 78 | 99 | my $newhue = ( $self->{hue} + $angle ) % 360; | ||||
| 454 | 78 | 108 | $self->set_hue($newhue); | ||||
| 455 | } | ||||||
| 456 | |||||||
| 457 | sub get_hue { | ||||||
| 458 | 35 | 35 | 43 | my ($self) = @_; | |||
| 459 | 35 | 68 | $self->{hue}; | ||||
| 460 | } | ||||||
| 461 | |||||||
| 462 | sub set_hue { | ||||||
| 463 | 190 | 190 | 251 | my ( $self, $h ) = @_; | |||
| 464 | |||||||
| 465 | my $avrg = sub { | ||||||
| 466 | 950 | 950 | 838 | my ( $a, $b, $k ) = @_; | |||
| 467 | 950 | 1223 | return $a + _round( ( $b - $a ) * $k ); | ||||
| 468 | 190 | 512 | }; | ||||
| 469 | |||||||
| 470 | 190 | 242 | $self->{hue} = _round($h) % 360; | ||||
| 471 | 190 | 426 | my $d = $self->{hue} % 15 + ( $self->{hue} - floor( $self->{hue} ) ); | ||||
| 472 | 190 | 169 | my $k = $d / 15; | ||||
| 473 | |||||||
| 474 | 190 | 293 | my $derivative1 = $self->{hue} - floor($d); | ||||
| 475 | 190 | 175 | my $derivative2 = ( $derivative1 + 15 ) % 360; | ||||
| 476 | 190 | 212 | my $colorset1 = $COLOR_WHEEL{$derivative1}; | ||||
| 477 | 190 | 223 | my $colorset2 = $COLOR_WHEEL{$derivative2}; | ||||
| 478 | |||||||
| 479 | 190 | 462 | my %enum = ( red => 0, green => 1, blue => 2, value => 3 ); | ||||
| 480 | 190 | 407 | while ( my ( $color, $i ) = each %enum ) { | ||||
| 481 | 760 | 939 | $self->{"base_$color"} | ||||
| 482 | = $avrg->( $colorset1->[$i], $colorset2->[$i], $k ); | ||||||
| 483 | } | ||||||
| 484 | 190 | 210 | $self->{base_saturation} = $avrg->( 100, 100, $k ) / 100; | ||||
| 485 | 190 | 684 | $self->{base_value} /= 100; | ||||
| 486 | } | ||||||
| 487 | |||||||
| 488 | sub get_saturation { | ||||||
| 489 | 440 | 440 | 345 | my ( $self, $variation ) = @_; | |||
| 490 | 440 | 418 | my $x = $self->{saturation}->[$variation]; | ||||
| 491 | 440 | 100 | 509 | my $s = $x < 0 ? -$x * $self->{base_saturation} : $x; | |||
| 492 | 440 | 50 | 544 | $s = 1 if $s > 1; | |||
| 493 | 440 | 50 | 521 | $s = 0 if $s < 0; | |||
| 494 | 440 | 444 | return $s; | ||||
| 495 | } | ||||||
| 496 | |||||||
| 497 | sub get_value { | ||||||
| 498 | 440 | 440 | 398 | my ( $self, $variation ) = @_; | |||
| 499 | 440 | 457 | my $x = $self->{value}->[$variation]; | ||||
| 500 | 440 | 100 | 557 | my $v = $x < 0 ? -$x * $self->{base_value} : $x; | |||
| 501 | 440 | 50 | 577 | $v = 1 if $v > 1; | |||
| 502 | 440 | 50 | 516 | $v = 0 if $v < 0; | |||
| 503 | 440 | 512 | return $v; | ||||
| 504 | } | ||||||
| 505 | |||||||
| 506 | sub set_variant { | ||||||
| 507 | 272 | 272 | 381 | my ( $self, $variation, $s, $v ) = @_; | |||
| 508 | 272 | 261 | $self->{saturation}->[$variation] = $s; | ||||
| 509 | 272 | 571 | $self->{value}->[$variation] = $v; | ||||
| 510 | } | ||||||
| 511 | |||||||
| 512 | sub set_variant_preset { | ||||||
| 513 | 68 | 68 | 75 | my ( $self, $p ) = @_; | |||
| 514 | 68 | 160 | $self->set_variant( $_, $p->[ 2 * $_ ], $p->[ 2 * $_ + 1 ] ) for 0 .. 3; | ||||
| 515 | } | ||||||
| 516 | |||||||
| 517 | sub get_hex { | ||||||
| 518 | 440 | 440 | 368 | my ( $self, $web_safe, $variation ) = @_; | |||
| 519 | |||||||
| 520 | 440 | 423 | my $max = max( map { $self->{"base_$_"} } qw( red green blue ) ); | ||||
| 1320 | 1883 | ||||||
| 521 | 440 | 413 | my $min = min( map { $self->{"base_$_"} } qw( red green blue ) ); | ||||
| 1320 | 1596 | ||||||
| 522 | |||||||
| 523 | 440 | 50 | 803 | my $v = ( | |||
| 524 | $variation < 0 ? $self->{base_value} : $self->get_value($variation) ) | ||||||
| 525 | * 255; | ||||||
| 526 | 440 | 50 | 774 | my $s = ( | |||
| 527 | $variation < 0 | ||||||
| 528 | ? $self->{base_saturation} | ||||||
| 529 | : $self->get_saturation($variation) | ||||||
| 530 | ); | ||||||
| 531 | 440 | 50 | 622 | my $k = $max > 0 ? $v / $max : 0; | |||
| 532 | |||||||
| 533 | 1320 | 2134 | my @rgb = map { | ||||
| 534 | 440 | 400 | min( 255, _round( $v - ( $v - $self->{"base_$_"} * $k ) * $s ) ) | ||||
| 535 | } qw( red green blue ); | ||||||
| 536 | 440 | 100 | 680 | @rgb = map { _round( $_ / 51 ) * 51 } @rgb if $web_safe; | |||
| 624 | 661 | ||||||
| 537 | |||||||
| 538 | 440 | 1826 | return sprintf( '%02x' x @rgb, @rgb ); | ||||
| 539 | } | ||||||
| 540 | |||||||
| 541 | #pod =head1 COLOR SCHEMES | ||||||
| 542 | #pod | ||||||
| 543 | #pod The following documentation is adapated (and mostly copied verbatim) from the | ||||||
| 544 | #pod Color Schemes 2 help. Use one of these scheme names as an argument to the | ||||||
| 545 | #pod L<"scheme()"> method. | ||||||
| 546 | #pod | ||||||
| 547 | #pod =head2 monochromatic (or mono) | ||||||
| 548 | #pod | ||||||
| 549 | #pod "Monochormatic scheme is based on only one color tint, and uses only variations | ||||||
| 550 | #pod made by changing its saturation and brightness. Black and white colors are | ||||||
| 551 | #pod always added. The result is comfortable for eyes, even when using aggressive | ||||||
| 552 | #pod color. However, it's harder to find accents and highlights. | ||||||
| 553 | #pod | ||||||
| 554 | #pod "The application makes only several monochromatic variants of each color. You'll | ||||||
| 555 | #pod be able to make others - more or less saturated, lighter or darker. | ||||||
| 556 | #pod Monochromatic variations are made for each color in other schemes, too." | ||||||
| 557 | #pod | ||||||
| 558 | #pod =head2 contrast | ||||||
| 559 | #pod | ||||||
| 560 | #pod "Base color is supplemented with its complement (color on the opposite side of | ||||||
| 561 | #pod the wheel). One warm and one cold color is always created - we have to | ||||||
| 562 | #pod consider, which one will be dominant, and if the result should look warm, or | ||||||
| 563 | #pod cold. Suitable monochromatic variations of this two colors may be added to the | ||||||
| 564 | #pod scheme." | ||||||
| 565 | #pod | ||||||
| 566 | #pod =head2 triade | ||||||
| 567 | #pod | ||||||
| 568 | #pod "Base color is supplemented with two colors, placed identically on both sides of | ||||||
| 569 | #pod its complement. Unlike the 'sharp' contrast, this scheme is often more | ||||||
| 570 | #pod comfortable for the eyes, it's softer, and has more space for balancing warm | ||||||
| 571 | #pod and cold colors. | ||||||
| 572 | #pod | ||||||
| 573 | #pod "You can use the L<"distance()"> method to set the distance of these colors | ||||||
| 574 | #pod from the base color complement. The less the value is, the closer the colors | ||||||
| 575 | #pod are to the contrast color, and are more similar. The best value is between 0.25 | ||||||
| 576 | #pod and 0.5. Higher values aren't too suitable - except the shift by 60E<0x00B0>, | ||||||
| 577 | #pod which makes another color scheme, the triade: | ||||||
| 578 | #pod | ||||||
| 579 | #pod "The triade is made by three colors evenly distributed on the thirds of the | ||||||
| 580 | #pod color wheel (by 120 degrees). The triade-schemes are vibrating, full of energy, | ||||||
| 581 | #pod and have large space to make contrasts, accents and to balance warm and cold | ||||||
| 582 | #pod colors. You can make the triade in the 'soft contrast' scheme setting the | ||||||
| 583 | #pod distance to the maximal value, 1." | ||||||
| 584 | #pod | ||||||
| 585 | #pod =head2 tetrade | ||||||
| 586 | #pod | ||||||
| 587 | #pod "This scheme, also known as 'double-contrast,' is made by a pair of colors and | ||||||
| 588 | #pod their complements. It's based on the tetrade - the foursome of colors evenly | ||||||
| 589 | #pod distributed on the fourths of the color wheel (by 90 degreees). The tetrade is | ||||||
| 590 | #pod very aggressive color scheme, requiring very good planning and very sensitive | ||||||
| 591 | #pod approach to relations of these colors. | ||||||
| 592 | #pod | ||||||
| 593 | #pod "Less distance between two base colors causes less tension in the result. | ||||||
| 594 | #pod However, this scheme is always more 'nervous' and 'action' than other schemes. | ||||||
| 595 | #pod While working with it, we have to take care especially of relations between one | ||||||
| 596 | #pod color and the complement of its adjacent color - in case of the tetrade | ||||||
| 597 | #pod (maximum distance 1), good feeling and very sensitive approach are necessary." | ||||||
| 598 | #pod | ||||||
| 599 | #pod =head2 analogic | ||||||
| 600 | #pod | ||||||
| 601 | #pod "This scheme is made by base color and its adjacent colors - two colors | ||||||
| 602 | #pod identically on both sides. It always looks very elegantly and clear, the result | ||||||
| 603 | #pod has less tension and it's uniformly warm, or cold. If a color on the warm-cold | ||||||
| 604 | #pod border is chosen, the color with opposite 'temperature' may be used for | ||||||
| 605 | #pod accenting the other two colors. | ||||||
| 606 | #pod | ||||||
| 607 | #pod "You can set the distance of adjacent colors by using L<"distance()">. Values | ||||||
| 608 | #pod between 0.25 and 0.5 (15-30 degrees on the wheel) are optimal. You can also add | ||||||
| 609 | #pod the contrast color; the scheme is then supplemented with the complement of the | ||||||
| 610 | #pod base color. It must be treated only as a complement - it adds tension to the | ||||||
| 611 | #pod palette, and it's too aggressive when overused. However, used in details and as | ||||||
| 612 | #pod accent of main colors, it can be very effective and elegant." | ||||||
| 613 | #pod | ||||||
| 614 | #pod =head1 COLOR VARIATIONS | ||||||
| 615 | #pod | ||||||
| 616 | #pod "Each of colors in displayed scheme has four variations. These are colors of | ||||||
| 617 | #pod the same hue, but they differ in the saturation and brightness. ... The very | ||||||
| 618 | #pod first variation ... is the base variation, which determines the look of the | ||||||
| 619 | #pod scheme. The other three variations are just additional. Iff the scheme is made | ||||||
| 620 | #pod by less than four colors, the unused place is used to display variations (or | ||||||
| 621 | #pod the complement) of the base color." | ||||||
| 622 | #pod | ||||||
| 623 | #pod Use one of these variation names as an argument to the L<"variation()"> method. | ||||||
| 624 | #pod | ||||||
| 625 | #pod =head2 default | ||||||
| 626 | #pod | ||||||
| 627 | #pod The default preset. Generally pretty nice. | ||||||
| 628 | #pod | ||||||
| 629 | #pod =head2 pastel | ||||||
| 630 | #pod | ||||||
| 631 | #pod Softer colors with added whiteness. | ||||||
| 632 | #pod | ||||||
| 633 | #pod =head2 soft | ||||||
| 634 | #pod | ||||||
| 635 | #pod Darker pastel colors. | ||||||
| 636 | #pod | ||||||
| 637 | #pod =head2 light | ||||||
| 638 | #pod | ||||||
| 639 | #pod Very light, almost washed-out colors. | ||||||
| 640 | #pod | ||||||
| 641 | #pod =head2 hard | ||||||
| 642 | #pod | ||||||
| 643 | #pod Deeper, more-saturated colors. | ||||||
| 644 | #pod | ||||||
| 645 | #pod =head2 pale | ||||||
| 646 | #pod | ||||||
| 647 | #pod Greyer, less-saturated colors. | ||||||
| 648 | #pod | ||||||
| 649 | #pod =head1 CREDIT | ||||||
| 650 | #pod | ||||||
| 651 | #pod Color Schemes 2, its documentation and original JavaScript code are copyright | ||||||
| 652 | #pod pixy L |
||||||
| 653 | #pod | ||||||
| 654 | #pod The author has explicitly granted license for this distribution of code to be | ||||||
| 655 | #pod redistribute as specified in the L |
||||||
| 656 | #pod | ||||||
| 657 | #pod =cut | ||||||
| 658 | |||||||
| 659 | 1; | ||||||
| 660 | |||||||
| 661 | __END__ |