Codebase list libchart-perl / ad3af42
Update upstream source from tag 'upstream/2.403.8' Update to upstream version '2.403.8' with Debian dir cace1b0746386ae76a3d9d7bfb3fa7010829ac31 gregor herrmann 1 year, 5 months ago
36 changed file(s) with 108 addition(s) and 2687 deletion(s). Raw diff Collapse all Expand all
0 2.403.8 2022-10-22 lichtkind
1 --------
2 * = POD rewrite release
3 * \ moved color code to Graphics::Toolkit::Color
4 * ? adapted POD
5
06 2.403.7 2022-07-29 lichtkind
17 -------
28 * = POD rewrite release
1313 lib/Chart/Bars.pm
1414 lib/Chart/Base.pm
1515 lib/Chart/BrushStyles.pm
16 lib/Chart/Color.pm
17 lib/Chart/Color/Constant.pm
18 lib/Chart/Color/Value.pm
1916 lib/Chart/Composite.pm
2017 lib/Chart/Constants.pm
2118 lib/Chart/Direction.pm
3330 lib/Chart/Pie.pm
3431 lib/Chart/Points.pm
3532 lib/Chart/Property.pm
33 lib/Chart/Property/DataType/Color.pm
3634 lib/Chart/Property/DataType/Font.pm
3735 lib/Chart/Split.pm
3836 lib/Chart/StackedBars.pm
39 t/001_color_value.t
40 t/002_color_constant.t
41 t/003_color.t
4237 t/Humidity.t
4338 t/Math_1_over_x.t
4439 t/bars.t
1717 "t"
1818 ],
1919 "namespace" : [
20 "Chart::Color",
21 "Chart::Manual"
20 "Chart::Manual",
21 "Chart::Property"
2222 ],
2323 "package" : [
2424 "Chart::Base",
3636 "requires" : {
3737 "Carp" : "1.35",
3838 "GD" : "2",
39 "Graphics::Toolkit::Color" : "1",
3940 "perl" : "v5.12.0"
4041 },
4142 "suggests" : {
5354 "provides" : {
5455 "Chart" : {
5556 "file" : "lib/Chart.pm",
56 "version" : "v2.403.7"
57 "version" : "v2.403.8"
5758 },
5859 "Chart::Bars" : {
5960 "file" : "lib/Chart/Bars.pm",
60 "version" : "v2.403.7"
61 },
62 "Chart::Color" : {
63 "file" : "lib/Chart/Color.pm",
64 "version" : "v2.403.7"
61 "version" : "v2.403.8"
6562 },
6663 "Chart::Composite" : {
6764 "file" : "lib/Chart/Composite.pm",
68 "version" : "v2.403.7"
65 "version" : "v2.403.8"
6966 },
7067 "Chart::Direction" : {
7168 "file" : "lib/Chart/Direction.pm",
72 "version" : "v2.403.7"
69 "version" : "v2.403.8"
7370 },
7471 "Chart::ErrorBars" : {
7572 "file" : "lib/Chart/ErrorBars.pm",
76 "version" : "v2.403.7"
73 "version" : "v2.403.8"
7774 },
7875 "Chart::HorizontalBars" : {
7976 "file" : "lib/Chart/HorizontalBars.pm",
80 "version" : "v2.403.7"
77 "version" : "v2.403.8"
8178 },
8279 "Chart::Lines" : {
8380 "file" : "lib/Chart/Lines.pm",
84 "version" : "v2.403.7"
81 "version" : "v2.403.8"
8582 },
8683 "Chart::LinesPoints" : {
8784 "file" : "lib/Chart/LinesPoints.pm",
88 "version" : "v2.403.7"
85 "version" : "v2.403.8"
8986 },
9087 "Chart::Mountain" : {
9188 "file" : "lib/Chart/Mountain.pm",
92 "version" : "v2.403.7"
89 "version" : "v2.403.8"
9390 },
9491 "Chart::Pareto" : {
9592 "file" : "lib/Chart/Pareto.pm",
96 "version" : "v2.403.7"
93 "version" : "v2.403.8"
9794 },
9895 "Chart::Pie" : {
9996 "file" : "lib/Chart/Pie.pm",
100 "version" : "v2.403.7"
97 "version" : "v2.403.8"
10198 },
10299 "Chart::Points" : {
103100 "file" : "lib/Chart/Points.pm",
104 "version" : "v2.403.7"
101 "version" : "v2.403.8"
105102 },
106103 "Chart::Property" : {
107104 "file" : "lib/Chart/Property.pm",
108 "version" : "v2.403.7"
109 },
110 "Chart::Property::DataType::Font" : {
111 "file" : "lib/Chart/Property/DataType/Font.pm",
112 "version" : "v2.403.7"
105 "version" : "v2.403.8"
113106 },
114107 "Chart::Split" : {
115108 "file" : "lib/Chart/Split.pm",
116 "version" : "v2.403.7"
109 "version" : "v2.403.8"
117110 },
118111 "Chart::StackedBars" : {
119112 "file" : "lib/Chart/StackedBars.pm",
120 "version" : "v2.403.7"
113 "version" : "v2.403.8"
121114 }
122115 },
123116 "release_status" : "stable",
128121 "web" : "https://github.com/lichtkind/Chart"
129122 }
130123 },
131 "version" : "v2.403.7",
124 "version" : "v2.403.8",
132125 "x_generated_by_perl" : "v5.30.0",
133126 "x_maintainers" : [
134127 "Herbert Breunung <lichtkind@cpan.org>"
1818 directory:
1919 - t
2020 namespace:
21 - Chart::Color
2221 - Chart::Manual
22 - Chart::Property
2323 package:
2424 - Chart::Base
2525 - Chart::BrushStyles
2727 provides:
2828 Chart:
2929 file: lib/Chart.pm
30 version: v2.403.7
30 version: v2.403.8
3131 Chart::Bars:
3232 file: lib/Chart/Bars.pm
33 version: v2.403.7
34 Chart::Color:
35 file: lib/Chart/Color.pm
36 version: v2.403.7
33 version: v2.403.8
3734 Chart::Composite:
3835 file: lib/Chart/Composite.pm
39 version: v2.403.7
36 version: v2.403.8
4037 Chart::Direction:
4138 file: lib/Chart/Direction.pm
42 version: v2.403.7
39 version: v2.403.8
4340 Chart::ErrorBars:
4441 file: lib/Chart/ErrorBars.pm
45 version: v2.403.7
42 version: v2.403.8
4643 Chart::HorizontalBars:
4744 file: lib/Chart/HorizontalBars.pm
48 version: v2.403.7
45 version: v2.403.8
4946 Chart::Lines:
5047 file: lib/Chart/Lines.pm
51 version: v2.403.7
48 version: v2.403.8
5249 Chart::LinesPoints:
5350 file: lib/Chart/LinesPoints.pm
54 version: v2.403.7
51 version: v2.403.8
5552 Chart::Mountain:
5653 file: lib/Chart/Mountain.pm
57 version: v2.403.7
54 version: v2.403.8
5855 Chart::Pareto:
5956 file: lib/Chart/Pareto.pm
60 version: v2.403.7
57 version: v2.403.8
6158 Chart::Pie:
6259 file: lib/Chart/Pie.pm
63 version: v2.403.7
60 version: v2.403.8
6461 Chart::Points:
6562 file: lib/Chart/Points.pm
66 version: v2.403.7
63 version: v2.403.8
6764 Chart::Property:
6865 file: lib/Chart/Property.pm
69 version: v2.403.7
70 Chart::Property::DataType::Font:
71 file: lib/Chart/Property/DataType/Font.pm
72 version: v2.403.7
66 version: v2.403.8
7367 Chart::Split:
7468 file: lib/Chart/Split.pm
75 version: v2.403.7
69 version: v2.403.8
7670 Chart::StackedBars:
7771 file: lib/Chart/StackedBars.pm
78 version: v2.403.7
72 version: v2.403.8
7973 requires:
8074 Carp: '1.35'
8175 GD: '2'
76 Graphics::Toolkit::Color: '1'
8277 perl: v5.12.0
8378 resources:
8479 repository: git://github.com/lichtkind/Chart.git
85 version: v2.403.7
80 version: v2.403.8
8681 x_generated_by_perl: v5.30.0
8782 x_maintainers:
8883 - 'Herbert Breunung <lichtkind@cpan.org>'
1717 "NAME" => "Chart",
1818 "PREREQ_PM" => {
1919 "Carp" => "1.35",
20 "GD" => 2
20 "GD" => 2,
21 "Graphics::Toolkit::Color" => 1
2122 },
2223 "TEST_REQUIRES" => {
2324 "File::Temp" => "0.19",
2425 "Test::More" => "1.3",
2526 "Test::Warn" => "0.30"
2627 },
27 "VERSION" => "v2.403.7",
28 "VERSION" => "v2.403.8",
2829 "test" => {
2930 "TESTS" => "t/*.t"
3031 }
3536 "Carp" => "1.35",
3637 "File::Temp" => "0.19",
3738 "GD" => 2,
39 "Graphics::Toolkit::Color" => 1,
3840 "Test::More" => "1.3",
3941 "Test::Warn" => "0.30"
4042 );
22
33 requires "Carp" => "1.35";
44 requires "GD" => "2";
5 requires "Graphics::Toolkit::Color" => "1";
56 requires "perl" => "v5.12.0";
67 suggests "Graphics::ColorNames" => "0";
78
1414 Carp = 1.35
1515 ;List::Util = 1.2
1616 GD = 2
17 Graphics::Toolkit::Color = 1
1718
1819 [Prereqs / RuntimeSuggests]
1920 Graphics::ColorNames = 0
2324 Test::More = 1.3
2425 Test::Warn = 0.30
2526
26
2727 [MetaNoIndex]
2828 directory = t
2929 package = Chart::Base
3030 package = Chart::Constants
3131 package = Chart::BrushStyles
32 ;package = Chart::Font
33 ;namespace = Chart::Setting
34 namespace = Chart::Color
32 namespace = Chart::Property
3533 namespace = Chart::Manual
3634
3735 ; pollutes meta section 'provides'
4846 ; use RewriteVersion or VersionFromModule
4947 ;[VersionFromModule]
5048 [RewriteVersion]
49 allow_decimal_underscore = 1
50
5151 [Repository]
5252 [AbstractFromPOD]
5353 [Pod2Readme]
44
55 package Chart::Bars;
66 our @ISA = qw(Chart::Base);
7 our $VERSION = 'v2.403.7';
7 our $VERSION = 'v2.403.8';
88
99 use Chart::Base;
1010 use GD;
44 use v5.12;
55
66 package Chart::Base;
7 our $VERSION = 'v2.403.7';
7 our $VERSION = 'v2.403.8';
88
99 use FileHandle;
1010 use Carp;
1111 use GD;
1212 use GD::Image;
13 use Chart::Color;
14 #use Property::DataType::Font;
13 use Chart::Property::DataType::Color;
14 #use Chart::Property::DataType::Font;
1515
1616 #>>>>>>>>>>>>>>>>>>>>>>>>>>#
1717 # public methods #
11611161
11621162 sub _color_spec_to_rgb {
11631163 my ($self, $role, $spec) = @_; # color role name (from set) for error msg
1164 my $color = Chart::Color->new( $spec );
1164 my $color = Chart::Property::DataType::Color->new( $spec );
11651165 return croak "Unrecognized color for $role\n" unless ref $color;
11661166 $color->rgb;
11671167 }
12451245
12461246 # now loop through the rest of them
12471247 # (the font is decreased in width and height by 1
1248 if ( $w > 1 ) { $w--; }
1249 if ( $h > 1 ) { $h--; }
1250 for ( 1 .. $#lines )
1251 {
1248 if ( $w > 1 ) { $w-- }
1249 if ( $h > 1 ) { $h-- }
1250 for ( 1 .. $#lines ) {
12521251 $self->{'curr_y_min'} += $self->{'text_space'} + $h;
12531252 $x = ( $self->{'curr_x_max'} - $self->{'curr_x_min'} ) / 2 + $self->{'curr_x_min'} - ( length( $lines[$_] ) * $w ) / 2;
12541253 $y = $self->{'curr_y_min'} + $self->{'text_space'};
33 use v5.12;
44
55 package Chart::BrushStyles;
6 our $VERSION = 'v2.403.7';
6 our $VERSION = 'v2.403.8';
77
88 use Carp;
99 use GD;
+0
-1136
lib/Chart/Color/Constant.pm less more
0 use v5.12;
1
2 # named colors from X11, HTML (SVG) standard and Pantone report
3
4 package Chart::Color::Constant;
5 our $VERSION = 'v2.403.7';
6 use Carp;
7 use Chart::Color::Value;
8
9 our %rgbhsl_from_name = ( # 2.6 MB
10 # http://en.wikipedia.org/wiki/Web_colors#X11_color_names
11 'white' => [ 255, 255, 255, 0, 0, 100 ],
12 'black' => [ 0, 0, 0, 0, 0, 0 ],
13 'red' => [ 255, 0, 0, 0, 100, 50 ],
14 'green' => [ 0, 128, 0, 120, 100, 25 ],
15 'blue' => [ 0, 0, 255, 240, 100, 50 ],
16 'yellow' => [ 255, 255, 0, 60, 100, 50 ],
17 'purple' => [ 128, 0, 128, 300, 100, 25 ],
18 'pink' => [ 255, 192, 203, 350, 100, 88 ],
19 'peach' => [ 250, 125, 125, 0, 93, 74 ],
20 'plum' => [ 221, 160, 221, 300, 47, 75 ],
21 'mauve' => [ 200, 125, 125, 0, 41, 64 ],
22 'brown' => [ 165, 42, 42, 0, 59, 41 ],
23 'grey' => [ 225, 225, 225, 0, 0, 88 ],
24 'aliceblue' => [ 240, 248, 255, 208, 100, 97 ],
25 'antiquewhite' => [ 250, 235, 215, 34, 78, 91 ],
26 'antiquewhite1' => [ 255, 239, 219, 33, 100, 93 ],
27 'antiquewhite2' => [ 238, 223, 204, 34, 50, 87 ],
28 'antiquewhite3' => [ 205, 192, 176, 33, 22, 75 ],
29 'antiquewhite4' => [ 139, 131, 120, 35, 8, 51 ],
30 'aqua' => [ 0, 255, 255, 180, 100, 50 ],
31 'aquamarine' => [ 127, 255, 212, 160, 100, 75 ],
32 'aquamarine1' => [ 127, 255, 212, 160, 100, 75 ],
33 'aquamarine2' => [ 118, 238, 198, 160, 78, 70 ],
34 'aquamarine3' => [ 102, 205, 170, 160, 51, 60 ], # not in X11
35 'aquamarine4' => [ 69, 139, 116, 160, 34, 41 ],
36 'azure' => [ 240, 255, 255, 180, 100, 97 ],
37 'azure1' => [ 240, 255, 255, 180, 100, 97 ],
38 'azure2' => [ 224, 238, 238, 180, 29, 91 ],
39 'azure3' => [ 193, 205, 205, 180, 11, 78 ],
40 'azure4' => [ 131, 139, 139, 180, 3, 53 ],
41 'beige' => [ 245, 245, 220, 60, 56, 91 ],
42 'bisque' => [ 255, 228, 196, 33, 100, 88 ],
43 'bisque1' => [ 255, 228, 196, 33, 100, 88 ],
44 'bisque2' => [ 238, 213, 183, 33, 62, 83 ],
45 'bisque3' => [ 205, 183, 158, 32, 32, 71 ],
46 'bisque4' => [ 139, 125, 107, 34, 13, 48 ],
47 'blanchedalmond' => [ 255, 235, 205, 36, 100, 90 ],
48 'blue1' => [ 0, 0, 255, 240, 100, 50 ],
49 'blue2' => [ 0, 0, 238, 240, 100, 47 ],
50 'blue3' => [ 0, 0, 205, 240, 100, 40 ],
51 'blue4' => [ 0, 0, 139, 240, 100, 27 ],
52 'blueviolet' => [ 138, 43, 226, 271, 76, 53 ],
53 'brown1' => [ 255, 64, 64, 0, 100, 63 ],
54 'brown2' => [ 238, 59, 59, 0, 84, 58 ],
55 'brown3' => [ 205, 51, 51, 0, 61, 50 ],
56 'brown4' => [ 139, 35, 35, 0, 60, 34 ],
57 'burlywood' => [ 222, 184, 135, 34, 57, 70 ],
58 'burlywood1' => [ 255, 211, 155, 34, 100, 80 ],
59 'burlywood2' => [ 238, 197, 145, 34, 73, 75 ],
60 'burlywood3' => [ 205, 170, 125, 34, 44, 65 ],
61 'burlywood4' => [ 139, 115, 85, 33, 24, 44 ],
62 'cadetblue' => [ 95, 158, 160, 182, 25, 50 ],
63 'cadetblue1' => [ 152, 245, 255, 186, 100, 80 ],
64 'cadetblue2' => [ 142, 229, 238, 186, 74, 75 ],
65 'cadetblue3' => [ 122, 197, 205, 186, 45, 64 ],
66 'cadetblue4' => [ 83, 134, 139, 185, 25, 44 ],
67 'chartreuse' => [ 127, 255, 0, 90, 100, 50 ],
68 'chartreuse1' => [ 127, 255, 0, 90, 100, 50 ],
69 'chartreuse2' => [ 118, 238, 0, 90, 100, 47 ],
70 'chartreuse3' => [ 102, 205, 0, 90, 100, 40 ],
71 'chartreuse4' => [ 69, 139, 0, 90, 100, 27 ],
72 'chocolate' => [ 210, 105, 30, 25, 75, 47 ],
73 'chocolate1' => [ 255, 127, 36, 25, 100, 57 ],
74 'chocolate2' => [ 238, 118, 33, 25, 86, 53 ],
75 'chocolate3' => [ 205, 102, 29, 25, 75, 46 ],
76 'chocolate4' => [ 139, 69, 19, 25, 76, 31 ],
77 'coral' => [ 255, 127, 80, 16, 100, 66 ],
78 'coral1' => [ 255, 114, 86, 10, 100, 67 ],
79 'coral2' => [ 238, 106, 80, 10, 82, 62 ],
80 'coral3' => [ 205, 91, 69, 10, 58, 54 ],
81 'coral4' => [ 139, 62, 47, 10, 49, 36 ],
82 'cornflowerblue' => [ 100, 149, 237, 219, 79, 66 ],
83 'cornsilk' => [ 255, 248, 220, 48, 100, 93 ],
84 'cornsilk1' => [ 255, 248, 220, 48, 100, 93 ],
85 'cornsilk2' => [ 238, 232, 205, 49, 49, 87 ],
86 'cornsilk3' => [ 205, 200, 177, 49, 22, 75 ],
87 'cornsilk4' => [ 139, 136, 120, 51, 8, 51 ],
88 'crimson' => [ 220, 20, 60, 348, 83, 47 ],
89 'cyan' => [ 0, 255, 255, 180, 100, 50 ],
90 'cyan1' => [ 0, 255, 255, 180, 100, 50 ],
91 'cyan2' => [ 0, 238, 238, 180, 100, 47 ],
92 'cyan3' => [ 0, 205, 205, 180, 100, 40 ],
93 'cyan4' => [ 0, 139, 139, 180, 100, 27 ],
94 'darkblue' => [ 0, 0, 139, 240, 100, 27 ],
95 'darkcyan' => [ 0, 139, 139, 180, 100, 27 ],
96 'darkgoldenrod' => [ 184, 134, 11, 43, 89, 38 ],
97 'darkgoldenrod1' => [ 255, 185, 15, 43, 100, 53 ],
98 'darkgoldenrod2' => [ 238, 173, 14, 43, 89, 49 ],
99 'darkgoldenrod3' => [ 205, 149, 12, 43, 89, 43 ],
100 'darkgoldenrod4' => [ 139, 101, 8, 43, 89, 29 ],
101 'darkgray' => [ 169, 169, 169, 0, 0, 66 ],
102 'darkgreen' => [ 0, 100, 0, 120, 100, 20 ],
103 'darkkhaki' => [ 189, 183, 107, 56, 38, 58 ],
104 'darkmagenta' => [ 139, 0, 139, 300, 100, 27 ],
105 'darkolivegreen' => [ 85, 107, 47, 82, 39, 30 ],
106 'darkolivegreen1' => [ 202, 255, 112, 82, 100, 72 ],
107 'darkolivegreen2' => [ 188, 238, 104, 82, 80, 67 ],
108 'darkolivegreen3' => [ 162, 205, 90, 82, 53, 58 ],
109 'darkolivegreen4' => [ 110, 139, 61, 82, 39, 39 ],
110 'darkorange' => [ 255, 140, 0, 33, 100, 50 ],
111 'darkorange1' => [ 255, 127, 0, 30, 100, 50 ],
112 'darkorange2' => [ 238, 118, 0, 30, 100, 47 ],
113 'darkorange3' => [ 205, 102, 0, 30, 100, 40 ],
114 'darkorange4' => [ 139, 69, 0, 30, 100, 27 ],
115 'darkorchid' => [ 153, 50, 204, 280, 61, 50 ],
116 'darkorchid1' => [ 191, 62, 255, 280, 100, 62 ],
117 'darkorchid2' => [ 178, 58, 238, 280, 84, 58 ],
118 'darkorchid3' => [ 154, 50, 205, 280, 61, 50 ],
119 'darkorchid4' => [ 104, 34, 139, 280, 61, 34 ],
120 'darkred' => [ 139, 0, 0, 0, 100, 27 ],
121 'darksalmon' => [ 233, 150, 122, 15, 72, 70 ],
122 'darkseagreen' => [ 143, 188, 143, 120, 25, 65 ],
123 'darkseagreen1' => [ 193, 255, 193, 120, 100, 88 ],
124 'darkseagreen2' => [ 180, 238, 180, 120, 63, 82 ],
125 'darkseagreen3' => [ 155, 205, 155, 120, 33, 71 ],
126 'darkseagreen4' => [ 105, 139, 105, 120, 14, 48 ],
127 'darkslateblue' => [ 72, 61, 139, 248, 39, 39 ],
128 'darkslategray' => [ 47, 79, 79, 180, 25, 25 ],
129 'darkslategray1' => [ 151, 255, 255, 180, 100, 80 ],
130 'darkslategray2' => [ 141, 238, 238, 180, 74, 74 ],
131 'darkslategray3' => [ 121, 205, 205, 180, 46, 64 ],
132 'darkslategray4' => [ 82, 139, 139, 180, 26, 43 ],
133 'darkturquoise' => [ 0, 206, 209, 181, 100, 41 ],
134 'darkviolet' => [ 148, 0, 211, 282, 100, 41 ],
135 'deeppink' => [ 255, 20, 147, 328, 100, 54 ],
136 'deeppink1' => [ 255, 20, 147, 328, 100, 54 ],
137 'deeppink2' => [ 238, 18, 137, 328, 87, 50 ],
138 'deeppink3' => [ 205, 16, 118, 328, 86, 43 ],
139 'deeppink4' => [ 139, 10, 80, 327, 87, 29 ],
140 'deepskyblue' => [ 0, 191, 255, 195, 100, 50 ],
141 'deepskyblue1' => [ 0, 191, 255, 195, 100, 50 ],
142 'deepskyblue2' => [ 0, 178, 238, 195, 100, 47 ],
143 'deepskyblue3' => [ 0, 154, 205, 195, 100, 40 ],
144 'deepskyblue4' => [ 0, 104, 139, 195, 100, 27 ],
145 'dimgray' => [ 105, 105, 105, 0, 0, 41 ],
146 'dodgerblue' => [ 30, 144, 255, 210, 100, 56 ],
147 'dodgerblue1' => [ 30, 144, 255, 210, 100, 56 ],
148 'dodgerblue2' => [ 28, 134, 238, 210, 86, 52 ],
149 'dodgerblue3' => [ 24, 116, 205, 210, 79, 45 ],
150 'dodgerblue4' => [ 16, 78, 139, 210, 79, 30 ],
151 'firebrick' => [ 178, 34, 34, 0, 68, 42 ],
152 'firebrick1' => [ 255, 48, 48, 0, 100, 59 ],
153 'firebrick2' => [ 238, 44, 44, 0, 85, 55 ],
154 'firebrick3' => [ 205, 38, 38, 0, 69, 48 ],
155 'firebrick4' => [ 139, 26, 26, 0, 68, 32 ],
156 'floralwhite' => [ 255, 250, 240, 40, 100, 97 ],
157 'forestgreen' => [ 34, 139, 34, 120, 61, 34 ],
158 'fuchsia' => [ 255, 0, 255, 300, 100, 50 ],
159 'gainsboro' => [ 220, 220, 220, 0, 0, 86 ],
160 'ghostwhite' => [ 248, 248, 255, 240, 100, 99 ],
161 'gold' => [ 255, 215, 0, 51, 100, 50 ],
162 'gold1' => [ 255, 215, 0, 51, 100, 50 ],
163 'gold2' => [ 238, 201, 0, 51, 100, 47 ],
164 'gold3' => [ 205, 173, 0, 51, 100, 40 ],
165 'gold4' => [ 139, 117, 0, 51, 100, 27 ],
166 'goldenrod' => [ 218, 165, 32, 43, 74, 49 ],
167 'goldenrod1' => [ 255, 193, 37, 43, 100, 57 ],
168 'goldenrod2' => [ 238, 180, 34, 43, 86, 53 ],
169 'goldenrod3' => [ 205, 155, 29, 43, 75, 46 ],
170 'goldenrod4' => [ 139, 105, 20, 43, 75, 31 ],
171 'gray' => [ 128, 128, 128, 0, 0, 50 ],
172 'gray1' => [ 3, 3, 3, 0, 0, 1 ],
173 'gray2' => [ 5, 5, 5, 0, 0, 2 ],
174 'gray3' => [ 8, 8, 8, 0, 0, 3 ],
175 'gray4' => [ 10, 10, 10, 0, 0, 4 ],
176 'gray5' => [ 13, 13, 13, 0, 0, 5 ],
177 'gray6' => [ 15, 15, 15, 0, 0, 6 ],
178 'gray7' => [ 18, 18, 18, 0, 0, 7 ],
179 'gray8' => [ 20, 20, 20, 0, 0, 8 ],
180 'gray9' => [ 23, 23, 23, 0, 0, 9 ],
181 'gray10' => [ 26, 26, 26, 0, 0, 10 ],
182 'gray11' => [ 28, 28, 28, 0, 0, 11 ],
183 'gray12' => [ 31, 31, 31, 0, 0, 12 ],
184 'gray13' => [ 33, 33, 33, 0, 0, 13 ],
185 'gray14' => [ 36, 36, 36, 0, 0, 14 ],
186 'gray15' => [ 38, 38, 38, 0, 0, 15 ],
187 'gray16' => [ 41, 41, 41, 0, 0, 16 ],
188 'gray17' => [ 43, 43, 43, 0, 0, 17 ],
189 'gray18' => [ 46, 46, 46, 0, 0, 18 ],
190 'gray19' => [ 48, 48, 48, 0, 0, 19 ],
191 'gray20' => [ 51, 51, 51, 0, 0, 20 ],
192 'gray21' => [ 54, 54, 54, 0, 0, 21 ],
193 'gray22' => [ 56, 56, 56, 0, 0, 22 ],
194 'gray23' => [ 59, 59, 59, 0, 0, 23 ],
195 'gray24' => [ 61, 61, 61, 0, 0, 24 ],
196 'gray25' => [ 64, 64, 64, 0, 0, 25 ],
197 'gray26' => [ 66, 66, 66, 0, 0, 26 ],
198 'gray27' => [ 69, 69, 69, 0, 0, 27 ],
199 'gray28' => [ 71, 71, 71, 0, 0, 28 ],
200 'gray29' => [ 74, 74, 74, 0, 0, 29 ],
201 'gray30' => [ 77, 77, 77, 0, 0, 30 ],
202 'gray31' => [ 79, 79, 79, 0, 0, 31 ],
203 'gray32' => [ 82, 82, 82, 0, 0, 32 ],
204 'gray33' => [ 84, 84, 84, 0, 0, 33 ],
205 'gray34' => [ 87, 87, 87, 0, 0, 34 ],
206 'gray35' => [ 89, 89, 89, 0, 0, 35 ],
207 'gray36' => [ 92, 92, 92, 0, 0, 36 ],
208 'gray37' => [ 94, 94, 94, 0, 0, 37 ],
209 'gray38' => [ 97, 97, 97, 0, 0, 38 ],
210 'gray39' => [ 99, 99, 99, 0, 0, 39 ],
211 'gray40' => [ 102, 102, 102, 0, 0, 40 ],
212 'gray41' => [ 105, 105, 105, 0, 0, 41 ],
213 'gray42' => [ 107, 107, 107, 0, 0, 42 ],
214 'gray43' => [ 110, 110, 110, 0, 0, 43 ],
215 'gray44' => [ 112, 112, 112, 0, 0, 44 ],
216 'gray45' => [ 115, 115, 115, 0, 0, 45 ],
217 'gray46' => [ 117, 117, 117, 0, 0, 46 ],
218 'gray47' => [ 120, 120, 120, 0, 0, 47 ],
219 'gray48' => [ 122, 122, 122, 0, 0, 48 ],
220 'gray49' => [ 125, 125, 125, 0, 0, 49 ],
221 'gray50' => [ 127, 127, 127, 0, 0, 50 ],
222 'gray51' => [ 130, 130, 130, 0, 0, 51 ],
223 'gray52' => [ 133, 133, 133, 0, 0, 52 ],
224 'gray53' => [ 135, 135, 135, 0, 0, 53 ],
225 'gray54' => [ 138, 138, 138, 0, 0, 54 ],
226 'gray55' => [ 140, 140, 140, 0, 0, 55 ],
227 'gray56' => [ 143, 143, 143, 0, 0, 56 ],
228 'gray57' => [ 145, 145, 145, 0, 0, 57 ],
229 'gray58' => [ 148, 148, 148, 0, 0, 58 ],
230 'gray59' => [ 150, 150, 150, 0, 0, 59 ],
231 'gray60' => [ 153, 153, 153, 0, 0, 60 ],
232 'gray61' => [ 156, 156, 156, 0, 0, 61 ],
233 'gray62' => [ 158, 158, 158, 0, 0, 62 ],
234 'gray63' => [ 161, 161, 161, 0, 0, 63 ],
235 'gray64' => [ 163, 163, 163, 0, 0, 64 ],
236 'gray65' => [ 166, 166, 166, 0, 0, 65 ],
237 'gray66' => [ 168, 168, 168, 0, 0, 66 ],
238 'gray67' => [ 171, 171, 171, 0, 0, 67 ],
239 'gray68' => [ 173, 173, 173, 0, 0, 68 ],
240 'gray69' => [ 176, 176, 176, 0, 0, 69 ],
241 'gray70' => [ 179, 179, 179, 0, 0, 70 ],
242 'gray71' => [ 181, 181, 181, 0, 0, 71 ],
243 'gray72' => [ 184, 184, 184, 0, 0, 72 ],
244 'gray73' => [ 186, 186, 186, 0, 0, 73 ],
245 'gray74' => [ 189, 189, 189, 0, 0, 74 ],
246 'gray75' => [ 191, 191, 191, 0, 0, 75 ],
247 'gray76' => [ 194, 194, 194, 0, 0, 76 ],
248 'gray77' => [ 196, 196, 196, 0, 0, 77 ],
249 'gray78' => [ 199, 199, 199, 0, 0, 78 ],
250 'gray79' => [ 201, 201, 201, 0, 0, 79 ],
251 'gray80' => [ 204, 204, 204, 0, 0, 80 ],
252 'gray81' => [ 207, 207, 207, 0, 0, 81 ],
253 'gray82' => [ 209, 209, 209, 0, 0, 82 ],
254 'gray83' => [ 212, 212, 212, 0, 0, 83 ],
255 'gray84' => [ 214, 214, 214, 0, 0, 84 ],
256 'gray85' => [ 217, 217, 217, 0, 0, 85 ],
257 'gray86' => [ 219, 219, 219, 0, 0, 86 ],
258 'gray87' => [ 222, 222, 222, 0, 0, 87 ],
259 'gray88' => [ 224, 224, 224, 0, 0, 88 ],
260 'gray89' => [ 227, 227, 227, 0, 0, 89 ],
261 'gray90' => [ 229, 229, 229, 0, 0, 90 ],
262 'gray91' => [ 232, 232, 232, 0, 0, 91 ],
263 'gray92' => [ 235, 235, 235, 0, 0, 92 ],
264 'gray93' => [ 237, 237, 237, 0, 0, 93 ],
265 'gray94' => [ 240, 240, 240, 0, 0, 94 ],
266 'gray95' => [ 242, 242, 242, 0, 0, 95 ],
267 'gray97' => [ 247, 247, 247, 0, 0, 97 ],
268 'gray98' => [ 250, 250, 250, 0, 0, 98 ],
269 'gray99' => [ 252, 252, 252, 0, 0, 99 ],
270 'green1' => [ 0, 255, 0, 120, 100, 50 ],
271 'green2' => [ 0, 238, 0, 120, 100, 47 ],
272 'green3' => [ 0, 205, 0, 120, 100, 40 ],
273 'green4' => [ 0, 139, 0, 120, 100, 27 ],
274 'greenyellow' => [ 173, 255, 47, 84, 100, 59 ],
275 'grey1' => [ 3, 3, 3, 0, 0, 1 ],
276 'grey2' => [ 5, 5, 5, 0, 0, 2 ],
277 'grey3' => [ 8, 8, 8, 0, 0, 3 ],
278 'grey4' => [ 10, 10, 10, 0, 0, 4 ],
279 'honeydew' => [ 240, 255, 240, 120, 100, 97 ],
280 'honeydew1' => [ 240, 255, 240, 120, 100, 97 ],
281 'honeydew2' => [ 224, 238, 224, 120, 29, 91 ],
282 'honeydew3' => [ 193, 205, 193, 120, 11, 78 ],
283 'honeydew4' => [ 131, 139, 131, 120, 3, 53 ],
284 'hotpink' => [ 255, 105, 180, 330, 100, 71 ],
285 'hotpink1' => [ 255, 110, 180, 331, 100, 72 ],
286 'hotpink2' => [ 238, 106, 167, 332, 80, 67 ],
287 'hotpink3' => [ 205, 96, 144, 334, 52, 59 ],
288 'hotpink4' => [ 139, 58, 98, 330, 41, 39 ],
289 'indianred' => [ 205, 92, 92, 0, 53, 58 ],
290 'indianred1' => [ 255, 106, 106, 0, 100, 71 ],
291 'indianred2' => [ 238, 99, 99, 0, 80, 66 ],
292 'indianred3' => [ 205, 85, 85, 0, 55, 57 ],
293 'indianred4' => [ 139, 58, 58, 0, 41, 39 ],
294 'indigo' => [ 75, 0, 130, 275, 100, 25 ],
295 'ivory' => [ 255, 255, 240, 60, 100, 97 ],
296 'ivory1' => [ 255, 255, 240, 60, 100, 97 ],
297 'ivory2' => [ 238, 238, 224, 60, 29, 91 ],
298 'ivory3' => [ 205, 205, 193, 60, 11, 78 ],
299 'ivory4' => [ 139, 139, 131, 60, 3, 53 ],
300 'khaki' => [ 240, 230, 140, 54, 77, 75 ],
301 'khaki1' => [ 255, 246, 143, 55, 100, 78 ],
302 'khaki2' => [ 238, 230, 133, 55, 76, 73 ],
303 'khaki3' => [ 205, 198, 115, 55, 47, 63 ],
304 'khaki4' => [ 139, 134, 78, 55, 28, 43 ],
305 'lavender' => [ 230, 230, 250, 240, 67, 94 ],
306 'lavenderblush' => [ 255, 240, 245, 340, 100, 97 ],
307 'lavenderblush1' => [ 255, 240, 245, 340, 100, 97 ],
308 'lavenderblush2' => [ 238, 224, 229, 339, 29, 91 ],
309 'lavenderblush3' => [ 205, 193, 197, 340, 11, 78 ],
310 'lavenderblush4' => [ 139, 131, 134, 338, 3, 53 ],
311 'lawngreen' => [ 124, 252, 0, 90, 100, 49 ],
312 'lemonchiffon' => [ 255, 250, 205, 54, 100, 90 ],
313 'lemonchiffon1' => [ 255, 250, 205, 54, 100, 90 ],
314 'lemonchiffon2' => [ 238, 233, 191, 54, 58, 84 ],
315 'lemonchiffon3' => [ 205, 201, 165, 54, 29, 73 ],
316 'lemonchiffon4' => [ 139, 137, 112, 56, 11, 49 ],
317 'light' => [ 238, 221, 130, 51, 76, 72 ],
318 'lightblue' => [ 173, 216, 230, 195, 53, 79 ],
319 'lightblue1' => [ 191, 239, 255, 195, 100, 87 ],
320 'lightblue2' => [ 178, 223, 238, 195, 64, 82 ],
321 'lightblue3' => [ 154, 192, 205, 195, 34, 70 ],
322 'lightblue4' => [ 104, 131, 139, 194, 14, 48 ],
323 'lightcoral' => [ 240, 128, 128, 0, 79, 72 ],
324 'lightcyan' => [ 224, 255, 255, 180, 100, 94 ],
325 'lightcyan1' => [ 224, 255, 255, 180, 100, 94 ],
326 'lightcyan2' => [ 209, 238, 238, 180, 46, 88 ],
327 'lightcyan3' => [ 180, 205, 205, 180, 20, 75 ],
328 'lightcyan4' => [ 122, 139, 139, 180, 7, 51 ],
329 'lightgoldenrod' => [ 238, 221, 130, 51, 76, 72 ],
330 'lightgoldenrod1' => [ 255, 236, 139, 50, 100, 77 ],
331 'lightgoldenrod2' => [ 238, 220, 130, 50, 76, 72 ],
332 'lightgoldenrod3' => [ 205, 190, 112, 50, 48, 62 ],
333 'lightgoldenrod4' => [ 139, 129, 76, 50, 29, 42 ],
334 'lightgray' => [ 211, 211, 211, 0, 0, 83 ],
335 'lightgreen' => [ 144, 238, 144, 120, 73, 75 ],
336 'lightpink' => [ 255, 182, 193, 351, 100, 86 ],
337 'lightpink1' => [ 255, 174, 185, 352, 100, 84 ],
338 'lightpink2' => [ 238, 162, 173, 351, 69, 78 ],
339 'lightpink3' => [ 205, 140, 149, 352, 39, 68 ],
340 'lightpink4' => [ 139, 95, 101, 352, 19, 46 ],
341 'lightpurple' => [ 145, 0, 250, 275, 100, 49 ], # not in X11
342 'lightsalmon' => [ 255, 160, 122, 17, 100, 74 ],
343 'lightsalmon1' => [ 255, 160, 122, 17, 100, 74 ],
344 'lightsalmon2' => [ 238, 149, 114, 17, 78, 69 ],
345 'lightsalmon3' => [ 205, 129, 98, 17, 52, 59 ],
346 'lightsalmon4' => [ 139, 87, 66, 17, 36, 40 ],
347 'lightseagreen' => [ 32, 178, 170, 177, 70, 41 ],
348 'lightskyblue' => [ 135, 206, 250, 203, 92, 75 ],
349 'lightskyblue1' => [ 176, 226, 255, 202, 100, 85 ],
350 'lightskyblue2' => [ 164, 211, 238, 202, 69, 79 ],
351 'lightskyblue3' => [ 141, 182, 205, 202, 39, 68 ],
352 'lightskyblue4' => [ 96, 123, 139, 202, 18, 46 ],
353 'lightslateblue' => [ 132, 112, 255, 248, 100, 72 ],
354 'lightslategray' => [ 119, 136, 153, 210, 14, 53 ],
355 'lightsteelblue' => [ 176, 196, 222, 214, 41, 78 ],
356 'lightsteelblue1' => [ 202, 225, 255, 214, 100, 90 ],
357 'lightsteelblue2' => [ 188, 210, 238, 214, 60, 84 ],
358 'lightsteelblue3' => [ 162, 181, 205, 213, 30, 72 ],
359 'lightsteelblue4' => [ 110, 123, 139, 213, 12, 49 ],
360 'lightyellow' => [ 255, 255, 224, 60, 100, 94 ],
361 'lightyellow1' => [ 255, 255, 224, 60, 100, 94 ],
362 'lightyellow2' => [ 238, 238, 209, 60, 46, 88 ],
363 'lightyellow3' => [ 205, 205, 180, 60, 20, 75 ],
364 'lightyellow4' => [ 139, 139, 122, 60, 7, 51 ],
365 'lime' => [ 0, 255, 0, 120, 100, 50 ],
366 'limegreen' => [ 50, 205, 50, 120, 61, 50 ],
367 'linen' => [ 250, 240, 230, 30, 67, 94 ],
368 'magenta' => [ 255, 0, 255, 300, 100, 50 ],
369 'magenta1' => [ 255, 0, 255, 300, 100, 50 ],
370 'magenta2' => [ 238, 0, 238, 300, 100, 47 ],
371 'magenta3' => [ 205, 0, 205, 300, 100, 40 ],
372 'magenta4' => [ 139, 0, 139, 300, 100, 27 ],
373 'maroon' => [ 128, 0, 0, 0, 100, 25 ],
374 'maroon1' => [ 255, 52, 179, 322, 100, 60 ],
375 'maroon2' => [ 238, 48, 167, 322, 85, 56 ],
376 'maroon3' => [ 205, 41, 144, 322, 67, 48 ],
377 'maroon4' => [ 139, 28, 98, 322, 66, 33 ],
378 'medium' => [ 102, 205, 170, 160, 51, 60 ],
379 'mediumaquamarine' => [ 102, 205, 170, 160, 51, 60 ],
380 'mediumblue' => [ 0, 0, 205, 240, 100, 40 ],
381 'mediumorchid' => [ 186, 85, 211, 288, 59, 58 ],
382 'mediumorchid1' => [ 224, 102, 255, 288, 100, 70 ],
383 'mediumorchid2' => [ 209, 95, 238, 288, 81, 65 ],
384 'mediumorchid3' => [ 180, 82, 205, 288, 55, 56 ],
385 'mediumorchid4' => [ 122, 55, 139, 288, 43, 38 ],
386 'mediumpurple' => [ 147, 112, 219, 260, 60, 65 ],
387 'mediumpurple1' => [ 171, 130, 255, 260, 100, 75 ],
388 'mediumpurple2' => [ 159, 121, 238, 259, 77, 70 ],
389 'mediumpurple3' => [ 137, 104, 205, 260, 50, 61 ],
390 'mediumpurple4' => [ 93, 71, 139, 259, 32, 41 ],
391 'mediumseagreen' => [ 60, 179, 113, 147, 50, 47 ],
392 'mediumslateblue' => [ 123, 104, 238, 249, 80, 67 ],
393 'mediumspringgreen' => [ 0, 250, 154, 157, 100, 49 ],
394 'mediumturquoise' => [ 72, 209, 204, 178, 60, 55 ],
395 'mediumvioletred' => [ 199, 21, 133, 322, 81, 43 ],
396 'midnightblue' => [ 25, 25, 112, 240, 64, 27 ],
397 'mintcream' => [ 245, 255, 250, 150, 100, 98 ],
398 'mistyrose' => [ 255, 228, 225, 6, 100, 94 ],
399 'mistyrose1' => [ 255, 228, 225, 6, 100, 94 ],
400 'mistyrose2' => [ 238, 213, 210, 6, 45, 88 ],
401 'mistyrose3' => [ 205, 183, 181, 5, 19, 76 ],
402 'mistyrose4' => [ 139, 125, 123, 8, 6, 51 ],
403 'moccasin' => [ 255, 228, 181, 38, 100, 85 ],
404 'navajowhite' => [ 255, 222, 173, 36, 100, 84 ],
405 'navajowhite1' => [ 255, 222, 173, 36, 100, 84 ],
406 'navajowhite2' => [ 238, 207, 161, 36, 69, 78 ],
407 'navajowhite3' => [ 205, 179, 139, 36, 40, 67 ],
408 'navajowhite4' => [ 139, 121, 94, 36, 19, 46 ],
409 'navy' => [ 0, 0, 128, 240, 100, 25 ],
410 'navyblue' => [ 0, 0, 128, 240, 100, 25 ],
411 'oldlace' => [ 253, 245, 230, 39, 85, 95 ],
412 'olive' => [ 128, 128, 0, 60, 100, 25 ],
413 'olivedrab' => [ 107, 142, 35, 80, 60, 35 ],
414 'olivedrab1' => [ 192, 255, 62, 80, 100, 62 ],
415 'olivedrab2' => [ 179, 238, 58, 80, 84, 58 ],
416 'olivedrab3' => [ 154, 205, 50, 80, 61, 50 ],
417 'olivedrab4' => [ 105, 139, 34, 79, 61, 34 ],
418 'orange' => [ 255, 165, 0, 39, 100, 50 ],
419 'orange1' => [ 255, 165, 0, 39, 100, 50 ],
420 'orange2' => [ 238, 154, 0, 39, 100, 47 ],
421 'orange3' => [ 205, 133, 0, 39, 100, 40 ],
422 'orange4' => [ 139, 90, 0, 39, 100, 27 ],
423 'orangered' => [ 255, 69, 0, 16, 100, 50 ],
424 'orangered1' => [ 255, 69, 0, 16, 100, 50 ],
425 'orangered2' => [ 238, 64, 0, 16, 100, 47 ],
426 'orangered3' => [ 205, 55, 0, 16, 100, 40 ],
427 'orangered4' => [ 139, 37, 0, 16, 100, 27 ],
428 'orchid' => [ 218, 112, 214, 302, 59, 65 ],
429 'orchid1' => [ 255, 131, 250, 302, 100, 76 ],
430 'orchid2' => [ 238, 122, 233, 303, 77, 71 ],
431 'orchid3' => [ 205, 105, 201, 302, 50, 61 ],
432 'orchid4' => [ 139, 71, 137, 302, 32, 41 ],
433 'pale' => [ 219, 112, 147, 340, 60, 65 ],
434 'palegoldenrod' => [ 238, 232, 170, 55, 67, 80 ],
435 'palegreen' => [ 152, 251, 152, 120, 93, 79 ],
436 'palegreen1' => [ 154, 255, 154, 120, 100, 80 ],
437 'palegreen2' => [ 144, 238, 144, 120, 73, 75 ],
438 'palegreen3' => [ 124, 205, 124, 120, 45, 65 ],
439 'palegreen4' => [ 84, 139, 84, 120, 25, 44 ],
440 'paleturquoise' => [ 175, 238, 238, 180, 65, 81 ],
441 'paleturquoise1' => [ 187, 255, 255, 180, 100, 87 ],
442 'paleturquoise2' => [ 174, 238, 238, 180, 65, 81 ],
443 'paleturquoise3' => [ 150, 205, 205, 180, 35, 70 ],
444 'paleturquoise4' => [ 102, 139, 139, 180, 15, 47 ],
445 'palevioletred' => [ 219, 112, 147, 340, 60, 65 ],
446 'palevioletred1' => [ 255, 130, 171, 340, 100, 75 ],
447 'palevioletred2' => [ 238, 121, 159, 341, 77, 70 ],
448 'palevioletred3' => [ 205, 104, 137, 340, 50, 61 ],
449 'palevioletred4' => [ 139, 71, 93, 341, 32, 41 ],
450 'papayawhip' => [ 255, 239, 213, 37, 100, 92 ],
451 'peachpuff' => [ 255, 218, 185, 28, 100, 86 ],
452 'peachpuff1' => [ 255, 218, 185, 28, 100, 86 ],
453 'peachpuff2' => [ 238, 203, 173, 28, 66, 81 ],
454 'peachpuff3' => [ 205, 175, 149, 28, 36, 69 ],
455 'peachpuff4' => [ 139, 119, 101, 28, 16, 47 ],
456 'peru' => [ 205, 133, 63, 30, 59, 53 ],
457 'pink1' => [ 255, 181, 197, 347, 100, 85 ],
458 'pink2' => [ 238, 169, 184, 347, 67, 80 ],
459 'pink3' => [ 205, 145, 158, 347, 38, 69 ],
460 'pink4' => [ 139, 99, 108, 347, 17, 47 ],
461 'plum1' => [ 255, 187, 255, 300, 100, 87 ],
462 'plum2' => [ 238, 174, 238, 300, 65, 81 ],
463 'plum3' => [ 205, 150, 205, 300, 35, 70 ],
464 'plum4' => [ 139, 102, 139, 300, 15, 47 ],
465 'powderblue' => [ 176, 224, 230, 187, 52, 80 ],
466 'purple1' => [ 155, 48, 255, 271, 100, 59 ],
467 'purple2' => [ 145, 44, 238, 271, 85, 55 ],
468 'purple3' => [ 125, 38, 205, 271, 69, 48 ],
469 'purple4' => [ 85, 26, 139, 271, 68, 32 ],
470 'rebeccapurple' => [ 102, 51, 153, 270, 50, 40 ],
471 'red1' => [ 255, 0, 0, 0, 100, 50 ],
472 'red2' => [ 238, 0, 0, 0, 100, 47 ],
473 'red3' => [ 205, 0, 0, 0, 100, 40 ],
474 'red4' => [ 139, 0, 0, 0, 100, 27 ],
475 'rosybrown' => [ 188, 143, 143, 0, 25, 65 ],
476 'rosybrown1' => [ 255, 193, 193, 0, 100, 88 ],
477 'rosybrown2' => [ 238, 180, 180, 0, 63, 82 ],
478 'rosybrown3' => [ 205, 155, 155, 0, 33, 71 ],
479 'rosybrown4' => [ 139, 105, 105, 0, 14, 48 ],
480 'royalblue' => [ 65, 105, 225, 225, 73, 57 ],
481 'royalblue1' => [ 72, 118, 255, 225, 100, 64 ],
482 'royalblue2' => [ 67, 110, 238, 225, 83, 60 ],
483 'royalblue3' => [ 58, 95, 205, 225, 60, 52 ],
484 'royalblue4' => [ 39, 64, 139, 225, 56, 35 ],
485 'saddlebrown' => [ 139, 69, 19, 25, 76, 31 ],
486 'salmon' => [ 250, 128, 114, 6, 93, 71 ],
487 'salmon1' => [ 255, 140, 105, 14, 100, 71 ],
488 'salmon2' => [ 238, 130, 98, 14, 80, 66 ],
489 'salmon3' => [ 205, 112, 84, 14, 55, 57 ],
490 'salmon4' => [ 139, 76, 57, 14, 42, 38 ],
491 'sandybrown' => [ 244, 164, 96, 28, 87, 67 ],
492 'seagreen' => [ 46, 139, 87, 146, 50, 36 ],
493 'seagreen1' => [ 84, 255, 159, 146, 100, 66 ],
494 'seagreen2' => [ 78, 238, 148, 146, 82, 62 ],
495 'seagreen3' => [ 67, 205, 128, 147, 58, 53 ],
496 'seagreen4' => [ 46, 139, 87, 146, 50, 36 ],
497 'seashell' => [ 255, 245, 238, 25, 100, 97 ],
498 'seashell1' => [ 255, 245, 238, 25, 100, 97 ],
499 'seashell2' => [ 238, 229, 222, 26, 32, 90 ],
500 'seashell3' => [ 205, 197, 191, 26, 12, 78 ],
501 'seashell4' => [ 139, 134, 130, 27, 4, 53 ],
502 'sienna' => [ 160, 82, 45, 19, 56, 40 ],
503 'sienna1' => [ 255, 130, 71, 19, 100, 64 ],
504 'sienna2' => [ 238, 121, 66, 19, 83, 60 ],
505 'sienna3' => [ 205, 104, 57, 19, 60, 51 ],
506 'sienna4' => [ 139, 71, 38, 20, 57, 35 ],
507 'silver' => [ 192, 192, 192, 0, 0, 75 ],
508 'skyblue' => [ 135, 206, 235, 197, 71, 73 ],
509 'skyblue1' => [ 135, 206, 255, 205, 100, 76 ],
510 'skyblue2' => [ 126, 192, 238, 205, 77, 71 ],
511 'skyblue3' => [ 108, 166, 205, 204, 49, 61 ],
512 'skyblue4' => [ 74, 112, 139, 205, 31, 42 ],
513 'slateblue' => [ 106, 90, 205, 248, 53, 58 ],
514 'slateblue1' => [ 131, 111, 255, 248, 100, 72 ],
515 'slateblue2' => [ 122, 103, 238, 248, 80, 67 ],
516 'slateblue3' => [ 105, 89, 205, 248, 54, 58 ],
517 'slateblue4' => [ 71, 60, 139, 248, 40, 39 ],
518 'slategray' => [ 112, 128, 144, 210, 13, 50 ],
519 'slategray1' => [ 198, 226, 255, 211, 100, 89 ],
520 'slategray2' => [ 185, 211, 238, 211, 61, 83 ],
521 'slategray3' => [ 159, 182, 205, 210, 32, 71 ],
522 'slategray4' => [ 108, 123, 139, 211, 13, 48 ],
523 'snow' => [ 255, 250, 250, 0, 100, 99 ],
524 'snow1' => [ 255, 250, 250, 0, 100, 99 ],
525 'snow2' => [ 238, 233, 233, 0, 13, 92 ],
526 'snow3' => [ 205, 201, 201, 0, 4, 80 ],
527 'snow4' => [ 139, 137, 137, 0, 1, 54 ],
528 'springgreen' => [ 0, 255, 127, 150, 100, 50 ],
529 'springgreen1' => [ 0, 255, 127, 150, 100, 50 ],
530 'springgreen2' => [ 0, 238, 118, 150, 100, 47 ],
531 'springgreen3' => [ 0, 205, 102, 150, 100, 40 ],
532 'springgreen4' => [ 0, 139, 69, 150, 100, 27 ],
533 'steelblue' => [ 70, 130, 180, 207, 44, 49 ],
534 'steelblue1' => [ 99, 184, 255, 207, 100, 69 ],
535 'steelblue2' => [ 92, 172, 238, 207, 81, 65 ],
536 'steelblue3' => [ 79, 148, 205, 207, 56, 56 ],
537 'steelblue4' => [ 54, 100, 139, 208, 44, 38 ],
538 'tan' => [ 210, 180, 140, 34, 44, 69 ],
539 'tan1' => [ 255, 165, 79, 29, 100, 65 ],
540 'tan2' => [ 238, 154, 73, 29, 83, 61 ],
541 'tan3' => [ 205, 133, 63, 30, 59, 53 ],
542 'tan4' => [ 139, 90, 43, 29, 53, 36 ],
543 'teal' => [ 0, 128, 128, 180, 100, 25 ],
544 'thistle' => [ 216, 191, 216, 300, 24, 80 ],
545 'thistle1' => [ 255, 225, 255, 300, 100, 94 ],
546 'thistle2' => [ 238, 210, 238, 300, 45, 88 ],
547 'thistle3' => [ 205, 181, 205, 300, 19, 76 ],
548 'thistle4' => [ 139, 123, 139, 300, 6, 51 ],
549 'tomato' => [ 255, 99, 71, 9, 100, 64 ],
550 'tomato1' => [ 255, 99, 71, 9, 100, 64 ],
551 'tomato2' => [ 238, 92, 66, 9, 83, 60 ],
552 'tomato3' => [ 205, 79, 57, 9, 60, 51 ],
553 'tomato4' => [ 139, 54, 38, 10, 57, 35 ],
554 'turquoise' => [ 69, 184, 172, 174, 45, 50 ],
555 'turquoise1' => [ 0, 245, 255, 182, 100, 50 ],
556 'turquoise2' => [ 0, 229, 238, 182, 100, 47 ],
557 'turquoise3' => [ 0, 197, 205, 182, 100, 40 ],
558 'turquoise4' => [ 0, 134, 139, 182, 100, 27 ],
559 'violet' => [ 238, 130, 238, 300, 76, 72 ],
560 'violetred' => [ 208, 32, 144, 322, 73, 47 ],
561 'violetred1' => [ 255, 62, 150, 333, 100, 62 ],
562 'violetred2' => [ 238, 58, 140, 333, 84, 58 ],
563 'violetred3' => [ 205, 50, 120, 333, 61, 50 ],
564 'violetred4' => [ 139, 34, 82, 333, 61, 34 ],
565 'wheat' => [ 245, 222, 179, 39, 77, 83 ],
566 'wheat1' => [ 255, 231, 186, 39, 100, 86 ],
567 'wheat2' => [ 238, 216, 174, 39, 65, 81 ],
568 'wheat3' => [ 205, 186, 150, 39, 35, 70 ],
569 'wheat4' => [ 139, 126, 102, 39, 15, 47 ],
570 'whitesmoke' => [ 245, 245, 245, 0, 0, 96 ],
571 'yellow1' => [ 255, 255, 0, 60, 100, 50 ],
572 'yellow2' => [ 238, 238, 0, 60, 100, 47 ],
573 'yellow3' => [ 205, 205, 0, 60, 100, 40 ],
574 'yellow4' => [ 139, 139, 0, 60, 100, 27 ],
575 'yellowgreen' => [ 154, 205, 50, 80, 61, 50 ],
576 # https://www.w3schools.com/colors/colors_trends.asp
577 'marsala' => [ 149, 82, 81, 1, 30, 45 ], # best 2015-2000
578 'radiandorchid' => [ 181, 101, 167, 311, 35, 55 ],
579 'emerald' => [ 0, 155, 119, 166, 100, 30 ],
580 'tangerinetango' => [ 221, 65, 36, 9, 73, 50 ],
581 'honeysucle' => [ 214, 80, 118, 343, 62, 58 ],
582 'turquoise' => [ 69, 184, 172, 174, 45, 50 ],
583 'mimosa' => [ 239, 192, 80, 42, 83, 63 ],
584 'blueizis' => [ 91, 94, 166, 238, 30, 50 ],
585 'chilipepper' => [ 155, 27, 48, 350, 70, 36 ],
586 'sanddollar' => [ 223, 207, 190, 31, 34, 81 ],
587 'blueturquoise' => [ 85, 180, 176, 177, 39, 52 ],
588 'tigerlily' => [ 225, 93, 68, 10, 72, 57 ],
589 'aquasky' => [ 127, 205, 205, 180, 44, 65 ],
590 'truered' => [ 188, 36, 60, 351, 68, 44 ],
591 'fuchsiarose' => [ 195, 68, 122, 334, 51, 52 ],
592 'ceruleanblue' => [ 152, 180, 212, 212, 41, 71 ],
593 'rosequartz' => [ 247, 202, 201, 1, 74, 88 ], # 2016 Spring
594 'peachecho' => [ 247, 120, 107, 6, 90, 69 ],
595 'serenity' => [ 145, 168, 208, 218, 40, 69 ],
596 'snorkelblue' => [ 3, 79, 132, 205, 96, 26 ],
597 'limpetshell' => [ 152, 221, 222, 181, 51, 73 ],
598 'lilacgrey' => [ 152, 221, 222, 181, 51, 73 ],
599 'icedcoffee' => [ 177, 143, 106, 31, 31, 55 ],
600 'fiesta' => [ 221, 65, 50, 5, 72, 53 ],
601 'buttercup' => [ 221, 65, 50, 5, 72, 53 ],
602 'greenflash' => [ 250, 224, 60, 52, 95, 61 ],
603 'riverside' => [ 76, 106, 146, 214, 32, 44 ], # Fall
604 'airyblue' => [ 146, 182, 213, 208, 44, 70 ],
605 'sharkskin' => [ 131, 132, 135, 225, 2, 52 ],
606 'aurorared' => [ 185, 58, 50, 4, 57, 46 ],
607 'warmtaupe' => [ 175, 148, 131, 23, 22, 60 ],
608 'dustycedar' => [ 173, 93, 93, 0, 33, 52 ],
609 'lushmeadow' => [ 0, 110, 81, 164, 100, 22 ],
610 'spicymustard' => [ 216, 174, 71, 43, 65, 56 ],
611 'pottersclay' => [ 158, 70, 36, 17, 63, 38 ], # Potter's Clay
612 'bodacious' => [ 183, 107, 163, 316, 35, 57 ],
613 'greenery' => [ 146, 181, 88, 83, 39, 53 ], # 2017
614 'niagara' => [ 87, 140, 169, 201, 32, 50 ],
615 'primroseyellow' => [ 246, 209, 85, 46, 90, 65 ],
616 'lapisblue' => [ 0, 75, 141, 208, 100, 28 ],
617 'flame' => [ 242, 85, 44, 12, 88, 56 ],
618 'islandparadise' => [ 149, 222, 227, 184, 58, 74 ],
619 'paledogwood' => [ 237, 205, 194, 15, 54, 85 ],
620 'pinkyarrow' => [ 206, 49, 117, 334, 62, 50 ],
621 'kale' => [ 90, 114, 71, 93, 23, 36 ],
622 'hazelnut' => [ 207, 176, 149, 28, 38, 70 ],
623 'grenadine' => [ 220, 76, 70, 2, 68, 57 ],
624 'balletslipper' => [ 243, 214, 228, 331, 55, 90 ],
625 'butterum' => [ 196, 143, 101, 27, 45, 58 ],
626 'navypeony' => [ 34, 58, 94, 216, 47, 25 ],
627 'neutralgray' => [ 137, 142, 140, 156, 2, 55 ],
628 'shadedspruce' => [ 0, 89, 96, 184, 100, 19 ],
629 'goldenlime' => [ 156, 154, 64, 59, 42, 43 ],
630 'marina' => [ 79, 132, 196, 213, 50, 54 ],
631 'autumnmaple' => [ 210, 105, 30, 25, 75, 47 ],
632 'meadowlark' => [ 236, 219, 84, 53, 80, 63 ], # 2018
633 'cherrytomato' => [ 233, 75, 60, 5, 80, 57 ],
634 'littleboyblue' => [ 111, 159, 216, 213, 57, 64 ],
635 'chilioil' => [ 148, 71, 67, 3, 38, 42 ],
636 'pinklavender' => [ 219, 177, 205, 320, 37, 78 ],
637 'bloomingdahlia' => [ 236, 151, 135, 10, 73, 73 ],
638 'arcadia' => [ 0, 165, 145, 173, 100, 32 ],
639 'ultraviolet' => [ 107, 91, 149, 257, 24, 47 ],
640 'emperador' => [ 108, 79, 61, 23, 28, 33 ],
641 'almostmauve' => [ 234, 222, 219, 12, 26, 89 ],
642 'springcrocus' => [ 188, 112, 164, 319, 36, 59 ],
643 'sailorblue' => [ 46, 74, 98, 208, 36, 28 ],
644 'harbormist' => [ 180, 183, 186, 210, 4, 72 ],
645 'warmsand' => [ 192, 171, 142, 35, 28, 65 ],
646 'coconutmilk' => [ 240, 237, 229, 44, 27, 92 ],
647 'redpear' => [ 127, 65, 69, 356, 32, 38 ],
648 'valiantpoppy' => [ 189, 61, 58, 1, 53, 48 ],
649 'nebulasblue' => [ 63, 105, 170, 216, 46, 46 ],
650 'ceylonyellow' => [ 213, 174, 65, 44, 64, 55 ],
651 'martiniolive' => [ 118, 111, 87, 46, 15, 40 ],
652 'russetorange' => [ 228, 122, 46, 25, 77, 54 ],
653 'crocuspetal' => [ 190, 158, 201, 285, 28, 70 ],
654 'limelight' => [ 241, 234, 127, 56, 80, 72 ],
655 'quetzalgreen' => [ 0, 110, 109, 179, 100, 22 ],
656 'sargassosea' => [ 72, 81, 103, 223, 18, 34 ],
657 'tofu' => [ 234, 230, 218, 45, 28, 89 ],
658 'almondbuff' => [ 209, 184, 148, 35, 40, 70 ],
659 'quietgray' => [ 188, 188, 190, 240, 2, 74 ],
660 'meerkat' => [ 169, 117, 79, 25, 36, 49 ],
661 'fiesta' => [ 221, 65, 50, 5, 72, 53 ], # 2019
662 'jesterred' => [ 158, 16, 48, 346, 82, 34 ],
663 'turmeric' => [ 254, 132, 14, 30, 99, 53 ],
664 'livingcoral' => [ 255, 111, 97, 5, 100, 69 ],
665 'pinkpeacock' => [ 198, 33, 104, 334, 71, 45 ],
666 'pepperstem' => [ 141, 148, 64, 65, 40, 42 ],
667 'aspengold' => [ 255, 214, 98, 44, 100, 69 ],
668 'princessblue' => [ 0, 83, 156, 208, 100, 31 ],
669 'toffee' => [ 117, 81, 57, 24, 34, 34 ],
670 'mangomojito' => [ 214, 156, 47, 39, 67, 51 ],
671 'terrariummoss' => [ 97, 98, 71, 62, 16, 33 ],
672 'sweetlilac' => [ 232, 181, 206, 331, 53, 81 ],
673 'soybean' => [ 210, 194, 157, 42, 37, 72 ],
674 'eclipse' => [ 52, 49, 72, 248, 19, 24 ],
675 'sweetcorn' => [ 240, 234, 214, 46, 46, 89 ],
676 'browngranite' => [ 97, 85, 80, 18, 10, 35 ],
677 'chilipepper' => [ 155, 27, 48, 350, 70, 36 ],
678 'bikingred' => [ 119, 33, 46, 351, 57, 30 ],
679 'peachpink' => [ 250, 154, 133, 11, 92, 75 ],
680 'rockyroad' => [ 90, 62, 54, 13, 25, 28 ],
681 'fruitdove' => [ 206, 91, 120, 345, 54, 58 ],
682 'sugaralmond' => [ 147, 85, 41, 25, 56, 37 ],
683 'darkcheddar' => [ 224, 129, 25, 31, 80, 49 ],
684 'galaxyblue' => [ 42, 75, 124, 216, 49, 33 ],
685 'bluestone' => [ 87, 114, 132, 204, 21, 43 ],
686 'orangetiger' => [ 249, 103, 20, 22, 95, 53 ],
687 'eden' => [ 38, 78, 54, 144, 34, 23 ],
688 'vanillacustard' => [ 243, 224, 190, 38, 69, 85 ],
689 'eveningblue' => [ 42, 41, 62, 243, 20, 20 ],
690 'paloma' => [ 159, 156, 153, 30, 3, 61 ],
691 'guacamole' => [ 121, 123, 58, 62, 36, 35 ],
692 'flamescarlet' => [ 205, 33, 42, 357, 72, 47 ], # 2020
693 'saffron' => [ 255, 165, 0, 39, 100, 50 ],
694 'biscaygreen' => [ 86, 198, 169, 164, 50, 56 ],
695 'chive' => [ 75, 83, 53, 76, 22, 27 ],
696 'fadeddenim' => [ 121, 142, 164, 211, 19, 56 ],
697 'orangepeel' => [ 250, 122, 53, 21, 95, 59 ],
698 'mosaicblue' => [ 0, 117, 143, 191, 100, 28 ],
699 'sunlight' => [ 237, 213, 158, 42, 69, 77 ],
700 'coralpink' => [ 232, 167, 152, 11, 63, 75 ],
701 'grapecompote' => [ 107, 88, 118, 278, 15, 40 ],
702 'lark' => [ 184, 155, 114, 35, 33, 58 ],
703 'navyblazer' => [ 40, 45, 60, 225, 20, 20 ],
704 'brilliantwhite' => [ 237, 241, 255, 227, 100, 96 ],
705 'ash' => [ 160, 153, 152, 8, 4, 61 ],
706 'amberglow' => [ 220, 121, 62, 22, 69, 55 ],
707 'samba' => [ 162, 36, 47, 355, 64, 39 ],
708 'sandstone' => [ 196, 138, 105, 22, 44, 59 ],
709 'classicblue' => [ 52, 86, 139, 217, 46, 37 ],
710 'greensheen' => [ 217, 206, 82, 55, 64, 59 ],
711 'rosetan' => [ 209, 156, 151, 5, 39, 71 ],
712 'ultramarinegreen' => [ 0, 107, 84, 167, 100, 21 ],
713 'firedbrick' => [ 106, 46, 42, 4, 43, 29 ],
714 'peachnougat' => [ 230, 175, 145, 21, 63, 74 ],
715 'magentapurple' => [ 108, 36, 76, 327, 50, 28 ],
716 'marigold' => [ 253, 172, 83, 31, 98, 66 ], # 2021
717 'cerulean' => [ 155, 183, 212, 211, 40, 72 ],
718 'rust' => [ 181, 90, 48, 19, 58, 45 ],
719 'illuminating' => [ 245, 223, 77, 52, 89, 63 ],
720 'frenchblue' => [ 0, 114, 181, 202, 100, 35 ],
721 'greenash' => [ 160, 218, 169, 129, 44, 74 ],
722 'burntcoral' => [ 233, 137, 126, 6, 71, 70 ],
723 'mint' => [ 0, 161, 112, 162, 100, 32 ],
724 'amethystorchid' => [ 146, 106, 166, 280, 25, 53 ],
725 'raspberrysorbet' => [ 210, 56, 108, 340, 63, 52 ],
726 'inkwell' => [ 54, 57, 69, 228, 12, 24 ],
727 'ultimategray' => [ 147, 149, 151, 210, 2, 58 ],
728 'buttercream' => [ 239, 225, 206, 35, 51, 87 ],
729 'desertmist' => [ 224, 181, 137, 30, 58, 71 ],
730 'willow' => [ 154, 139, 79, 48, 32, 46 ],
731 );
732
733 our (@name_from_rgb, @name_from_hsl); # fill them through:
734
735 _add_color_to_reverse_search( $_, @{$rgbhsl_from_name{$_}} ) for all_names();
736
737
738 sub all_names { sort keys %rgbhsl_from_name }
739 sub name_taken { exists $rgbhsl_from_name{ _clean_name($_[0]) }}
740
741 sub rgb_from_name {
742 my $name = _clean_name(shift);
743 @{$rgbhsl_from_name{$name}}[0..2] if name_taken( $name );
744 }
745
746 sub hsl_from_name {
747 my $name = _clean_name(shift);
748 @{$rgbhsl_from_name{$name}}[3..5] if name_taken( $name );
749 }
750
751 sub name_from_rgb {
752 my (@rgb) = @_;
753 @rgb = @{$rgb[0]} if (ref $rgb[0] eq 'ARRAY');
754 Chart::Color::Value::check_rgb( @rgb ) and return; # return if sub did carp
755 my @names = _names_from_rgb( @rgb );
756 wantarray ? @names : $names[0];
757 }
758
759 sub name_from_hsl {
760 my (@hsl) = @_;
761 @hsl = @{$hsl[0]} if (ref $hsl[0] eq 'ARRAY');
762 Chart::Color::Value::check_hsl( @hsl ) and return;
763 my @names = _names_from_hsl( @hsl );
764 wantarray ? @names : $names[0];
765 }
766
767 sub names_in_hsl_range { # @center, (@d | $d) --> @names
768 my $help = 'need two arguments: 1. array with h s l values '.
769 '2. radius (real number) or array with tolerances in h s l direction';
770 return carp $help if @_ != 2;
771 my ($hsl_center, $radius) = @_;
772 return carp 'first argument has to be an array ref with thre number ([$h, $s, $l])'
773 if ref $hsl_center ne 'ARRAY' or @$hsl_center != 3;
774 return carp 'second argument has to be a integer < 180 or array ref with 3 integer'
775 unless (ref $radius eq 'ARRAY' and @$radius == 3) or (defined $radius and not ref $radius);
776 Chart::Color::Value::check_hsl( @$hsl_center ) and return;
777
778 my @hsl_delta = ref $radius ? @$radius : ($radius, $radius, $radius);
779 $hsl_delta[$_] = int abs $hsl_delta[$_] for 0 ..2;
780 $hsl_delta[0] = 180 if $hsl_delta[0] > 180; # enough to search complete HSL space (prevent double results)
781
782 my (@min, @max, @names, $minhrange, $maxhrange);
783 $min[$_] = $hsl_center->[$_] - $hsl_delta[$_] for 0..2;
784 $max[$_] = $hsl_center->[$_] + $hsl_delta[$_] for 0..2;
785 $min[1] = 0 if $min[1] < 0;
786 $min[2] = 0 if $min[2] < 0;
787 $max[1] = 100 if $max[1] > 100;
788 $max[2] = 100 if $max[2] > 100;
789 my @hrange = ($min[0] < 0 ? 0 : $min[0]) .. ($max[0] > 359 ? 359 : $max[0]);
790 push @hrange, (360 + $min[0]) .. 359 if $min[0] < 0;
791 push @hrange, 0 .. ($max[0] - 360) if $max[0] > 359;
792 for my $h (@hrange){
793 next unless defined $name_from_hsl[ $h ];
794 for my $s ($min[1] .. $max[1]){
795 next unless defined $name_from_hsl[ $h ][ $s ];
796 for my $l ($min[2] .. $max[2]){
797 my $name = $name_from_hsl[ $h ][ $s ][ $l ];
798 next unless defined $name;
799 push @names, (ref $name ? $name->[0] : $name);
800 }
801 }
802 }
803 @names = grep {Chart::Color::Value::distance_hsl( $hsl_center ,[hsl_from_name($_)] ) <= $radius} @names if not ref $radius;
804 @names;
805 }
806
807 sub add_rgb {
808 my ($name, @rgb) = @_;
809 @rgb = @{$rgb[0]} if (ref $rgb[0] eq 'ARRAY');
810 return carp "missing first argument: color name" unless defined $name and $name;
811 Chart::Color::Value::check_rgb( @rgb ) and return;
812 _add_color( $name, @rgb, Chart::Color::Value::hsl_from_rgb( @rgb ) );
813 }
814
815 sub add_hsl {
816 my ($name, @hsl) = @_;
817 @hsl = @{$hsl[0]} if (ref $hsl[0] eq 'ARRAY');
818 return carp "missing first argument: color name" unless defined $name and $name;
819 Chart::Color::Value::check_hsl( @hsl ) and return;
820 _add_color( $name, Chart::Color::Value::rgb_from_hsl( @hsl ), @hsl );
821 }
822
823 sub _add_color {
824 my ($name, @rgb, @hsl) = @_;
825 $name = _clean_name( $name );
826 return carp "there is already a color named '$name' in store of ".__PACKAGE__ if name_taken( $name );
827 _add_color_to_reverse_search( $name, @rgb, @hsl);
828 my $ret = $rgbhsl_from_name{$name} = [@rgb, @hsl]; # add to foreward search
829 (ref $ret) ? [@$ret] : ''; # make returned ref not transparent
830 }
831
832 sub _clean_name {
833 my $name = shift;
834 $name =~ tr/_//d;
835 lc $name;
836 }
837
838 sub _names_from_rgb { # each of AoAoA cells (if exists) contains name or array with names (shortes first)
839 return '' unless exists $name_from_rgb[ $_[0] ]
840 and exists $name_from_rgb[ $_[0] ][ $_[1] ] and exists $name_from_rgb[ $_[0] ][ $_[1] ][ $_[2] ];
841 my $cell = $name_from_rgb[ $_[0] ][ $_[1] ][ $_[2] ];
842 ref $cell ? @$cell : $cell;
843 }
844
845 sub _names_from_hsl {
846 return '' unless exists $name_from_hsl[ $_[0] ]
847 and exists $name_from_hsl[ $_[0] ][ $_[1] ] and exists $name_from_hsl[ $_[0] ][ $_[1] ][ $_[2] ];
848 my $cell = $name_from_hsl[ $_[0] ][ $_[1] ][ $_[2] ];
849 ref $cell ? @$cell : $cell;
850 }
851
852 sub _add_color_to_reverse_search { # my ($name, @rgb, @hsl) = @_;
853 my $name = $_[0];
854 my $cell = $name_from_rgb[ $_[1] ][ $_[2] ][ $_[3] ];
855 if (defined $cell) {
856 if (ref $cell) {
857 if (length $name < length $cell->[0] ) { unshift @$cell, $name }
858 else { push @$cell, $name }
859 } else {
860 $name_from_rgb[ $_[1] ][ $_[2] ][ $_[3] ] =
861 (length $name < length $cell) ? [ $name, $cell ]
862 : [ $cell, $name ] ;
863 }
864 } else { $name_from_rgb[ $_[1] ][ $_[2] ][ $_[3] ] = $name }
865
866 $cell = $name_from_hsl[ $_[4] ][ $_[5] ][ $_[6] ];
867 if (defined $cell) {
868 if (ref $cell) {
869 if (length $name < length $cell->[0] ) { unshift @$cell, $name }
870 else { push @$cell, $name }
871 } else {
872 $name_from_hsl[ $_[4] ][ $_[5] ][ $_[6] ] =
873 (length $name < length $cell) ? [ $name, $cell ]
874 : [ $cell, $name ] ;
875 }
876 } else { $name_from_hsl[ $_[4] ][ $_[5] ][ $_[6] ] = $name }
877 }
878
879 1;
880
881 __END__
882
883 =pod
884
885 =head1 NAME
886
887 Chart::Color::Constant - access values of color constants
888
889 =head1 SYNOPSIS
890
891 my @names = Chart::Color::Constant::all_names();
892 my @rgb = Chart::Color::Constant::rgb_from_name('darkblue');
893 my @hsl = Chart::Color::Constant::hsl_from_name('darkblue');
894
895 Chart::Color::Value::add_rgb('lucky', [0, 100, 50]);
896
897 =head1 DESCRIPTION
898
899 RGB and HSL values of named colors from the X11 and HTML standard
900 and Pantone report. Allows also reverse search, storage and conversion
901 of color values.
902
903 This module is supposed to be used by Chart::Color and not directly
904 by the user (for the most part). It converts a stored color name into
905 its values (rgb, hsl or both) and back. One color can have multiple names.
906 Also nearby (similar) colors can be searched. Own colors can be
907 (none permanently) stored for later reference by name. For this a name
908 has to be chosen, that is not already taken. Independently of that
909 can any color be converted from rgb to hsl and back.
910
911 =head1 ROUTINES
912
913 =head2 rgb_from_name
914
915 Red, Green and Blue value of the named color.
916 These values are integer in 0 .. 255.
917
918 my @rgb = Chart::Color::Constant::rgb_from_name('darkblue');
919 @rgb = Chart::Color::Constant::rgb_from_name('dark_blue'); # same result
920 @rgb = Chart::Color::Constant::rgb_from_name('DarkBlue'); # still same
921
922 =head2 hsl_from_name
923
924 Hue, saturation and lightness of the named color.
925 These are integer between 0 .. 359 (hue) or 100 (sat. & light.).
926 A hue of 360 and 0 (degree in a cylindrical coordinate system) is
927 considered to be the same, this modul deals only with the ladder.
928
929 my @hsl = Chart::Color::Constant::hsl_from_name('darkblue');
930
931 =head2 name_from_rgb
932
933 Returns name of color with given rgb value triplet.
934 Returns empty string if color is not stored. When several names define
935 given color, the shortest name will be selected in scalar context.
936 In array context all names are given.
937
938 say Chart::Color::Constant::name_from_rgb( 15, 10, 121 ); # 'darkblue'
939 say Chart::Color::Constant::name_from_rgb([15, 10, 121]); # works too
940
941 =head2 name_from_hsl
942
943 Returns name of color with given hsl value triplet.
944 Returns empty string if color is not stored. When several names define
945 given color, the shortest name will be selected in scalar context.
946 In array context all names are given.
947
948 say scalar Chart::Color::Constant::name_from_hsl( 0, 100, 50 ); # 'red'
949 scalar Chart::Color::Constant::name_from_hsl([0, 100, 50]); # works too
950 say for Chart::Color::Constant::name_from_hsl( 0, 100, 50 ); # 'red', 'red1'
951
952 =head2 names_in_hsl_range
953
954 Color names in selected neighbourhood of hsl color space, that look similar.
955 It requires two arguments. The first one is an array containing three
956 values (hue, saturation and lightness), that define the center of the
957 neighbourhood (searched area).
958
959 The second argument can either be a number or again an array with
960 three values (h,s and l). If its just a number, it will be the radius r
961 of a ball, that defines the neighbourhood. From all colors inside that
962 ball, that are equal distanced or nearer to the center than r, one
963 name will returned.
964
965 If the second argument is an array, it has to contain the tolerance
966 (allowed distance) in h, s and l direction. Please note the h dimension
967 is circular: the distance from 355 to 0 is 5. The s and l dimensions are
968 linear, so that a center value of 90 and a tolerance of 15 will result
969 in a search of in the range 75 .. 100.
970
971 The results contains only one name per color (the shortest).
972
973 # all bright red'ish clors
974 my @names = Chart::Color::Constant::names_in_hsl_range([0, 90, 50], 5);
975 # approximates to :
976 my @names = Chart::Color::Constant::names_in_hsl_range([0, 90, 50],[ 3, 3, 3]);
977
978
979 =head2 all_names
980
981 A sorted list of all stored color names.
982
983 =head2 name_taken
984
985 A perlish pseudo boolean tells if the color name is already in use.
986
987 =head2 add_rgb
988
989 Adding a color to the store under an not taken (not already used) name.
990 Arguments are name, red, green and blue value (integer < 256, see rgb).
991
992 Chart::Color::Constant::add_rgb('nightblue', 15, 10, 121 );
993 Chart::Color::Constant::add_rgb('nightblue', [15, 10, 121]);
994
995 =head2 add_hsl
996
997 Adding a color to the store under an not taken (not already used) name.
998 Arguments are name, hue, saturation and lightness value (see hsl).
999
1000 Chart::Color::Constant::add_rgb('lucky', 0, 100, 50 );
1001 Chart::Color::Constant::add_rgb('lucky', [0, 100, 50]);
1002
1003 =head2 NAMES
1004
1005 white, black, red, green, blue, yellow, purple, pink, peach, plum, mauve, brown, grey
1006
1007 aliceblue, antiquewhite, antiquewhite1, antiquewhite2, antiquewhite3,
1008 antiquewhite4, aqua, aquamarine, aquamarine1, aquamarine2, aquamarine3,
1009 aquamarine4, azure, azure1, azure2, azure3, azure4, beige, bisque, bisque1,
1010 bisque2, bisque3, bisque4, blanchedalmond, blue1, blue2, blue3, blue4,
1011 blueviolet, brown1, brown2, brown3, brown4, burlywood, burlywood1,
1012 burlywood2, burlywood3, burlywood4, cadetblue, cadetblue1, cadetblue2,
1013 cadetblue3, cadetblue4, chartreuse, chartreuse1, chartreuse2, chartreuse3,
1014 chartreuse4, chocolate, chocolate1, chocolate2, chocolate3, chocolate4,
1015 coral, coral1, coral2, coral3, coral4, cornflowerblue, cornsilk, cornsilk1,
1016 cornsilk2, cornsilk3, cornsilk4, crimson, cyan, cyan1, cyan2, cyan3, cyan4,
1017 darkblue, darkcyan, darkgoldenrod, darkgoldenrod1, darkgoldenrod2,
1018 darkgoldenrod3, darkgoldenrod4, darkgray, darkgreen, darkkhaki, darkmagenta,
1019 darkolivegreen, darkolivegreen1, darkolivegreen2, darkolivegreen3,
1020 darkolivegreen4, darkorange, darkorange1, darkorange2, darkorange3,
1021 darkorange4, darkorchid, darkorchid1, darkorchid2, darkorchid3,
1022 darkorchid4, darkred, darksalmon, darkseagreen, darkseagreen1,
1023 darkseagreen2, darkseagreen3, darkseagreen4, darkslateblue, darkslategray,
1024 darkslategray1, darkslategray2, darkslategray3, darkslategray4,
1025 darkturquoise, darkviolet, deeppink, deeppink1, deeppink2, deeppink3,
1026 deeppink4, deepskyblue, deepskyblue1, deepskyblue2, deepskyblue3,
1027 deepskyblue4, dimgray, dodgerblue, dodgerblue1, dodgerblue2, dodgerblue3,
1028 dodgerblue4, firebrick, firebrick1, firebrick2, firebrick3, firebrick4,
1029 floralwhite, forestgreen, fuchsia, gainsboro, ghostwhite, gold, gold1,
1030 gold2, gold3, gold4, goldenrod, goldenrod1, goldenrod2, goldenrod3,
1031 goldenrod4, gray, gray1, gray2, gray3, gray4, gray5, gray6, gray7, gray8,
1032 gray9, gray10, gray11, gray12, gray13, gray14, gray15, gray16, gray17,
1033 gray18, gray19, gray20, gray21, gray22, gray23, gray24, gray25, gray26,
1034 gray27, gray28, gray29, gray30, gray31, gray32, gray33, gray34, gray35,
1035 gray36, gray37, gray38, gray39, gray40, gray41, gray42, gray43, gray44,
1036 gray45, gray46, gray47, gray48, gray49, gray50, gray51, gray52, gray53,
1037 gray54, gray55, gray56, gray57, gray58, gray59, gray60, gray61, gray62,
1038 gray63, gray64, gray65, gray66, gray67, gay68, gray69, gray70, gray71,
1039 gray72, gray73, gray74, gray75, gray76, gray77, gray78, gray79, gray80,
1040 gray81, gray82, gray83, gray84, gray85, gray86, gray87, gray88, gray89,
1041 gray90, gray91, gray92, gray93, gray94, gray95, gray97, gray98, gray99,
1042 green1, green2, green3, green4, greenyellow, grey1, grey2, grey3, grey4,
1043 honeydew, honeydew1, honeydew2, honeydew3, honeydew4, hotpink, hotpink1,
1044 hotpink2, hotpink3, hotpink4, indianred, indianred1, indianred2, indianred3,
1045 indianred4, indigo, ivory, ivory1, ivory2, ivory3, ivory4, khaki, khaki1,
1046 khaki2, khaki3, khaki4, lavender, lavenderblush, lavenderblush1,
1047 lavenderblush2, lavenderblush3, lavenderblush4, lawngreen, lemonchiffon,
1048 lemonchiffon1, lemonchiffon2, lemonchiffon3, lemonchiffon4, light,
1049 lightblue, lightblue1, lightblue2, lightblue3,lightblue4, lightcoral,
1050 lightcyan, lightcyan1, lightcyan2, lightcyan3, lightcyan4, lightgoldenrod,
1051 lightgoldenrod1, lightgoldenrod2, lightgoldenrod3, lightgoldenrod4,
1052 lightgray, lightgreen, lightpink, lightpink1, lightpink2, lightpink3,
1053 lightpink4, lightpurple, lightsalmon, lightsalmon1, lightsalmon2,
1054 lightsalmon3, lightsalmon4, lightseagreen, lightskyblue, lightskyblue1,
1055 lightskyblue2, lightskyblue3, lightskyblue4, lightslateblue, lightslategray,
1056 lightsteelblue, lightsteelblue1, lightsteelblue2, lightsteelblue3,
1057 lightsteelblue4, lightyellow, lightyellow1, lightyellow2, lightyellow3,
1058 lightyellow4, lime, limegreen, linen, magenta, magenta1, magenta2, magenta3,
1059 magenta4, maroon, maroon1, maroon2, maroon3, maroon4, medium,
1060 mediumaquamarine, mediumblue, mediumorchid, mediumorchid1, mediumorchid2,
1061 mediumorchid3, mediumorchid4, mediumpurple, mediumpurple1, mediumpurple2,
1062 mediumpurple3, mediumpurple4, mediumseagreen, mediumslateblue,
1063 mediumspringgreen, mediumturquoise, mediumvioletred, midnightblue,
1064 mintcream, mistyrose, mistyrose1, mistyrose2, mistyrose3, mistyrose4,
1065 moccasin, navajowhite, navajowhite1, navajowhite2, navajowhite3,
1066 navajowhite4, navy, navyblue, oldlace, olive, olivedrab, olivedrab1,
1067 olivedrab2, olivedrab3, olivedrab4, orange, orange1, orange2, orange3,
1068 orange4, orangered, orangered1, orangered2, orangered3, orangered4,
1069 orchid, orchid1, orchid2, orchid3, orchid4, pale, palegoldenrod, palegreen,
1070 palegreen1, palegreen2, palegreen3, palegreen4, paleturquoise,
1071 paleturquoise1, paleturquoise2, paleturquoise3, paleturquoise4,
1072 palevioletred, palevioletred1, palevioletred2, palevioletred3,
1073 palevioletred4, papayawhip, peachpuff, peachpuff1, peachpuff2, peachpuff3,
1074 peachpuff4, peru, pink1, pink2, pink3, pink4, plum1, plum2, plum3, plum4,
1075 powderblue, purple1, purple2, purple3, purple4, rebeccapurple, red1, red2,
1076 red3, red4, rosybrown, rosybrown1, rosybrown2, rosybrown3, rosybrown4,
1077 royalblue, royalblue1, royalblue2, royalblue3, royalblue4, saddlebrown,
1078 salmon, salmon1, salmon2, salmon3, salmon4, sandybrown, seagreen,
1079 seagreen1, seagreen2, seagreen3, seagreen4, seashell, seashell1, seashell2,
1080 seashell3, seashell4, sienna, sienna1, sienna2, sienna3, sienna4, silver,
1081 skyblue, skyblue1, skyblue2, skyblue3, skyblue4, slateblue, slateblue1,
1082 slateblue2, slateblue3, slateblue4, slategray, slategray1, slategray2,
1083 slategray3, slategray4, snow, snow1, snow2, snow3, snow4, springgreen,
1084 springgreen1, springgreen2, springgreen3, springgreen4, steelblue,
1085 steelblue1, steelblue2, steelblue3, steelblue4, tan, tan1, tan2, tan3,
1086 tan4, teal, thistle, thistle1, thistle2, thistle3, thistle4, tomato,
1087 tomato1, tomato2, tomato3, tomato4, turquoise, turquoise1, turquoise2,
1088 turquoise3, turquoise4, violet, violetred, violetred1, violetred2,
1089 violetred3, violetred4, wheat, wheat1, wheat2, wheat3, wheat4, whitesmoke,
1090 yellow1, yellow2, yellow3, yellow4, yellowgreen
1091
1092 marsala, radiandorchid, emerald, tangerinetango, honeysucle, turquoise,
1093 mimosa, blueizis, chilipepper, sanddollar, blueturquoise, tigerlily,
1094 aquasky, truered, fuchsiarose, ceruleanblue, rosequartz, peachecho,
1095 serenity, snorkelblue, limpetshell, lilacgrey, icedcoffee, fiesta,
1096 buttercup, greenflash, riverside, airyblue, sharkskin, aurorared,
1097 warmtaupe, dustycedar, lushmeadow, spicymustard, pottersclay, bodacious,
1098 greenery, niagara, primroseyellow, lapisblue, flame, islandparadise,
1099 paledogwood, pinkyarrow, kale, hazelnut, grenadine, balletslipper,
1100 butterum, navypeony, neutralgray, shadedspruce, goldenlime, marina,
1101 autumnmaple, meadowlark, cherrytomato, littleboyblue, chilioil,
1102 pinklavender, bloomingdahlia, arcadia, ultraviolet, emperador,
1103 almostmauve, springcrocus, sailorblue, harbormist, warmsand, coconutmilk,
1104 redpear, valiantpoppy, nebulasblue, ceylonyellow, martiniolive,
1105 russetorange, crocuspetal, limelight, quetzalgreen, sargassosea, tofu,
1106 almondbuff, quietgray, meerkat, fiesta, jesterred, turmeric, livingcoral,
1107 pinkpeacock, pepperstem, aspengold, princessblue, toffee, mangomojito,
1108 terrariummoss, sweetlilac, soybean, eclipse, sweetcorn, browngranite,
1109 chilipepper, bikingred, peachpink, rockyroad, fruitdove, sugaralmond,
1110 darkcheddar, galaxyblue, bluestone, orangetiger, eden, vanillacustard,
1111 eveningblue, paloma, guacamole, flamescarlet, saffron, biscaygreen, chive,
1112 fadeddenim, orangepeel, mosaicblue, sunlight, coralpink, grapecompote,
1113 lark, navyblazer, brilliantwhite, ash, amberglow, samba, sandstone,
1114 classicblue, greensheen, rosetan, ultramarinegreen, firedbrick,
1115 peachnougat, magentapurple, marigold, cerulean, rust, illuminating,
1116 frenchblue, greenash, burntcoral, mint, amethystorchid, raspberrysorbet,
1117 inkwell, ultimategray, buttercream, desertmist, willow
1118
1119 =for HTML <p>
1120 <img src="https://raw.githubusercontent.com/lichtkind/Chart/main/dev/function/color/table/color_table0.png" alt="color table 1">
1121 <img src="https://raw.githubusercontent.com/lichtkind/Chart/main/dev/function/color/table/color_table1.png" alt="color table 2">
1122 <img src="https://raw.githubusercontent.com/lichtkind/Chart/main/dev/function/color/table/color_table2.png" alt="color table 3">
1123 <img src="https://raw.githubusercontent.com/lichtkind/Chart/main/dev/function/color/table/color_table3.png" alt="color table 4">
1124 </p>
1125
1126 =head1 COPYRIGHT & LICENSE
1127
1128 Copyright 2022 Herbert Breunung.
1129
1130 This program is free software; you can redistribute it and/or modify it
1131 under same terms as Perl itself.
1132
1133 =head1 AUTHOR
1134
1135 Herbert Breunung, <lichtkind@cpan.org>
+0
-262
lib/Chart/Color/Value.pm less more
0 use v5.12;
1
2 # check, convert and measure color values
3
4 package Chart::Color::Value;
5 our $VERSION = 'v2.403.7';
6 use Carp;
7
8 sub check_rgb { # carp returns 1
9 my (@rgb) = @_;
10 my $help = 'has to be an integer between 0 and 255';
11 return carp "need exactly 3 positive integer values 0 <= n < 256 for rgb input" unless @rgb == 3;
12 return carp "red value $rgb[0] ".$help unless int $rgb[0] == $rgb[0] and $rgb[0] >= 0 and $rgb[0] < 256;
13 return carp "green value $rgb[1] ".$help unless int $rgb[1] == $rgb[1] and $rgb[1] >= 0 and $rgb[1] < 256;
14 return carp "blue value $rgb[2] ".$help unless int $rgb[2] == $rgb[2] and $rgb[2] >= 0 and $rgb[2] < 256;
15 0;
16 }
17
18 sub check_hsl {
19 my (@hsl) = @_;
20 my $help = 'has to be an integer between 0 and';
21 return carp "need exactly 3 positive integer between 0 and 359 or 99 for hsl input" unless @hsl == 3;
22 return carp "hue value $hsl[0] $help 359" unless int $hsl[0] == $hsl[0] and $hsl[0] >= 0 and $hsl[0] < 360;
23 return carp "saturation value $hsl[1] $help 100" unless int $hsl[1] == $hsl[1] and $hsl[1] >= 0 and $hsl[1] < 101;
24 return carp "lightness value $hsl[2] $help 100" unless int $hsl[2] == $hsl[2] and $hsl[2] >= 0 and $hsl[2] < 101;
25 0;
26 }
27
28 sub trim_rgb { # cut values into the domain of definition of 0 .. 255
29 my (@rgb) = @_;
30 return (0,0,0) unless @rgb == 3;
31 for (0..2){
32 $rgb[$_] = 0 if $rgb[$_] < 0;
33 $rgb[$_] = 255 if $rgb[$_] > 255;
34 }
35 $rgb[$_] = round($rgb[$_]) for 0..2;
36 @rgb;
37 }
38
39 sub trim_hsl { # cut values into 0 ..359, 0 .. 100, 0 .. 100
40 my (@hsl) = @_;
41 return (0,0,0) unless @hsl == 3;
42 $hsl[0] += 360 while $hsl[0] < 0;
43 $hsl[0] -= 360 while $hsl[0] >= 360;
44 for (1..2){
45 $hsl[$_] = 0 if $hsl[$_] < 0;
46 $hsl[$_] = 100 if $hsl[$_] > 100;
47 }
48 $hsl[$_] = round($hsl[$_]) for 0..2;
49 @hsl;
50 }
51
52 sub difference_rgb { # \@rgb, \@rgb --> @rgb distance as vector
53 my ($rgb, $rgb2) = @_;
54 return carp "need two triplets of rgb values in 2 arrays to compute rgb differences"
55 unless ref $rgb eq 'ARRAY' and @$rgb == 3 and ref $rgb2 eq 'ARRAY' and @$rgb2 == 3;
56 check_rgb(@$rgb) and return;
57 check_rgb(@$rgb2) and return;
58 (abs($rgb->[0] - $rgb2->[0]), abs($rgb->[1] - $rgb2->[1]), abs($rgb->[2] - $rgb2->[2]) );
59 }
60
61 sub difference_hsl { # \@hsl, \@hsl --> $d
62 my ($hsl, $hsl2) = @_;
63 return carp "need two triplets of hsl values in 2 arrays to compute hsl differences"
64 unless ref $hsl eq 'ARRAY' and @$hsl == 3 and ref $hsl2 eq 'ARRAY' and @$hsl2 == 3;
65 check_hsl(@$hsl) and return;
66 check_hsl(@$hsl2) and return;
67 my $delta_h = abs($hsl->[0] - $hsl2->[0]);
68 $delta_h = 360 - $delta_h if $delta_h > 180;
69 ($delta_h, abs($hsl->[1] - $hsl2->[1]), abs($hsl->[2] - $hsl2->[2]) );
70 }
71
72 sub distance_rgb { # \@rgb, \@rgb --> $d
73 return carp "need two triplets of rgb values in 2 arrays to compute rgb distance " if @_ != 2;
74 my @delta_rgb = difference_rgb( $_[0], $_[1] );
75 return unless @delta_rgb == 3;
76 sqrt($delta_rgb[0] ** 2 + $delta_rgb[1] ** 2 + $delta_rgb[2] ** 2);
77 }
78
79 sub distance_hsl { # \@hsl, \@hsl --> $d
80 return carp "need two triplets of hsl values in 2 arrays to compute hsl distance " if @_ != 2;
81 my @delta_hsl = difference_hsl( $_[0], $_[1] );
82 return unless @delta_hsl == 3;
83 sqrt($delta_hsl[0] ** 2 + $delta_hsl[1] ** 2 + $delta_hsl[2] ** 2);
84 }
85
86 sub hsl_from_rgb { # convert color value triplet (int --> int), (real --> real) if $real
87 my (@rgb) = @_;
88 my $real = '';
89 if (ref $rgb[0] eq 'ARRAY'){
90 @rgb = @{$rgb[0]};
91 $real = $rgb[1] // $real;
92 }
93 check_rgb( @rgb ) and return unless $real;
94 my @hsl = _hsl_from_rgb( @rgb );
95 return @hsl if $real;
96 ( round( $hsl[0] ), round( $hsl[1] ), round( $hsl[2] ) );
97 }
98
99 sub rgb_from_hsl { # convert color value triplet (int > int), (real > real) if $real
100 my (@hsl) = @_;
101 my $real = '';
102 if (ref $hsl[0] eq 'ARRAY'){
103 @hsl = @{$hsl[0]};
104 $real = $hsl[1] // $real;
105 }
106 check_hsl( @hsl ) and return unless $real;
107 my @rgb = _rgb_from_hsl( @hsl );
108 return @rgb if $real;
109 ( round( $rgb[0] ), round( $rgb[1] ), round( $rgb[2] ) );
110 }
111
112 sub hex_from_rgb { return unless @_ == 3; sprintf "#%02x%02x%02x", @_ }
113 sub rgb_from_hex { # translate #000000 and #000 --> r, g, b
114 my $hex = shift;
115 return carp "hex color definition '$hex' has to start with # followed by 3 or 6 hex characters (0-9,a-f)"
116 unless defined $hex and (length($hex) == 4 or length($hex) == 7) and $hex =~ /^#[\da-f]+$/i;
117 $hex = substr $hex, 1;
118 (length $hex == 3) ? (map { hex($_.$_) } unpack( "a1 a1 a1", $hex))
119 : (map { hex($_ ) } unpack( "a2 a2 a2", $hex));
120 }
121
122 sub _hsl_from_rgb { # float conversion
123 my (@rgb) = @_;
124 my ($maxi, $mini) = (0 , 1); # index of max and min value in @rgb
125 if ($rgb[1] > $rgb[0]) { ($maxi, $mini ) = ($mini, $maxi ) }
126 if ($rgb[2] > $rgb[$maxi]) { $maxi = 2 }
127 elsif ($rgb[2] < $rgb[$mini]) { $mini = 2 }
128 my $delta = $rgb[$maxi] - $rgb[$mini];
129 my $avg = ($rgb[$maxi] + $rgb[$mini]) / 2;
130 my $H = !$delta ? 0 : (2 * $maxi + (($rgb[($maxi+1) % 3] - $rgb[($maxi+2) % 3]) / $delta)) * 60;
131 $H += 360 if $H < 0;
132 my $S = ($avg == 0) ? 0 : ($avg == 255) ? 0 : $delta / (255 - abs((2 * $avg) - 255));
133 ($H, $S * 100, $avg * 0.392156863 );
134 }
135
136 sub _rgb_from_hsl { # float conversion
137 my (@hsl) = @_;
138 $hsl[0] /= 60;
139 my $C = $hsl[1] * (100 - abs($hsl[2] * 2 - 100)) * 0.0255;
140 my $X = $C * (1 - abs($hsl[0] % 2 - 1 + ($hsl[0] - int $hsl[0])));
141 my $m = ($hsl[2] * 2.55) - ($C / 2);
142 return ($hsl[0] < 1) ? ($C + $m, $X + $m, $m)
143 : ($hsl[0] < 2) ? ($X + $m, $C + $m, $m)
144 : ($hsl[0] < 3) ? ( $m, $C + $m, $X + $m)
145 : ($hsl[0] < 4) ? ( $m, $X + $m, $C + $m)
146 : ($hsl[0] < 5) ? ($X + $m, $m, $C + $m)
147 : ($C + $m, $m, $X + $m);
148 }
149
150 my $half = 0.50000000000008;
151
152 sub round {
153 $_[0] >= 0 ? int ($_[0] + $half)
154 : int ($_[0] - $half)
155 }
156
157
158 1;
159
160 __END__
161
162 =pod
163
164 =head1 NAME
165
166 Chart::Color::Value - check, convert and measure color values
167
168 =head1 SYNOPSIS
169
170 my @names = Chart::Color::Value::all_names();
171 my @rgb = Chart::Color::Value::rgb_from_name('darkblue');
172 my @hsl = Chart::Color::Value::hsl_from_name('darkblue');
173 my @hsl2 = Chart::Color::Value::hsl_from_rgb( 5 ,10, 100);
174 my $d = Chart::Color::Value::distance_hsl( \@hsl, \@hsl2);
175
176 Chart::Color::Value::add_rgb('lucky', [0, 100, 50]);
177
178
179 =head1 DESCRIPTION
180
181 RGB and HSL values of named colors from the X11 and HTML standard
182 and Pantone report. Allows also reverse search, storage and conversion
183 of color values.
184
185 This module is supposed to be used by Chart::Color and not directly
186 by the user (for the most part). It converts a stored color name into
187 its values (rgb, hsl or both) and back. One color can have multiple names.
188 Also nearby (similar) colors can be searched. Own colors can be
189 (none permanently) stored for later reference by name. For this a name
190 has to be chosen, that is not already taken. Independently of that
191 can any color be converted from rgb to hsl and back.
192
193
194 =head1 ROUTINES
195
196 =head2 check_rgb
197
198 Return error message if RGB value triplet is not valid (in range).
199
200 =head2 check_hsl
201
202 Return error message if HSL value triplet is not valid (in range).
203
204
205 =head2 trim_rgb
206
207 Change RGB triplet to the nearest valid values.
208
209 =head2 trim_hsl
210
211 Change HSL triplet to the nearest valid values.
212
213
214 =head2 hsl_from_rgb
215
216 Converting an rgb value triplet into the corresponding hsl
217
218 Red, Green and Blue are integer in 0 .. 255.
219 Hue is an integer between 0 .. 359 (hue)
220 and saturation and lightness are 0 .. 100 (percentage).
221 A hue of 360 and 0 (degree in a cylindrical coordinate system) is
222 considered to be the same, this modul deals only with the ladder.
223
224 =head2 rgb_from_hsl
225
226 Converting an hsl value triplet into the corresponding rgb
227 (see rgb_from_name and hsl_from_name). Please not that back and forth
228 conversion can lead to drifting results due to rounding.
229
230 my @rgb = Chart::Color::Value::rgb_from_hsl(0, 90, 50);
231 my @rgb = Chart::Color::Value::rgb_from_hsl([0, 90, 50]); # works too
232 # for real (none integer results), any none zero value works as second arg
233 my @rgb = Chart::Color::Value::rgb_from_hsl([0, 90, 50], 'real');
234
235 =head2 distance_rgb
236
237 Distance in (linear) rgb color space between two coordinates.
238
239
240 my $d = Chart::Color::Value::distance_rgb([1,1,1], [2,2,2]); # approx 1.7
241
242
243 =head2 distance_hsl
244
245 Distance in (cylindrical) hsl color space between two coordinates.
246
247 my $d = Chart::Color::Value::distance_rgb([1,1,1], [356, 3, 2]); # approx 6
248
249
250 =head1 COPYRIGHT & LICENSE
251
252 Copyright 2022 Herbert Breunung.
253
254 This program is free software; you can redistribute it and/or modify it
255 under same terms as Perl itself.
256
257 =head1 AUTHOR
258
259 Herbert Breunung, <lichtkind@cpan.org>
260
261 =cut
+0
-525
lib/Chart/Color.pm less more
0
1 # Chart::Color: read only color holding object
2 # with methods for relation, mixing and transitions
3
4 use v5.12;
5
6 package Chart::Color;
7 our $VERSION = 'v2.403.7';
8
9 use Carp;
10 use Chart::Color::Constant;
11
12 my $new_help = 'constructor of Chart::Color object needs either:'.
13 ' 1. RGB or HSL hash or ref: ->new(r => 255, g => 0, b => 0), ->new({ h => 0, s => 100, l => 50 })'.
14 ' 2. RGB array or ref: ->new( [255, 0, 0 ]) or >new( 255, 0, 0 )'.
15 ' 3. hex form "#FF0000" or "#f00" 4. a name: "red" or "SVG:red".';
16
17 ## constructor #########################################################
18
19 sub new {
20 my ($pkg, @args) = @_;
21 @args = ([@args]) if @args == 3;
22 @args = ({ $args[0] => $args[1], $args[2] => $args[3], $args[4] => $args[5] }) if @args == 6;
23 return carp $new_help unless @args == 1;
24 _new_from_scalar($args[0]);
25 }
26 sub _new_from_scalar {
27 my ($arg) = shift;
28 my $name;
29 if (not ref $arg){ # resolve 'color_name' or '#RRGGBB' -> ($r, $g, $b)
30 my @rgb = _rgb_from_name_or_hex($arg);
31 return unless @rgb == 3;
32 $name = $arg if index( $arg, ':') > -1;
33 $arg = { r => $rgb[0], g => $rgb[1], b => $rgb[2] };
34 } elsif (ref $arg eq 'ARRAY'){
35 return carp "need exactly 3 RGB numbers!" unless @$arg == 3;
36 $arg = { r => $arg->[0], g => $arg->[1], b => $arg->[2] };
37 }
38 return carp $new_help unless ref $arg eq 'HASH' and keys %$arg == 3;
39 my %named_arg = map { _shrink_key($_) => $arg->{$_} } keys %$arg; # reduce keys to lc first char
40
41 my (@rgb, @hsl);
42 if (exists $named_arg{'r'} and exists $named_arg{'g'} and exists $named_arg{'b'}) {
43 @rgb = Chart::Color::Value::trim_rgb(@named_arg{qw/r g b/});
44 @hsl = Chart::Color::Value::hsl_from_rgb( @rgb );
45 } elsif (exists $named_arg{'h'} and exists $named_arg{'s'} and exists $named_arg{'l'}) {
46 @hsl = Chart::Color::Value::trim_hsl( @named_arg{qw/h s l/});
47 @rgb = Chart::Color::Value::rgb_from_hsl( @hsl );
48 } else { return carp "argument keys need to be r, g and b or h, s and l (long names and upper case work too!)" }
49 $name = Chart::Color::Constant::name_from_rgb( @rgb ) unless defined $name;
50 bless [$name, @rgb, @hsl];
51 }
52 sub _rgb_from_name_or_hex {
53 my $arg = shift;
54 my $i = index( $arg, ':');
55 if (substr($arg, 0, 1) eq '#'){ # resolve #RRGGBB -> ($r, $g, $b)
56 return Chart::Color::Value::rgb_from_hex( $arg );
57 } elsif ($i > -1 ){ # resolve pallet:name -> ($r, $g, $b)
58 my $pallet_name = substr $arg, 0, $i-1;
59 my $color_name = substr $arg, $i+1;
60
61 my $module_base = 'Graphics::ColorNames';
62 eval "use $module_base";
63 return carp "$module_base is not installed, but it's needed to load external colors" if $@;
64
65 my $module = $module_base.'::'.$pallet_name;
66 eval "use $module";
67 return carp "$module is not installed, to load color '$color_name'" if $@;
68
69 my $pal = Graphics::ColorNames->new( $pallet_name );
70 my @rgb = $pal->rgb( $color_name );
71 return carp "color '$color_name' was not found, propably not part of $module" unless @rgb == 3;
72 @rgb;
73 } else { # resolve name -> ($r, $g, $b)
74 my @rgb = Chart::Color::Constant::rgb_from_name( $arg );
75 carp "'$arg' is an unknown color name, please check Chart::Color::Constant::all_names()." unless @rgb == 3;
76 @rgb;
77 }
78 }
79
80 ## getter ##############################################################
81
82 sub name { $_[0][0] }
83 sub red { $_[0][1] }
84 sub green { $_[0][2] }
85 sub blue { $_[0][3] }
86 sub hue { $_[0][4] }
87 sub saturation { $_[0][5] }
88 sub lightness { $_[0][6] }
89 sub string { $_[0][0] ? $_[0][0] : "[ $_[0][1], $_[0][2], $_[0][3] ]" }
90
91 sub hsl { @{$_[0]}[4 .. 6] }
92 sub rgb { @{$_[0]}[1 .. 3] }
93 sub rgb_hex { Chart::Color::Value::hex_from_rgb( $_[0]->rgb() ) }
94
95 ## methods ##############################################################
96
97 sub distance_to {
98 my ($self, $c2, $metric) = @_;
99 return croak "missing argument: color object or scalar color definition" unless defined $c2;
100 $c2 = (ref $c2 eq __PACKAGE__) ? $c2 : Chart::Color->new( $c2 );
101 return unless ref $c2 eq __PACKAGE__;
102
103 return Chart::Color::Value::distance_hsl( [$self->hsl], [$c2->hsl] ) unless defined $metric;
104 $metric = lc $metric;
105 return Chart::Color::Value::distance_hsl( [$self->hsl], [$c2->hsl] ) if $metric eq 'hsl';
106 return Chart::Color::Value::distance_rgb( [$self->rgb], [$c2->rgb] ) if $metric eq 'rgb';
107 my @delta_rgb = Chart::Color::Value::difference_rgb( [$self->rgb], [$c2->rgb] );
108 my @delta_hsl = Chart::Color::Value::difference_hsl( [$self->hsl], [$c2->hsl] );
109 my $help = "unknown distance metric: $metric. try r, g, b, rg, rb, gb, rgb, h, s, l, hs, hl, sl, hsl (default).";
110 if (length $metric == 2){
111 if ($metric eq 'hs' or $metric eq 'sh') {return sqrt( $delta_hsl[0] ** 2 + $delta_hsl[1] ** 2 )}
112 elsif ($metric eq 'hl' or $metric eq 'lh') {return sqrt( $delta_hsl[0] ** 2 + $delta_hsl[2] ** 2 )}
113 elsif ($metric eq 'sl' or $metric eq 'ls') {return sqrt( $delta_hsl[1] ** 2 + $delta_hsl[2] ** 2 )}
114 elsif ($metric eq 'rg' or $metric eq 'gr') {return sqrt( $delta_rgb[0] ** 2 + $delta_rgb[1] ** 2 )}
115 elsif ($metric eq 'rb' or $metric eq 'br') {return sqrt( $delta_rgb[0] ** 2 + $delta_rgb[2] ** 2 )}
116 elsif ($metric eq 'gb' or $metric eq 'bg') {return sqrt( $delta_rgb[1] ** 2 + $delta_rgb[2] ** 2 )}
117 }
118 $metric = substr $metric, 0, 1;
119 $metric eq 'h' ? $delta_hsl[0] :
120 $metric eq 's' ? $delta_hsl[1] :
121 $metric eq 'l' ? $delta_hsl[2] :
122 $metric eq 'r' ? $delta_rgb[0] :
123 $metric eq 'g' ? $delta_rgb[1] :
124 $metric eq 'b' ? $delta_rgb[2] : croak $help;
125 }
126
127 sub add {
128 my ($self, @args) = @_;
129 my $add_help = 'Chart::Color->add argument options: 1. a color object with optional factor as second arg, '.
130 '2. a color name as string, 3. a color hex definition as in "#FF0000"'.
131 '4. a list of thre values (RGB) (also in an array ref)'.
132 '5. a hash with RGB and HSL keys (as in new, but can be mixed) (also in an hash ref).';
133 if ((@args == 1 or @args == 2) and ref $args[0] ne 'HASH'){
134 my @add_rgb;
135 if (ref $args[0] eq __PACKAGE__){
136 @add_rgb = $args[0]->rgb;
137 } elsif (ref $args[0] eq 'ARRAY'){
138 @add_rgb = @{$args[0]};
139 return carp "array ref argument needs to have 3 numerical values (RGB) in it." unless @add_rgb == 3;
140 } elsif (not ref $args[0] and not $args[0] =~ /^\d/){
141 @add_rgb = _rgb_from_name_or_hex($args[0]);
142 return unless @add_rgb > 1;
143 } else { return carp $add_help }
144 @add_rgb = ($add_rgb[0] * $args[1], $add_rgb[1] * $args[1], $add_rgb[2] * $args[1]) if defined $args[1];
145 @args = @add_rgb;
146 }
147 my @rgb = $self->rgb;
148 if (@args == 3) {
149 @rgb = Chart::Color::Value::trim_rgb( $rgb[0] + $args[0], $rgb[1] + $args[1], $rgb[2] + $args[2]);
150 return Chart::Color->new( @rgb );
151 }
152 return carp $add_help unless @args and ((@args % 2 == 0) or (ref $args[0] eq 'HASH'));
153 my %arg = ref $args[0] eq 'HASH' ? %{$args[0]} : @args;
154 my %named_arg = map {_shrink_key($_) => $arg{$_}} keys %arg; # clean keys
155 $rgb[0] += delete $named_arg{'r'} // 0;
156 $rgb[1] += delete $named_arg{'g'} // 0;
157 $rgb[2] += delete $named_arg{'b'} // 0;
158 return Chart::Color->new( Chart::Color::Value::trim_rgb( @rgb ) ) unless %named_arg;
159 my @hsl = Chart::Color::Value::_hsl_from_rgb( @rgb );
160 $hsl[0] += delete $named_arg{'h'} // 0;
161 $hsl[1] += delete $named_arg{'s'} // 0;
162 $hsl[2] += delete $named_arg{'l'} // 0;
163 if (%named_arg) {
164 my @nrkey = grep {/^\d+$/} keys %named_arg;
165 return carp "wrong number of numerical arguments (only 3 needed)" if @nrkey;
166 carp "got unknown hash key starting with", map {' '.$_} keys %named_arg;
167 }
168 @hsl = Chart::Color::Value::trim_hsl( @hsl );
169 Chart::Color->new({ H => $hsl[0], S => $hsl[1], L => $hsl[2] });
170 }
171
172 sub blend_with {
173 my ($self, $c2, $pos) = @_;
174 return carp "need color object or definition as first argument" unless defined $c2;
175 $c2 = (ref $c2 eq __PACKAGE__) ? $c2 : _new_from_scalar( $c2 );
176 return unless ref $c2 eq __PACKAGE__;
177 $pos //= 0.5;
178 my $delta_hue = $c2->hue - $self->hue;
179 $delta_hue -= 360 if $delta_hue > 180;
180 $delta_hue += 360 if $delta_hue < -180;
181 my @hsl = ( $self->hue + ($pos * $delta_hue),
182 $self->saturation + ($pos * ($c2->saturation - $self->saturation)),
183 $self->lightness + ($pos * ($c2->lightness - $self->lightness))
184 );
185 @hsl = Chart::Color::Value::trim_hsl( @hsl );
186 Chart::Color->new({ H => $hsl[0], S => $hsl[1], L => $hsl[2] });
187 }
188
189
190 sub gradient_to {
191 my ($self, $c2, $steps, $power) = @_;
192 return carp "need color object or definition as first argument" unless defined $c2;
193 $c2 = (ref $c2 eq __PACKAGE__) ? $c2 : _new_from_scalar( $c2 );
194 return unless ref $c2 eq __PACKAGE__;
195 $steps //= 3;
196 $power //= 1;
197 return carp "third argument (dynamics), has to be positive (>= 0)" if $power <= 0;
198 return $self if $steps == 1;
199 my @colors = ();
200 my @delta_hsl = ($c2->hue - $self->hue, $c2->saturation - $self->saturation,
201 $c2->lightness - $self->lightness );
202 $delta_hsl[0] -= 360 if $delta_hsl[0] > 180;
203 $delta_hsl[0] += 360 if $delta_hsl[0] < -180;
204 for my $i (1 .. $steps-2){
205 my $pos = ($i / ($steps-1)) ** $power;
206 my @hsl = ( $self->hue + ($pos * $delta_hsl[0]),
207 $self->saturation + ($pos * $delta_hsl[1]),
208 $self->lightness + ($pos * $delta_hsl[2]));
209 @hsl = Chart::Color::Value::trim_hsl( @hsl );
210 push @colors, Chart::Color->new({ H => $hsl[0], S => $hsl[1], L => $hsl[2] });
211 }
212 $self, @colors, $c2;
213 }
214
215 sub complementary {
216 my ($self) = shift;
217 my ($count) = int ((shift // 1) + 0.5);
218 my ($saturation_change) = shift // 0;
219 my ($lightness_change) = shift // 0;
220 my @hsl2 = my @hsl_l = my @hsl_r = $self->hsl;
221 $hsl2[0] += 180;
222 $hsl2[1] += $saturation_change;
223 $hsl2[2] += $lightness_change;
224 @hsl2 = Chart::Color::Value::trim_hsl( @hsl2 ); # HSL of C2
225 my $c2 = Chart::Color->new({ h => $hsl2[0], s => $hsl2[1], l => $hsl2[2] });
226 return $c2 if $count < 2;
227 my (@colors_r, @colors_l);
228 my @delta = (360 / $count, (($hsl2[1] - $hsl_r[1]) * 2 / $count), (($hsl2[2] - $hsl_r[2]) * 2 / $count) );
229 for (1 .. ($count - 1) / 2){
230 $hsl_r[$_] += $delta[$_] for 0..2;
231 $hsl_l[0] -= $delta[0];
232 $hsl_l[$_] = $hsl_r[$_] for 1,2;
233 $hsl_l[0] += 360 if $hsl_l[0] < 0;
234 $hsl_r[0] -= 360 if $hsl_l[0] >= 360;
235 push @colors_r, Chart::Color->new({ H => $hsl_r[0], S => $hsl_r[1], L => $hsl_r[2] });
236 unshift @colors_l, Chart::Color->new({ H => $hsl_l[0], S => $hsl_l[1], L => $hsl_l[2] });
237 }
238 push @colors_r, $c2 unless $count % 2;
239 $self, @colors_r, @colors_l;
240 }
241
242 sub _shrink_key { lc substr( $_[0], 0, 1 ) }
243
244 1;
245
246 __END__
247
248 =pod
249
250 =head1 NAME
251
252 Chart::Color - read only single color holding objects
253
254 =head1 SYNOPSIS
255
256 my $red = Chart::Color->new('red');
257 say $red->add('blue')->name; # magenta, mixed in RGB space
258 Chart::Color->new( 0, 0, 255)->hsl # 240, 100, 50 = blue
259 $blue->blend_with({H=> 0, S=> 0, L=> 80}, 0.1);# mix blue with a little grey
260 $red->gradient( '#0000FF', 10); # 10 colors from red to blue
261 $red->complementary( 3 ); # get fitting red green and blue
262
263 =head1 DESCRIPTION
264
265 This module is designed for internal usage. It handles also all color
266 definitions done by users with method "$chart->set(color => {...})".
267 To see which formats are allowed there, read the next section and please
268 note that ->set() handles only scalar values.
269
270 =head1 CONSTRUCTOR
271
272 There are many options to create a color objects. In short you can
273 either use the name of a predefined constant or provide values in RGB
274 or HSL color space.
275
276 =head2 new( 'name' )
277
278 Get a color by providing a name from the X11 or HTML (SVG) standard or
279 a Pantone report. Upper/Camel case will be treated as lower case and
280 inserted underscore letters ('_') will be ignored as perl does in
281 numbers (1_000 == 1000) (see more under L<Chart::Color::Constant>).
282
283 my $color = Chart::Color->new('Emerald');
284 my @names = Chart::Color::Constant::all_names(); # select from these
285
286 =head2 new( 'standard:color' )
287
288 Get a color by name from a specific standard as provided by an external
289 module L<Graphics::ColorNames>::* , which has to be installed separately.
290 * is a placeholder for the pallet name, which might be: Crayola, CSS,
291 EmergyC, GrayScale, HTML, IE, SVG, Werner, WWW or X. In ladder case
292 Graphics::ColorNames::X has to be installed.
293
294 my $color = Chart::Color->new('SVG:green');
295 my @s = Graphics::ColorNames::all_schemes(); # installed pallets
296
297 =head2 new( '#rgb' )
298
299 Color definitions in hexadecimal format as widely used in the web, are
300 also acceptable.
301
302 my $color = Chart::Color->new('#FF0000');
303 my $color = Chart::Color->new('#f00'); # works too
304
305
306 =head2 new( [$r, $g, $b] )
307
308 Triplet of integer RGB values (L</red>, L</green> and L</blue> : 0 .. 255).
309 Out of range values will be corrected to the closest value in range.
310
311
312 my $red = Chart::Color->new( 255, 0, 0 );
313 my $red = Chart::Color->new([255, 0, 0]); # does the same
314
315
316 =head2 new( {r => $r, g => $g, b => $b} )
317
318 Hash with the keys 'r', 'g' and 'b' does the same as previous paragraph,
319 only more declarative. Casing of the keys will be normalised and only
320 the first letter of each key is significant.
321
322 my $red = Chart::Color->new( r => 255, g => 0, b => 0 );
323 my $red = Chart::Color->new({r => 255, g => 0, b => 0}); # works too
324 ... Color->new( Red => 255, Green => 0, Blue => 0); # also fine
325
326 =head2 new( {h => $h, s => $s, l => $l} )
327
328 To define a color in HSL space, with values for L</hue>, L</saturation> and
329 L</lightness>, use the following keys, which will be normalized as decribed
330 in previous paragraph. Out of range values will be corrected to the
331 closest value in range. Since L</hue> is a polar coordinate,
332 it will be rotated into range, e.g. 361 = 1.
333
334 my $red = Chart::Color->new( h => 0, s => 100, b => 50 );
335 my $red = Chart::Color->new({h => 0, s => 100, b => 50}); # good too
336 ... ->new( Hue => 0, Saturation => 100, Lightness => 50 ); # also fine
337
338
339 =head1 GETTER / ATTRIBUTES
340
341 are all read only methods - giving access to different parts of the
342 objects data.
343
344 =head2 name
345
346 Name of the color in the X11 or HTML (SVG) standard or the Pantone report.
347 The name will be found and filled in, even when the object is created
348 with RGB or HSL values. If the color is not found in any of the mentioned
349 standards, it returns an empty string. All names are at: L<Chart::Color::Constant/NAMES>
350
351 =head2 string
352
353 String to reproduce color object by: Chart::Color->new (eval $string).
354 It is either the name (if color has one) or the stringified triplet:
355 "[ $red, $green, $blue ]".
356
357 =head2 red
358
359 Integer between 0 .. 255 describing the red portion in RGB space.
360
361 =head2 green
362
363 Integer between 0 .. 255 describing the green portion in RGB space.
364
365 =head2 blue
366
367 Integer between 0 .. 255 describing the blue portion in RGB space.
368
369 =head2 rgb
370
371 Three values of red, green and blue (see above).
372
373 =head2 rgb_hex
374
375 String starting with '#', followed by six hexadecimal figures.
376 Two digits for each of red, green and blue value - the format used in CSS.
377
378 =head2 hue
379
380 Integer between 0 .. 359 describing the angle (in degrees) of the
381 circular dimension in HSL space named hue.
382 0 approximates red, 30 - orange, 60 - yellow, 120 - green, 180 - cyan,
383 240 - blue, 270 - violet, 300 - magenta, 330 - pink.
384 0 and 360 point to the same coordinate, but this module only deals with 0.
385
386 =head2 saturation
387
388 Integer between 0 .. 100 describing percentage of saturation in HSL space.
389 0 is grey and 100 the most colorful (except when lightness is 0 or 100).
390
391 =head2 lightness
392
393 Integer between 0 .. 100 describing percentage of lightness in HSL space.
394 0 is always black, 100 is always white and 50 the most colorful
395 (depending on hue value) (or grey - if saturation = 0).
396
397 =head2 hsl
398
399 Three values of hue, saturation and lightness (see above).
400
401 =head1 METHODS
402
403 create new, related color (objects) or compute similarity of colors
404
405 =head2 distance_to
406
407 A number that measures the distance (difference) between two colors:
408 1. the calling object (C1) and 2. a provided first argument C2 -
409 color object or scalar data that is acceptable by new method :
410 name or #hex or [$r, $g, $b] or {...} (see chapter L<CONSTRUCTOR>).
411
412 If no second argument is provided, than the difference is the Euclidean
413 distance in cylindric HSL space. If second argument is 'rgb' or 'RGB',
414 then its the Euclidean distance in RGB space. But als subspaces of both
415 are possible, as r, g, b, rg, rb, gb, h, s, l, hs, hl, and sl.
416
417 my $d = $blue->distance_to( 'lapisblue' ); # how close to lapis color?
418 # how different is my blue value to airy_blue
419 $d = $blue->distance_to( 'airyblue', 'Blue'); # same amount of blue?
420 $d = $color->distance_to( $c2, 'Hue' ); # same hue ?
421 $d = $color->distance_to( [10, 32, 112 ], 'rgb' );
422 $d = $color->distance_to( { Hue => 222, Sat => 23, Light => 12 } );
423
424 =head2 add
425
426 Create a Chart::Color object, by adding any RGB or HSL values to current
427 color. (Same rules apply for key names as in new - values can be negative.)
428 RGB and HSL can be combined, but please note that RGB are applied first.
429
430 If the first argument is a Chart::Color object, than RGB values will be added.
431 In that case an optional second argument is a factor (default = 1),
432 by which the RGB values will be multiplied before being added. Negative
433 values of that factor lead to darkening of result colors, but its not
434 subtractive color mixing, since this module does not support CMY color
435 space. All RGB operations follow the logic of additive mixing, and the
436 result will be rounded (trimmed), to keep it inside the defined RGB space.
437
438 my $blue = Chart::Color->new('blue');
439 my $darkblue = $blue->add( Lightness => -25 );
440 my $blue2 = $blue->add( blue => 10 );
441 $blue->distance( $blue2 ); # == 0, can't get bluer than blue
442 my $color = $blue->add( $c2, -1.2 ); # subtract color c2 with factor 1.2
443
444 =head2 blend_with
445
446 Create Chart::Color object, that is the average of two colors in HSL space:
447 1. the calling object (C1) and 2. a provided argument C2 (object or a
448 refrence to data that is acceptable definition).
449
450 The second argument is the blend ratio, which defaults to 0.5 ( 1:1 ).
451 0 represents here C1 and 1 C2. Numbers below 0 and above 1 are possible,
452 and will be applied, as long the result is inside the finite HSL space.
453 There is a slight overlap with the add method which mostly operates in
454 RGB (unless told so), while this method always operates in HSL space.
455
456 my $c = $color->blend_with( Chart::Color->new('silver') );
457 $color->blend_with( 'silver' ); # same thing
458 $color->blend_with( [192, 192, 192] ); # still same
459 my $difference = $color->blend_with( $c2, -1 );
460
461
462 =head2 gradient_to
463
464 Creates a gradient (a list of colors that build a transition) between
465 current (C1) and a second, given color (C2).
466
467 The first argument is C2. Either as an Chart::Color object or a
468 scalar (name, hex or reference), which is acceptable to the method new.
469
470 Second argument is the number $n of colors, which make up the gradient
471 (including C1 and C2). It defaults to 3. These 3 colors C1, C2 and a
472 color in between, which is the same as the result of method blend_with.
473
474 Third argument is also a positive number $p, which defaults to one.
475 It defines the dynamics of the transition between the two colors.
476 If $p == 1 you get a linear transition - meaning the distance in HSL
477 space (distance_hsl) is equal from one color to the next. If $p != 1,
478 the formula $n ** $p starts to create a parabola function, which defines
479 a none linear mapping. For values $n > 1 the transition starts by sticking
480 to C1 and slowly getting faster and faster toward C2. Values $n < 1 do
481 the opposite: starting by moving fastest from C1 to C2 (big distances)
482 and becoming slower and slower.
483
484 my @colors = $c->gradient_to( $grey, 5 ); # we turn to grey
485 @colors = $c1->gradient_to( [14,10,222], 10, 3 ); # none linear gradient
486
487 =head2 complementary
488
489 Creates a set of complementary colors.
490 It accepts 3 numerical arguments: n, delta_S and delta_L.
491
492 Imagine an horizontal circle in HSL space, whith a center in the (grey)
493 center column. The saturation and lightness of all colors on that
494 circle is the same, they differ only in hue. The color of the current
495 color object ($self a.k.a C1) lies on that circle as well as C2,
496 which is 180 degrees (half the circumference) apposed to C1.
497
498 This circle will be divided in $n (first argument) equal partitions,
499 creating $n equally distanced colors. All of them will be returned,
500 as objects, starting with C1. However, when $n is set to 1 (default),
501 the result is only C2, which is THE complementary color to C1.
502
503 The second argument moves C2 along the S axis (both directions),
504 so that the center of the circle is no longer in the HSL middle column
505 and the complementary colors differ in saturation. (C1 stays unmoved. )
506
507 The third argument moves C2 along the L axis (vertical), which gives the
508 circle a tilt, so that the complementary colors will differ in lightness.
509
510 my @colors = $c->complementary( 3, +20, -10 );
511
512 =head1 COPYRIGHT & LICENSE
513
514 Copyright 2022 Herbert Breunung.
515
516 This program is free software; you can redistribute it and/or modify it
517 under same terms as Perl itself.
518
519 =head1 AUTHOR
520
521 Herbert Breunung, <lichtkind@cpan.org>
522
523 =cut
524
33
44 package Chart::Composite;
55 our @ISA = qw(Chart::Base);
6 our $VERSION = 'v2.403.7';
6 our $VERSION = 'v2.403.8';
77
88 use Chart::Base;
99 use GD;
33 use v5.12;
44
55 package Chart::Constants;
6 our $VERSION = 'v2.403.7';
6 our $VERSION = 'v2.403.8';
77
88 use constant PI => 4 * atan2( 1, 1 );
99
44
55 package Chart::Direction;
66 our @ISA = qw(Chart::Base);
7 our $VERSION = 'v2.403.7';
7 our $VERSION = 'v2.403.8';
88
99 use Chart::Base;
1010 use GD;
55
66 package Chart::ErrorBars;
77 our @ISA = qw(Chart::Base);
8 our $VERSION = 'v2.403.7';
8 our $VERSION = 'v2.403.8';
99
1010 use Chart::Base;
1111 use GD;
22
33 package Chart::HorizontalBars;
44 our @ISA = qw(Chart::Base);
5 our $VERSION = 'v2.403.7';
5 our $VERSION = 'v2.403.8';
66
77 use Chart::Base;
88 use GD;
44
55 package Chart::Lines;
66 our @ISA = qw(Chart::Base);
7 our $VERSION = 'v2.403.7';
7 our $VERSION = 'v2.403.8';
88
99 use Chart::Base;
1010 use GD;
77
88 package Chart::LinesPoints;
99 our @ISA = qw(Chart::Base);
10 our $VERSION = 'v2.403.7';
10 our $VERSION = 'v2.403.8';
1111
1212 use Chart::Base;
1313 use GD;
5555
5656 names: 'blue'
5757
58 All names are tabulated visually and by name under L<Chart::Color::Constant/NAMES>.
58 All names are tabulated visually and by name under L<Chart::Property::DataType::Color::Constant/NAMES>.
5959 You can also define a color with integer values in the RGB or HSL space.
6060 Acceptable ranges are for RGB: 3x 0..255 and for HSL: 0..359, 2 x 0..100.
6161
6565 HSL hashes: { H => 240, S => 100, L => 50 }
6666 HSL hashes: { hue => 240, saturation => 100, lightness => 50 }
6767
68 Detailed explanation and even more options you find under L<Chart::Color>.
68 Detailed explanation and even more options you find under L<Graphics::Toolkit::Color>.
6969
7070 =head2 font
7171
826826
827827 =head1 AUTHOR
828828
829 David Bonner, <chartgrp@web.de>
829 David Bonner, Chart Group,
830830
831831 Herbert Breunung, <lichtkind@cpan.org>
832832
150150 my $g = Chart::Direction->new( 500, 500 );
151151
152152 $g->add_dataset( 210, 220, 200, 215, 225, 200 );
153 $g->add_dataset( 30, 40, 20, 35, 45, 20 );
153 $g->add_dataset( 30, 40, 20, 35, 45, 20 );
154154
155155 $g->add_dataset( 30, 40, 20, 35, 45, 20 );
156156 $g->add_dataset( 30, 40, 20, 35, 45, 20 );
157157
158158 $g->add_dataset( 120, 130, 110, 125, 135, 110 );
159 $g->add_dataset( 30, 40, 20, 35, 45, 20 );
159 $g->add_dataset( 30, 40, 20, 35, 45, 20 );
160160
161161 $g->add_dataset( 300, 310, 290, 305, 315, 290 );
162 $g->add_dataset( 30, 40, 20, 35, 45, 20 );
162 $g->add_dataset( 30, 40, 20, 35, 45, 20 );
163163
164164 $g->set(
165165 title => 'Direction Demo',
201201
202202 use Chart::ErrorBars;
203203
204 my $g = Chart::ErrorBars->new();
204 my $g = Chart::ErrorBars->new( 500, 400 );
205205 $g->add_dataset(qw(1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2 2.1 2.2 2.3 2.4 2.5));
206 $g->add_dataset(qw(1 1.1 1.2 1.1 1.14 1.15 1.26 1.2 1.1 1.19 1.2 1.4 1.6 2.0 2.5 3.1));
206 $g->add_dataset(qw(1 1.1 1.2 1.1 1.14 1.15 1.26 1.2 1.1 1.19 1.2 1.4 1.6 2.0 2.5 3.1));
207207 $g->add_dataset(qw(0.4 0.1 0.2 0.1 0.14 0.15 0.26 0.27 0.1 0.19 0.2 0.1 0.1 0.2 0.1 0.3));
208208 $g->add_dataset(qw(0.2 0.11 0.12 0.11 0.2 0.3 0.12 0.27 0.11 0.3 0.2 0.2 0.2 0.1 0.1 0.2));
209209 $g->set(
260260 $g->png("hbars.png");
261261
262262
263 =head1 Lines
263 =head2 Lines
264264
265265 =for HTML <p>
266266 <img src="https://raw.githubusercontent.com/lichtkind/Chart/main/dev/example/manual/lines.png" alt="xy chart with lines">
646646
647647 =head1 AUTHOR
648648
649 David Bonner, <chartgrp@web.de>
649 David Bonner,
650650
651651 Herbert Breunung, <lichtkind@cpan.org>
652652
5555
5656 =head1 AUTHOR
5757
58 David Bonner, <chartgrp@web.de>
58 David Bonner, Chart Group,
5959
6060 Herbert Breunung, <lichtkind@cpan.org>
6161
2323
2424 package Chart::Mountain;
2525 our @ISA = qw(Chart::Base);
26 our $VERSION = 'v2.403.7';
26 our $VERSION = 'v2.403.8';
2727
2828 use Chart::Base;
2929 use GD;
11
22 package Chart::Pareto;
33 our @ISA = qw(Chart::Base);
4 our $VERSION = 'v2.403.7';
4 our $VERSION = 'v2.403.8';
55
66 use Chart::Base;
77 use GD;
44
55 package Chart::Pie;
66 our @ISA = qw(Chart::Base);
7 our $VERSION = 'v2.403.7';
7 our $VERSION = 'v2.403.8';
88
99 use Carp;
1010 use GD;
44
55 package Chart::Points;
66 our @ISA = qw(Chart::Base);
7 our $VERSION = 'v2.403.7';
7 our $VERSION = 'v2.403.8';
88
99 use Carp;
1010 use GD;
0
1 # Chart::Color: read only color holding object
2 # with methods for relation, mixing and transitions
3
4 use v5.12;
5
6 package Chart::Property::DataType::Color;
7 our $VERSION = 'v2.403.8';
8
9 use base qw(Graphics::Toolkit::Color);
10
11 1;
12 __END__
13
14 =pod
15
16 moved to L<Graphics::Toolkit::Color>
17
18 =cut
33 use v5.12;
44
55 package Chart::Property::DataType::Font;
6 our $VERSION = 'v2.403.7';
6 our $VERSION = 'v2.403.8';
77
88 use Carp;
99
33 use v5.12;
44
55 package Chart::Property;
6 our $VERSION = 'v2.403.7';
6 our $VERSION = 'v2.403.8';
77
88 use Carp;
99
1717
1818 package Chart::Split;
1919 our @ISA = qw(Chart::Base);
20 our $VERSION = 'v2.403.7';
20 our $VERSION = 'v2.403.8';
2121
2222 use Chart::Base;
2323 use GD;
11
22 package Chart::StackedBars;
33 our @ISA = qw(Chart::Base);
4 our $VERSION = 'v2.403.7';
4 our $VERSION = 'v2.403.8';
55
66 use Chart::Base;
77 use GD;
00 use v5.12;
11
22 package Chart;
3 our $VERSION = 'v2.403.7';
3 our $VERSION = 'v2.403.8';
44
55 use Chart::Points;
66 use Chart::Lines;
+0
-169
t/001_color_value.t less more
0 #!/usr/bin/perl
1
2 use v5.12;
3 use warnings;
4 use Test::More tests => 108;
5 use Test::Warn;
6
7 BEGIN { unshift @INC, 'lib', '../lib'}
8 my $module = 'Chart::Color::Value';
9
10 eval "use $module";
11 is( not($@), 1, 'could load the module');
12
13 my $chk_rgb = \&Chart::Color::Value::check_rgb;
14 my $chk_hsl = \&Chart::Color::Value::check_hsl;
15 my $tr_rgb = \&Chart::Color::Value::trim_rgb;
16 my $tr_hsl = \&Chart::Color::Value::trim_hsl;
17 my $d_rgb = \&Chart::Color::Value::distance_rgb;
18 my $d_hsl = \&Chart::Color::Value::distance_hsl;
19 my $rgb2h = \&Chart::Color::Value::hex_from_rgb;
20
21
22 ok( !$chk_rgb->(0,0,0), 'check rgb values works on lower bound values');
23 ok( !$chk_rgb->(255,255,255), 'check rgb values works on upper bound values');
24 warning_like {$chk_rgb->(0,0)} {carped => qr/exactly 3/}, "check rgb got too few values";
25 warning_like {$chk_rgb->(0,0,0,0)} {carped => qr/exactly 3/}, "check rgb got too many values";
26 warning_like {$chk_rgb->(-1, 0,0)} {carped => qr/red value/}, "red value is too small";
27 warning_like {$chk_rgb->(0.5, 0,0)} {carped => qr/red value/}, "red value is not integer";
28 warning_like {$chk_rgb->(256, 0,0)} {carped => qr/red value/}, "red value is too big";
29 warning_like {$chk_rgb->(0, -1, 0)} {carped => qr/green value/}, "green value is too small";
30 warning_like {$chk_rgb->(0, 0.5, 0)} {carped => qr/green value/}, "green value is not integer";
31 warning_like {$chk_rgb->(0, 256,0)} {carped => qr/green value/}, "green value is too big";
32 warning_like {$chk_rgb->(0,0, -1 )} {carped => qr/blue value/}, "blue value is too small";
33 warning_like {$chk_rgb->(0,0, 0.5 )} {carped => qr/blue value/}, "blue value is not integer";
34 warning_like {$chk_rgb->(0,0, 256)} {carped => qr/blue value/}, "blue value is too big";
35
36 ok( !$chk_hsl->(0,0,0), 'check hsl values works on lower bound values');
37 ok( !$chk_hsl->(359,100,100), 'check hsl values works on upper bound values');
38 warning_like {$chk_hsl->(0,0)} {carped => qr/exactly 3/}, "check rgb got too few values";
39 warning_like {$chk_hsl->(0,0,0,0)} {carped => qr/exactly 3/}, "check rgb got too many values";
40 warning_like {$chk_hsl->(-1, 0,0)} {carped => qr/hue value/}, "hue value is too small";
41 warning_like {$chk_hsl->(0.5, 0,0)} {carped => qr/hue value/}, "hue value is not integer";
42 warning_like {$chk_hsl->(360, 0,0)} {carped => qr/hue value/}, "hue value is too big";
43 warning_like {$chk_hsl->(0, -1, 0)} {carped => qr/saturation value/}, "saturation value is too small";
44 warning_like {$chk_hsl->(0, 0.5, 0)} {carped => qr/saturation value/}, "saturation value is not integer";
45 warning_like {$chk_hsl->(0, 101,0)} {carped => qr/saturation value/}, "saturation value is too big";
46 warning_like {$chk_hsl->(0,0, -1 )} {carped => qr/lightness value/}, "lightness value is too small";
47 warning_like {$chk_hsl->(0,0, 0.5 )} {carped => qr/lightness value/}, "lightness value is not integer";
48 warning_like {$chk_hsl->(0,0, 101)} {carped => qr/lightness value/}, "lightness value is too big";
49
50
51
52 my @rgb = $tr_rgb->();
53 is( int @rgb, 3, 'default color is set');
54 is( $rgb[0], 0, 'default color is black (R) no args');
55 is( $rgb[1], 0, 'default color is black (G) no args');
56 is( $rgb[2], 0, 'default color is black (B) no args');
57 @rgb = $tr_rgb->(1,2);
58 is( $rgb[0], 0, 'default color is black (R) too few args');
59 is( $rgb[1], 0, 'default color is black (G) too few args');
60 is( $rgb[2], 0, 'default color is black (B) too few args');
61 @rgb = $tr_rgb->(1,2,3,4);
62 is( $rgb[0], 0, 'default color is black (R) too many args');
63 is( $rgb[1], 0, 'default color is black (G) too many args');
64 is( $rgb[2], 0, 'default color is black (B) too many args');
65 @rgb = $tr_rgb->(-1,-1,-1);
66 is( int @rgb, 3, 'color is trimmed up');
67 is( $rgb[0], 0, 'too low red value is trimmed up');
68 is( $rgb[1], 0, 'too low green value is trimmed up');
69 is( $rgb[2], 0, 'too low blue value is trimmed up');
70 @rgb = $tr_rgb->(256, 256, 256);
71 is( int @rgb, 3, 'color is trimmed up');
72 is( $rgb[0], 255, 'too high red value is trimmed down');
73 is( $rgb[1], 255, 'too high green value is trimmed down');
74 is( $rgb[2], 255, 'too high blue value is trimmed down');
75
76 my @hsl = $tr_hsl->();
77 is( int @hsl, 3, 'default color is set');
78 is( $hsl[0], 0, 'default color is black (H) no args');
79 is( $hsl[1], 0, 'default color is black (S) no args');
80 is( $hsl[2], 0, 'default color is black (L) no args');
81 @hsl = $tr_hsl->(1,2);
82 is( $hsl[0], 0, 'default color is black (H) too few args');
83 is( $hsl[1], 0, 'default color is black (S) too few args');
84 is( $hsl[2], 0, 'default color is black (L) too few args');
85 @hsl = $tr_hsl->(1,2,3,4);
86 is( $hsl[0], 0, 'default color is black (H) too many args');
87 is( $hsl[1], 0, 'default color is black (S) too many args');
88 is( $hsl[2], 0, 'default color is black (L) too many args');;
89 @hsl = $tr_hsl->(-1,-1,-1);
90 is( int @rgb, 3, 'color is trimmed up');
91 is( $hsl[0], 359, 'too low hue value is rotated up');
92 is( $hsl[1], 0, 'too low green value is trimmed up');
93 is( $hsl[2], 0, 'too low blue value is trimmed up');
94 @hsl = $tr_hsl->(360, 101, 101);
95 is( int @rgb, 3, 'color is trimmed up');
96 is( $hsl[0], 0, 'too high hue value is rotated down');
97 is( $hsl[1], 100, 'too high saturation value is trimmed down');
98 is( $hsl[2], 100, 'too high lightness value is trimmed down');
99
100
101 warning_like {Chart::Color::Value::hsl_from_rgb(1,1,1,1)} {carped => qr/3 positive integer/},
102 "need 3 values rgb to convert color from rgb to hsl";
103 warning_like {Chart::Color::Value::hsl_from_rgb(1,1)} {carped => qr/3 positive integer/},
104 "need 3 values rgb to convert color from rgb to hsl";
105 warning_like {Chart::Color::Value::hsl_from_rgb(1,1,-1)} {carped => qr/blue value/},
106 "blue value is too small for conversion";
107 warning_like {Chart::Color::Value::hsl_from_rgb(256,1,0)} {carped => qr/red value/},
108 "red value is too large for conversion";
109 warning_like {Chart::Color::Value::rgb_from_hsl(1,1)} {carped => qr/3 positive integer/},
110 "need 3 values rgb to convert color from rgb to hsl";
111
112 @hsl = Chart::Color::Value::hsl_from_rgb(127, 127, 127);
113 is( int @hsl, 3, 'converted color grey has hsl values');
114 is( $hsl[0], 0, 'converted color grey has computed right hue value');
115 is( $hsl[1], 0, 'converted color grey has computed right saturation');
116 is( $hsl[2], 50, 'converted color grey has computed right lightness');
117
118 @rgb = Chart::Color::Value::rgb_from_hsl(0, 0, 50);
119 is( int @rgb, 3, 'converted back color grey has rgb values');
120 is( $rgb[0], 128, 'converted back color grey has right red value');
121 is( $rgb[1], 128, 'converted back color grey has right green value');
122 is( $rgb[2], 128, 'converted back color grey has right blue value');
123
124 warning_like {$d_rgb->()} {carped => qr/two triplets/},"can't get distance without rgb values";
125 warning_like {$d_rgb->( [1,1,1],[1,1,1],[1,1,1])} {carped => qr/two triplets/},'too many array arg';
126 warning_like {$d_rgb->( [1,2],[1,2,3])} {carped => qr/two triplets/},'first color is missing a value';
127 warning_like {$d_rgb->( [1,2,3],[2,3])} {carped => qr/two triplets/},'second color is missing a value';
128 warning_like {$d_rgb->( [-1,2,3],[1,2,3])} {carped => qr/red value/}, 'first red value is too small';
129 warning_like {$d_rgb->( [1,2,3],[2,256,3])} {carped => qr/green value/}, 'second green value is too large';
130 warning_like {$d_rgb->( [1,2,-3],[2,25,3])} {carped => qr/blue value/}, 'first blue value is too large';
131 warning_like {$d_hsl->( []) } {carped => qr/two triplets/},"can't get distance without hsl values";
132 warning_like {$d_hsl->( [1,1,1],[1,1,1],[1,1,1])} {carped => qr/two triplets/},'too many array arg';
133 warning_like {$d_hsl->( [1,2],[1,2,3])} {carped => qr/two triplets/},'first color is missing a value';
134 warning_like {$d_hsl->( [1,2,3],[2,3])} {carped => qr/two triplets/},'second color is missing a value';
135 warning_like {$d_hsl->( [-1,2,3],[1,2,3])} {carped => qr/hue value/}, 'first hue value is too small';
136 warning_like {$d_hsl->( [1,2,3],[360,2,3])} {carped => qr/hue value/}, 'second hue value is too large';
137 warning_like {$d_hsl->( [1,-1,3],[2,10,3])} {carped => qr/saturation value/},'first saturation value is too small';
138 warning_like {$d_hsl->( [1,2,3],[2,101,3])} {carped => qr/saturation value/},'second saturation value is too large';
139 warning_like {$d_hsl->( [1,1,-1],[2,10,3])} {carped => qr/lightness value/}, 'first lightness value is too small';
140 warning_like {$d_hsl->( [1,2,3],[2,1,101])} {carped => qr/lightness value/}, 'second lightness value is too large';
141
142 is( Chart::Color::Value::distance_rgb([1, 2, 3], [ 2, 6, 11]), 9, 'compute rgb distance');
143 is( Chart::Color::Value::distance_hsl([1, 2, 3], [ 2, 6, 11]), 9, 'compute hsl distance');
144 is( Chart::Color::Value::distance_hsl([0, 2, 3], [359, 6, 11]), 9, 'compute hsl distance (test circular property of hsl)');
145
146
147 is( $rgb2h->(0,0,0), '#000000', 'converted black from rgb to hex');
148 is( uc $rgb2h->(255,255,255), '#FFFFFF', 'converted white from rgb to hex');
149 is( uc $rgb2h->( 10, 20, 30), '#0A141E', 'converted random color from rgb to hex');
150
151 @rgb = Chart::Color::Value::rgb_from_hex('#000000');
152 is( $rgb[0], 0, 'converted black from hex to RGB red is correct');
153 is( $rgb[1], 0, 'converted black from hex to RGB green is correct');
154 is( $rgb[2], 0, 'converted black from hex to RGB blue is correct');
155
156 @rgb = Chart::Color::Value::rgb_from_hex('#FFF');
157 is( $rgb[0], 255, 'converted white (short form) from hex to RGB red is correct');
158 is( $rgb[1], 255, 'converted white (short form) from hex to RGB green is correct');
159 is( $rgb[2], 255, 'converted white (short form) from hex to RGB blue is correct');
160
161 @rgb = Chart::Color::Value::rgb_from_hex('#0a141e');
162 is( $rgb[0], 10, 'converted random color (lower case) from hex to RGB red is correct');
163 is( $rgb[1], 20, 'converted random color (lower case) from hex to RGB green is correct');
164 is( $rgb[2], 30, 'converted random color (lower case) from hex to RGB blue is correct');
165
166
167
168 exit 0;
+0
-125
t/002_color_constant.t less more
0 #!/usr/bin/perl
1
2 use v5.12;
3 use warnings;
4 use Test::More tests => 57;
5 use Test::Warn;
6
7 BEGIN { unshift @INC, 'lib', '../lib'}
8 my $module = 'Chart::Color::Constant';
9
10 eval "use $module";
11 is( not($@), 1, 'could load the module');
12
13 my @names = Chart::Color::Constant::all_names();
14 is( @names > 700, 1, 'get a large list of names, all_names seems to working');
15
16 my $add_rgb = \&Chart::Color::Constant::add_rgb;
17 my $add_hsl = \&Chart::Color::Constant::add_hsl;
18 my $taken = \&Chart::Color::Constant::name_taken;
19 my $get_name_rgb = \&Chart::Color::Constant::name_from_rgb;
20 my $get_name_hsl = \&Chart::Color::Constant::name_from_hsl;
21 my $get_name_range = \&Chart::Color::Constant::names_in_hsl_range;
22
23 warning_like {$add_rgb->()} {carped => qr/missing first arg/}, "can't get color without name";
24 warning_like {$add_rgb->( 'one',1,1)} {carped => qr/need exactly 3/},'not enough args to add color';
25 warning_like {$add_rgb->( 'one', 0, -1, 25)} {carped => qr/green/}, 'too small green value got cought';
26 warning_like {$add_rgb->( 'one', 0, 1, 256)} {carped => qr/blue/}, 'too large blue value got cought';
27 warning_like {$add_rgb->( 'white', 0, 3, 22 )} {carped => qr/already/}, 'got cought overwriting white';
28
29 is( $taken->('one'), '', 'there is not color named "one"' );
30 is( ref $add_rgb->('one', 1, 2, 3 ), 'ARRAY', 'could add color to store');
31 is( $get_name_rgb->( 1, 2, 3 ), 'one', 'retrieve added color' );
32 is( $taken->('one'), 1, 'there is now a color named "one"' );
33 is( $taken->('One'), 1, 'even there with different spelling');
34 is( ref $add_hsl->('lucky', 0,100, 50),'ARRAY', 'added red under different name');
35 is( ref $add_hsl->('blob', 14, 10, 50),'ARRAY', 'added color by hsl definition');
36
37 is( $get_name_rgb->( 255 ,255, 255 ), 'white', 'could get a color def');
38 is( scalar $get_name_rgb->( 255, 215, 0 ), 'gold', 'selects shorter name: gold instead of gold1');
39 is( scalar $get_name_rgb->( [255, 215, 0]),'gold', 'array ref arg format works too');
40 is( scalar $get_name_rgb->( 255, 0, 0 ), 'red', 'selects shorter name red instead of inserted lucky');
41 is( $get_name_hsl->( 0, 100, 50 ), 'red', 'found red by hsl');
42 is( $get_name_hsl->( 14, 10, 50 ), 'blob', 'found inserted color by hsl');
43
44 my @rgb = Chart::Color::Constant::rgb_from_name('white');
45 my @hsl = Chart::Color::Constant::hsl_from_name('white');
46 is( int @rgb, 3, 'white has 3 rgb values');
47 is( $rgb[0], 255, 'white has full red value');
48 is( $rgb[1], 255, 'white has full green value');
49 is( $rgb[2], 255, 'white has full blue value');
50 is( int @hsl, 3, 'white has 3 hsl values');
51 is( $hsl[0], 0, 'white has zero hue value');
52 is( $hsl[1], 0, 'white has zero sat value');
53 is( $hsl[2], 100, 'white has full light value');
54
55 @rgb = Chart::Color::Constant::rgb_from_name('one');
56 @hsl = Chart::Color::Constant::hsl_from_name('one');
57 is( int @rgb, 3, 'self defined color has rgb values');
58 is( $rgb[0], 1, 'self defined color has defined red value');
59 is( $rgb[1], 2, 'self defined color has defined full green value');
60 is( $rgb[2], 3, 'self defined color has defined full blue value');
61 is( int @hsl, 3, 'self defined color has hsl values');
62 is( $hsl[0], 210, 'self defined color has computed hue value');
63 is( $hsl[1], 50, 'self defined color has computed saturation');
64 is( $hsl[2], 1, 'self defined color has computed lightness');
65
66 @rgb = Chart::Color::Constant::rgb_from_name('One');
67 is( int @rgb, 3, 'upper case gets cleaned from color name');
68 @rgb = Chart::Color::Constant::rgb_from_name('O_ne');
69 is( int @rgb, 3, 'under score gets cleaned from color name');
70
71 warning_like{ $get_name_range->( []) } {carped => qr/two arguments/},"can't get names in range without hsl values";
72 warning_like{ $get_name_range->( [1,1,1],[1,1,1],[1,1,1])} {carped => qr/two arguments/},'too many array arg';
73 warning_like{ $get_name_range->( [1,2],[1,2,3])} {carped => qr/first argument/},'range center is missing a value';
74 warning_like{ $get_name_range->( [1,2,3],[2,3])} {carped => qr/second argument/}, 'tolerances are missing a value';
75 warning_like{ $get_name_range->( [-1,2,3],[1,2,3])} {carped => qr/hue value/}, 'first value of search center is too small';
76 warning_like{ $get_name_range->( [360,2,3],[1,2,3])} {carped => qr/hue value/}, 'first value of search center is too large';
77 warning_like{ $get_name_range->( [1,-1,3],[2,10,3])} {carped => qr/saturation value/}, 'saturation center value is too small';
78 warning_like{ $get_name_range->( [1,101,3],[2,1,3])} {carped => qr/saturation value/}, 'saturation center value is too large';
79 warning_like{ $get_name_range->( [1,1,-1],[2,10,3])} {carped => qr/lightness value/}, 'first lightness value is too small';
80 warning_like{ $get_name_range->( [1,2,101],[2,1,1])} {carped => qr/lightness value/}, 'second lightness value is too large';
81
82 @names = $get_name_range->( [0, 0, 100], 0);
83 is( int @names, 1, 'only one color has distance of 0 to white');
84 is( $names[0], 'white', 'only white has distance of 0 to white');
85
86 @names = sort $get_name_range->( [0, 0, 100], 5);
87 is( int @names, 6, '6 colors are in short distance to white');
88 @names = grep { /whitesmoke/ } @names;
89 is( int @names, 1, 'whitesmoke is near to white');
90
91 my @morenames = sort $get_name_range->( [0, 0, 100], 10);
92 is( @names < @morenames, 1, 'bigger radius has to catch more colors');
93
94 @names = sort $get_name_range->( [240, 100, 50], [10, 20, 30]);
95 @names = grep { /navy/ } @names;
96 is( int @names, 1, 'navy is a shade of blue');
97
98 @names = sort $get_name_range->( [240, 100, 50], [100, 5, 5]);
99 @names = grep { /aqua/ } @names;
100 is( int @names, 1, 'aqua is a bluish color with high saturation and medium lightness');
101
102 @names = sort $get_name_range->( [ 0, 100, 50], [100, 5, 5]);
103 @names = grep { /lightpurple/ } @names;
104 is( int @names, 1, 'purple is near red because hue is circular');
105
106 @names = sort $get_name_range->( [ 359, 100, 50], [100, 5, 5]);
107 @names = grep { /chartreuse/ } @names;
108 is( @names > 0, 1, 'chartreuse is near purple because hue is circular');
109
110 #say for @names;
111 #say scalar $get_name_hsl->(240, 100, 50);
112
113 exit 0;
114
115 __END__
116
117 use Memory::Usage;
118 my $mu = Memory::Usage->new();
119 $mu->record('starting work');
120 eval "use $module";
121 $mu->record('after ');
122 eval "use GD;";
123 $mu->record('GD ');
124 $mu->dump();
+0
-372
t/003_color.t less more
0 #!/usr/bin/perl
1 #
2 use v5.12;
3 use warnings;
4 use Test::More tests => 303;
5 use Test::Warn;
6
7 BEGIN { unshift @INC, 'lib', '../lib'}
8 my $module = 'Chart::Color';
9 eval "use $module";
10 is( not( $@), 1, 'could load the module');
11
12
13 warning_like {Chart::Color->new()} {carped => qr/constructor of/}, "need argument to create object";
14 warning_like {Chart::Color->new('weirdcolorname')} {carped => qr/unknown color/}, "accept only known color names";
15 warning_like {Chart::Color->new('CHIMNEY:red')} {carped => qr/ not installed/}, "accept only known palletes";
16 warning_like {Chart::Color->new('#23232') } {carped => qr/hex color definition/}, "hex definition too short";
17 warning_like {Chart::Color->new('#232321f') } {carped => qr/hex color definition/}, "hex definition too long";
18 warning_like {Chart::Color->new('#23232g') } {carped => qr/hex color definition/}, "hex definition has forbidden chars";
19 warning_like {Chart::Color->new('#2322%E') } {carped => qr/hex color definition/}, "hex definition has forbidden chars";
20 warning_like {Chart::Color->new(1,1)} {carped => qr/constructor of/}, "too few positional args";
21 warning_like {Chart::Color->new(1,1,1,1,1)} {carped => qr/constructor of/}, "too many positional args";
22 warning_like {Chart::Color->new([1,1])} {carped => qr/need exactly 3/}, "too few positional args in ref";
23 warning_like {Chart::Color->new([1,1,1,1])} {carped => qr/need exactly 3/}, "too many positional args in ref";
24 warning_like {Chart::Color->new({ r=>1, g=>1})} {carped => qr/constructor of/}, "too few named args in ref";
25 warning_like {Chart::Color->new({r=>1,g=>1,b=>1,h=>1,})} {carped => qr/constructor of/},"too many name args in ref";
26 warning_like {Chart::Color->new( r=>1)} {carped => qr/constructor of/}, "too few named args";
27 warning_like {Chart::Color->new(r=>1,g=>1,b=>1,h=>1,a=>1)} {carped => qr/constructor of/}, "too many name args";
28 warning_like {Chart::Color->new(r=>1,g=>1,h=>1)} {carped => qr/argument keys/}, "don't mix named args";
29 warning_like {Chart::Color->new(r=>1,g=>1,t=>1)} {carped => qr/argument keys/}, "don't invent named args";
30
31 my $red = Chart::Color->new('red');
32 is( ref $red, $module, 'could create object by name');
33 is( $red->red, 255, 'named red has correct red component value');
34 is( $red->green, 0, 'named red has correct green component value');
35 is( $red->blue, 0, 'named red has correct blue component value');
36 is( $red->hue, 0, 'named red has correct hue component value');
37 is( $red->saturation, 100, 'named red has correct saturation component value');
38 is( $red->lightness, 50, 'named red has correct lightness component value');
39 is( $red->name, 'red', 'named red has correct name');
40 is( $red->rgb_hex, '#ff0000', 'named red has correct hex value');
41 is(($red->rgb)[0], 255, 'named red has correct rgb red component value');
42 is(($red->rgb)[1], 0, 'named red has correct rgb green component value');
43 is(($red->rgb)[2], 0, 'named red has correct rgb blue component value');
44 is(($red->hsl)[0], 0, 'named red has correct hsl hue component value');
45 is(($red->hsl)[1], 100, 'named red has correct hsl saturation component value');
46 is(($red->hsl)[2], 50, 'named red has correct hsl lightness component value');
47 is( $red->string, 'red', 'named red does stringify correctly');
48 is( Chart::Color->new(15,12,13)->string, '[ 15, 12, 13 ]', 'random color does stringify correctly');
49
50
51 $red = Chart::Color->new('#FF0000');
52 is( ref $red, $module, 'could create object by hex value');
53 is( $red->red, 255, 'hex red has correct red component value');
54 is( $red->green, 0, 'hex red has correct green component value');
55 is( $red->blue, 0, 'hex red has correct blue component value');
56 is( $red->hue, 0, 'hex red has correct hue component value');
57 is( $red->saturation, 100, 'hex red has correct saturation component value');
58 is( $red->lightness, 50, 'hex red has correct lightness component value');
59 is( $red->name, 'red', 'hex red has correct name');
60 is( $red->rgb_hex, '#ff0000', 'hex red has correct hex value');
61 is(($red->rgb)[0], 255, 'hex red has correct rgb red component value');
62 is(($red->rgb)[1], 0, 'hex red has correct rgb green component value');
63 is(($red->rgb)[2], 0, 'hex red has correct rgb blue component value');
64 is(($red->hsl)[0], 0, 'hex red has correct hsl hue component value');
65 is(($red->hsl)[1], 100, 'hex red has correct hsl saturation component value');
66 is(($red->hsl)[2], 50, 'hex red has correct hsl lightness component value');
67
68 $red = Chart::Color->new('#f00');
69 is( ref $red, $module, 'could create object by short hex value');
70 is( $red->name, 'red', 'short hex red has correct name');
71
72 $red = Chart::Color->new(255, 0, 0);
73 is( ref $red, $module, 'could create object by positional RGB');
74 is( $red->red, 255, 'positional red has correct red component value');
75 is( $red->green, 0, 'positional red has correct green component value');
76 is( $red->blue, 0, 'positional red has correct blue component value');
77 is( $red->hue, 0, 'positional red has correct hue component value');
78 is( $red->saturation, 100, 'positional red has correct saturation component value');
79 is( $red->lightness, 50, 'positional red has correct lightness component value');
80 is( $red->name, 'red', 'positional red has correct name');
81 is( $red->rgb_hex, '#ff0000', 'positional red has correct hex value');
82 is(($red->rgb)[0], 255, 'positional red has correct rgb red component value');
83 is(($red->rgb)[1], 0, 'positional red has correct rgb green component value');
84 is(($red->rgb)[2], 0, 'positional red has correct rgb blue component value');
85 is(($red->hsl)[0], 0, 'positional red has correct hsl hue component value');
86 is(($red->hsl)[1], 100, 'positional red has correct hsl saturation component value');
87 is(($red->hsl)[2], 50, 'positional red has correct hsl lightness component value');
88
89 $red = Chart::Color->new([255, 0, 0]);
90 is( ref $red, $module, 'could create object by RGB array ref');
91 is( $red->red, 255, 'array ref red has correct red component value');
92 is( $red->green, 0, 'array ref red has correct green component value');
93 is( $red->blue, 0, 'array ref red has correct blue component value');
94 is( $red->hue, 0, 'array ref red has correct hue component value');
95 is( $red->saturation, 100, 'array ref red has correct saturation component value');
96 is( $red->lightness, 50, 'array ref red has correct lightness component value');
97 is( $red->name, 'red', 'array ref red has correct name');
98 is( $red->rgb_hex, '#ff0000', 'array ref red has correct hex value');
99 is(($red->rgb)[0], 255, 'array ref red has correct rgb red component value');
100 is(($red->rgb)[1], 0, 'array ref red has correct rgb green component value');
101 is(($red->rgb)[2], 0, 'array ref red has correct rgb blue component value');
102 is(($red->hsl)[0], 0, 'array ref red has correct hsl hue component value');
103 is(($red->hsl)[1], 100, 'array ref red has correct hsl saturation component value');
104 is(($red->hsl)[2], 50, 'array ref red has correct hsl lightness component value');
105
106 $red = Chart::Color->new(r => 255, g => 0, b => 0);
107 is( ref $red, $module, 'could create object by RGB named args');
108 is( $red->red, 255, 'named arg red has correct red component value');
109 is( $red->green, 0, 'named arg red has correct green component value');
110 is( $red->blue, 0, 'named arg red has correct blue component value');
111 is( $red->hue, 0, 'named arg red has correct hue component value');
112 is( $red->saturation, 100, 'named arg red has correct saturation component value');
113 is( $red->lightness, 50, 'named arg red has correct lightness component value');
114 is( $red->name, 'red', 'named arg red has correct name');
115 is( $red->rgb_hex, '#ff0000', 'named arg red has correct hex value');
116 is(($red->rgb)[0], 255, 'named arg red has correct rgb red component value');
117 is(($red->rgb)[1], 0, 'named arg red has correct rgb green component value');
118 is(($red->rgb)[2], 0, 'named arg red has correct rgb blue component value');
119 is(($red->hsl)[0], 0, 'named arg red has correct hsl hue component value');
120 is(($red->hsl)[1], 100, 'named arg red has correct hsl saturation component value');
121 is(($red->hsl)[2], 50, 'named arg red has correct hsl lightness component value');
122
123 $red = Chart::Color->new({Red => 255, Green => 0, Blue => 0 });
124 is( ref $red, $module, 'could create object by RGB hash ref');
125 is( $red->red, 255, 'hash ref red has correct red component value');
126 is( $red->green, 0, 'hash ref red has correct green component value');
127 is( $red->blue, 0, 'hash ref red has correct blue component value');
128 is( $red->hue, 0, 'hash ref red has correct hue component value');
129 is( $red->saturation, 100, 'hash ref red has correct saturation component value');
130 is( $red->lightness, 50, 'hash ref red has correct lightness component value');
131 is( $red->name, 'red', 'hash ref red has correct name');
132 is( $red->rgb_hex, '#ff0000', 'hash ref red has correct hex value');
133 is(($red->rgb)[0], 255, 'hash ref red has correct rgb red component value');
134 is(($red->rgb)[1], 0, 'hash ref red has correct rgb green component value');
135 is(($red->rgb)[2], 0, 'hash ref red has correct rgb blue component value');
136 is(($red->hsl)[0], 0, 'hash ref red has correct hsl hue component value');
137 is(($red->hsl)[1], 100, 'hash ref red has correct hsl saturation component value');
138 is(($red->hsl)[2], 50, 'hash ref red has correct hsl lightness component value');
139
140 $red = Chart::Color->new({h => 0, s => 100, l => 50 });
141 is( ref $red, $module, 'could create object by HSL hash ref');
142 is( $red->red, 255, 'hash ref red has correct red component value');
143 is( $red->green, 0, 'hash ref red has correct green component value');
144 is( $red->blue, 0, 'hash ref red has correct blue component value');
145 is( $red->hue, 0, 'hash ref red has correct hue component value');
146 is( $red->saturation, 100, 'hash ref red has correct saturation component value');
147 is( $red->lightness, 50, 'hash ref red has correct lightness component value');
148 is( $red->name, 'red', 'hash ref red has correct name');
149 is( $red->rgb_hex, '#ff0000', 'hash ref red has correct hex value');
150 is(($red->rgb)[0], 255, 'hash ref red has correct rgb red component value');
151 is(($red->rgb)[1], 0, 'hash ref red has correct rgb green component value');
152 is(($red->rgb)[2], 0, 'hash ref red has correct rgb blue component value');
153 is(($red->hsl)[0], 0, 'hash ref red has correct hsl hue component value');
154 is(($red->hsl)[1], 100, 'hash ref red has correct hsl saturation component value');
155 is(($red->hsl)[2], 50, 'hash ref red has correct hsl lightness component value');
156
157 $red = Chart::Color->new( Hue => 0, Sat => 100, Light => 50 );
158 is( ref $red, $module, 'could create object by HSL named args');
159 is( $red->red, 255, 'hash ref red has correct red component value');
160 is( $red->green, 0, 'hash ref red has correct green component value');
161 is( $red->blue, 0, 'hash ref red has correct blue component value');
162 is( $red->hue, 0, 'hash ref red has correct hue component value');
163 is( $red->saturation, 100, 'hash ref red has correct saturation component value');
164 is( $red->lightness, 50, 'hash ref red has correct lightness component value');
165 is( $red->name, 'red', 'hash ref red has correct name');
166 is( $red->rgb_hex, '#ff0000', 'hash ref red has correct hex value');
167 is(($red->rgb)[0], 255, 'hash ref red has correct rgb red component value');
168 is(($red->rgb)[1], 0, 'hash ref red has correct rgb green component value');
169 is(($red->rgb)[2], 0, 'hash ref red has correct rgb blue component value');
170 is(($red->hsl)[0], 0, 'hash ref red has correct hsl hue component value');
171 is(($red->hsl)[1], 100, 'hash ref red has correct hsl saturation component value');
172 is(($red->hsl)[2], 50, 'hash ref red has correct hsl lightness component value');
173
174
175 my $c = Chart::Color->new( 1,2,3 );
176 is( ref $red, $module, 'could create object by random unnamed color');
177 is( $c->red, 1, 'random color has correct red component value');
178 is( $c->green, 2, 'random color has correct green component value');
179 is( $c->blue, 3, 'random color has correct blue component value');
180 is( $c->name, '', 'random color has no name');
181
182 my $blue = Chart::Color->new( 'blue' );
183 is( $blue->red, 0, 'blue has correct red component value');
184 is( $blue->green, 0, 'blue has correct green component value');
185 is( $blue->blue, 255, 'blue has correct blue component value');
186 is( $blue->hue, 240, 'blue has correct hue component value');
187 is( $blue->saturation,100,'blue has correct saturation component value');
188 is( $blue->lightness, 50,'blue has correct lightness component value');
189 is( $blue->name, 'blue', 'blue color has correct name');
190
191 is( $blue->distance_to($red), 120, 'correct default hsl distance between red and blue');
192 is( $blue->distance_to($red, 'HSL'), 120, 'correct hsl distance between red and blue');
193 is( $blue->distance_to($red, 'Hue'), 120, 'correct hue distance between red and blue, long name');
194 is( $blue->distance_to($red, 'h'), 120, 'correct hue distance between red and blue');
195 is( $blue->distance_to($red, 's'), 0, 'correct sturation distance between red and blue');
196 is( $blue->distance_to($red, 'Sat'), 0, 'correct sturation distance between red and blue, long name');
197 is( $blue->distance_to($red, 'l'), 0, 'correct lightness distance between red and blue');
198 is( $blue->distance_to($red, 'Light'), 0, 'correct lightness distance between red and blue, long name');
199 is( $blue->distance_to($red, 'hs'), 120, 'correct hs distance between red and blue');
200 is( $blue->distance_to($red, 'hl'), 120, 'correct hl distance between red and blue');
201 is( $blue->distance_to($red, 'sl'), 0, 'correct sl distance between red and blue');
202 is( int $blue->distance_to($red, 'rgb'), 360, 'correct rgb distance between red and blue');
203 is( $blue->distance_to($red, 'Red'), 255, 'correct red distance between red and blue, long name');
204 is( $blue->distance_to($red, 'r'), 255, 'correct red distance between red and blue');
205 is( $blue->distance_to($red, 'Green'), 0, 'correct green distance between red and blue, long name');
206 is( $blue->distance_to($red, 'g'), 0, 'correct green distance between red and blue');
207 is( $blue->distance_to($red, 'Blue'), 255, 'correct blue distance between red and blue, long name');
208 is( $blue->distance_to($red, 'b'), 255, 'correct blue distance between red and blue');
209 is( $blue->distance_to($red, 'rg'), 255, 'correct rg distance between red and blue');
210 is( int $blue->distance_to($red, 'rb'), 360, 'correct rb distance between red and blue');
211 is( $blue->distance_to($red, 'gb'), 255, 'correct gb distance between red and blue');
212
213 is( int $blue->distance_to([10, 10, 245], ), 8, 'correct default hsl distance between own rgb blue and blue');
214 is( int $blue->distance_to([10, 10, 245], 'HSL'), 8, 'correct hsl distance between own rgb blue and blue');
215 is( $blue->distance_to([10, 10, 245], 'Hue'), 0, 'correct hue distance between own rgb blue and blue, long name');
216 is( $blue->distance_to([10, 10, 245], 'h'), 0, 'correct hue distance between own rgb blue and blue');
217 is( int $blue->distance_to([10, 10, 245], 's'), 8, 'correct sturation distance between own rgb blue and blue');
218 is( int $blue->distance_to([10, 10, 245], 'Sat'), 8, 'correct sturation distance between own rgb blue and blue, long name');
219 is( int $blue->distance_to([10, 10, 245], 'l'), 0, 'correct lightness distance between own rgb blue and blue');
220 is( int $blue->distance_to([10, 10, 245], 'Light'), 0, 'correct lightness distance between own rgb blue and blue, long name');
221 is( int $blue->distance_to([10, 10, 245], 'hs'), 8, 'correct hs distance between own rgb blue and blue');
222 is( int $blue->distance_to([10, 10, 245], 'hl'), 0, 'correct hl distance between own rgb blue and blue');
223 is( int $blue->distance_to([10, 10, 245], 'sl'), 8, 'correct sl distance between own rgb blue and blue');
224 is( int $blue->distance_to([10, 10, 245], 'rgb'), 17, 'correct rgb distance between own rgb blue and blue');
225 is( $blue->distance_to([10, 10, 245], 'Red'), 10, 'correct red distance between own rgb blue and blue, long name');
226 is( $blue->distance_to([10, 10, 245], 'r'), 10, 'correct red distance between own rgb blue and blue');
227 is( $blue->distance_to([10, 10, 245], 'Green'),10, 'correct green distance between own rgb blue and blue, long name');
228 is( $blue->distance_to([10, 10, 245], 'g'), 10, 'correct green distance between own rgb blue and blue');
229 is( $blue->distance_to([10, 10, 245], 'Blue'), 10, 'correct blue distance between own rgb blue and blue, long name');
230 is( $blue->distance_to([10, 10, 245], 'b'), 10, 'correct blue distance between own rgb blue and blue');
231 is( int $blue->distance_to([10, 10, 245], 'rg'), 14, 'correct rg distance between own rgb blue and blue');
232 is( int $blue->distance_to([10, 10, 245], 'rb'), 14, 'correct rb distance between own rgb blue and blue');
233 is( int $blue->distance_to([10, 10, 245], 'gb'), 14, 'correct gb distance between own rgb blue and blue');
234
235 is( int $blue->distance_to({h =>230, s => 90, l=>40}), 17, 'correct default hsl distance between own hsl blue and blue');
236 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'HSL'), 17, 'correct hsl distance between own hsl blue and blue');
237 is( $blue->distance_to({h =>230, s => 90, l=>40}, 'Hue'), 10, 'correct hue distance between own hsl blue and blue, long name');
238 is( $blue->distance_to({h =>230, s => 90, l=>40}, 'h'), 10, 'correct hue distance between own hsl blue and blue');
239 is( $blue->distance_to({h =>230, s => 90, l=>40}, 's'), 10, 'correct sturation distance between own hsl blue and blue');
240 is( $blue->distance_to({h =>230, s => 90, l=>40}, 'Sat'), 10, 'correct sturation distance between own hsl blue and blue, long name');
241 is( $blue->distance_to({h =>230, s => 90, l=>40}, 'l'), 10, 'correct lightness distance between own hsl blue and blue');
242 is( $blue->distance_to({h =>230, s => 90, l=>40}, 'Light'),10, 'correct lightness distance between own hsl blue and blue, long name');
243 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'hs'), 14, 'correct hs distance between own hsl blue and blue');
244 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'hl'), 14, 'correct hl distance between own hsl blue and blue');
245 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'sl'), 14, 'correct sl distance between own hsl blue and blue');
246 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'rgb'), 74, 'correct rgb distance between own hsl blue and blue');
247 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'Red'), 10, 'correct red distance between own hsl blue and blue, long name');
248 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'r'), 10, 'correct red distance between own hsl blue and blue');
249 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'Green'),41, 'correct green distance between own hsl blue and blue, long name');
250 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'g'), 41, 'correct green distance between own hsl blue and blue');
251 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'Blue'), 61, 'correct blue distance between own hsl blue and blue, long name');
252 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'b'), 61, 'correct blue distance between own hsl blue and blue');
253 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'rg'), 42, 'correct rg distance between own hsl blue and blue');
254 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'rb'), 61, 'correct rb distance between own hsl blue and blue');
255 is( int $blue->distance_to({h =>230, s => 90, l=>40}, 'gb'), 73, 'correct gb distance between own hsl blue and blue');
256
257 $red = Chart::Color->new('#FF0000');
258 warning_like {$red->add()} {carped => qr/argument options/}, "need argument to add to color object";
259 warning_like {$red->add('weirdcolorname')} {carped => qr/unknown color/}, "accept only known color names";
260 warning_like {$red->add('#23232') } {carped => qr/hex color definition/}, "hex definition too short";
261 warning_like {$red->add('#232321f') } {carped => qr/hex color definition/}, "hex definition too long";
262 warning_like {$red->add(1,1)} {carped => qr/argument options/}, "too few positional args";
263 warning_like {$red->add(1,1,1,1)} {carped => qr/wrong number/}, "too many positional args";
264 warning_like {$red->add([1,1])} {carped => qr/ 3 numerical values/}, "too few positional args in ref";
265 warning_like {$red->add([1,1,1,1])} {carped => qr/ 3 numerical values/}, "too many positional args in ref";
266 warning_like {$red->add(r=>1,g=>1,t=>1)} {carped => qr/unknown hash key/}, "don't invent named args";
267 warning_like {$red->add({r=>1,g=>1,t=>1})} {carped => qr/unknown hash key/}, "don't invent named args, in ref";
268
269 my $white = Chart::Color->new('white');
270 my $black = Chart::Color->new('black');
271
272 is( $white->add( 255, 255, 255 )->name, 'white', "it can't get whiter than white with additive color adding");
273 is( $white->add( {Hue => 10} )->name, 'white', "hue doesnt change when were on level white");
274 is( $white->add( {Red => 10} )->name, 'white', "hue doesnt change when adding red on white");
275 is( $white->add( $white )->name, 'white', "adding white on white is still white");
276 is( $red->add( $black )->name, 'red', "red + black = red");
277 is( $red->add( $black, -1 )->name, 'red', "red - black = red");
278 is( $white->add( $red, -1 )->name, 'aqua', "white - red = aqua");
279 is( $white->add( $white, -0.5 )->name, 'gray', "white - 0.5 white = grey");
280 is( Chart::Color->new(1,2,3)->add( 2,1,0)->name, 'gray1', "adding positional args"); # = 3, 3, 3
281 is( $red->add( {Saturation => -10} )->red, 242, "paling red 10%, red value");
282 is( $red->add( {Saturation => -10} )->blue, 13, "paling red 10%, blue value");
283 is( $white->add( {Lightness => -12} )->name, 'gray88', "dimming white 12%");
284 is( $black->add( {Red => 255} )->name, 'red', "creating pure red from black");
285 is( $black->add( {Green => 255} )->name, 'lime', "creating pure green from black");
286 is( $black->add( { b => 255} )->name, 'blue', "creating pure blue from black with short name");
287
288
289 warning_like {$red->blend_with()} {carped => qr/color object/}, "need argument to blend to color object";
290 warning_like {$red->blend_with('weirdcolorname')} {carped => qr/unknown color/}, "accept only known color names";
291 warning_like {$red->blend_with('#23232') } {carped => qr/hex color definition/}, "hex definition too short";
292 warning_like {$red->blend_with('#232321f') } {carped => qr/hex color definition/}, "hex definition too long";
293 warning_like {$red->blend_with([1,1])} {carped => qr/need exactly 3/}, "too few positional args in ref";
294 warning_like {$red->blend_with([1,1,1,1])} {carped => qr/need exactly 3/}, "too many positional args in ref";
295 warning_like {$red->blend_with({r=>1,g=>1,t=>1})} {carped => qr/argument keys/}, "don't mix named args, in hash ref color def";
296 warning_like {$red->blend_with({r=>1,g=>1,l=>1})} {carped => qr/argument keys/}, "don't invent named args, in hash ref color def";
297
298 is( $black->blend_with( $white )->name, 'gray', "blend black + white = gray");
299 is( $black->blend_with( $white, 0 )->name, 'black', "blend nothing, keep color");
300 is( $black->blend_with( $white, 1 )->name, 'white', "blend nothing, take c2");
301 is( $black->blend_with( $white, 2 )->name, 'white', "RGB limits kept");
302 is( $red->blend_with( 'blue')->name, 'fuchsia', "blending with name");
303 is( $red->blend_with( '#0000ff')->name, 'fuchsia', "blending with hex def");
304 is( $red->blend_with( [0,0,255])->name, 'fuchsia', "blending with array ref color def");
305 is( $red->blend_with({R=> 0, G=> 0, B=>255})->name, 'fuchsia', "blending with RGB hash ref color def");
306 is( $red->blend_with({H=> 240, S=> 100, L=>50})->name,'fuchsia', "blending with HSL hash ref color def");
307
308 is( $black->gradient_to( $white, 1 )->name, 'black', 'shortest gradient is $self');
309 my @g = $black->gradient_to( $white, 2 );
310 is( int @g, 2, 'gradient with length 2 has only boundary cases');
311 is( $g[0]->name, 'black', 'gradient with length 2 starts on left boundary');
312 is( $g[1]->name, 'white', 'gradient with length 2 ends on right boundary');
313 @g = $black->gradient_to( $white, 6 );
314 is( int @g, 6, 'gradient has right length = 6');
315 is( $g[1]->name, 'gray20', 'grey20 is between black and white');
316 is( $g[2]->name, 'gray40', 'grey40 is between black and white');
317 @g = $black->gradient_to( $white, 3, 2 );
318 is( int @g, 3, 'gradient has right length = 3');
319 is( $g[1]->name, 'gray25', 'grey25 is between black and white in none linear gradient');
320 @g = $black->gradient_to( $white, 3, .41 );
321 is( $g[1]->name, 'gray75', 'grey75 is between black and white in none linear gradient');
322 @g = $red->gradient_to( '#0000FF', 3 );
323 is( $g[1]->name, 'fuchsia', 'fuchsia is between red and blue in linear gradient');
324
325 @g = $black->complementary();
326 is( int @g, 1, "default is one complementary color");
327 is( $black->complementary()->name, 'black', "black has no complementary color");
328 is( $white->complementary()->name, 'white', "white has no complementary color");
329 is( $red->complementary()->name, 'aqua', "aqua is complementary to red");
330
331 @g = $red->complementary(3);
332 is( int @g, 3, "requested amount of complementary colors");
333 is( $g[0]->saturation, $g[1]->saturation, "saturation is equal on complementary circle");
334 is( $g[1]->saturation, $g[2]->saturation, "saturation is equal on complementary circle 2");
335 is( $g[0]->lightness, $g[1]->lightness, "lightness is equal on complementary circle");
336 is( $g[1]->lightness, $g[2]->lightness, "lightness is equal on complementary circle 2");
337 is( $g[0]->name, 'red', "complementary circle starts with C1");
338 is( $g[1]->name, 'lime', "complementary gos on to green");
339 is( $g[2]->name, 'blue', "complementary circle ends with blue");
340
341 @g = Chart::Color->new(15,12,13)->complementary(3);
342 is( $g[0]->saturation, $g[1]->saturation, "saturation is equal on complementary circle of random color");
343 is( $g[1]->saturation, $g[2]->saturation, "saturation is equal on complementary circle 2");
344 is( $g[0]->lightness, $g[1]->lightness, "lightness is equal on complementary circle of random color");
345 is( $g[1]->lightness, $g[2]->lightness, "lightness is equal on complementary circle 2");
346
347 @g = Chart::Color->new(15,12,13)->complementary(4, 12, 20);
348 is( int @g, 4, "requested amount of complementary colors");
349 is( $g[1]->saturation, $g[3]->saturation, "saturation is equal on opposing sides of skewed circle");
350 is( $g[1]->lightness, $g[3]->lightness, "lightness is equal on opposing sides of skewed circle");
351 is( $g[1]->saturation-6, $g[0]->saturation, "saturation moves on skewed circle as predicted fore ");
352 is( $g[1]->saturation+6, $g[2]->saturation, "saturation moves on skewed circle as predicted back");
353 is( $g[1]->lightness-10, $g[0]->lightness, "lightness moves on skewed circle as predicted fore");
354 is( $g[1]->lightness+10, $g[2]->lightness, "lightness moves on skewed circle as predicted back");
355
356 @g = Chart::Color->new(15,12,13)->complementary(4, 512, 520);
357 is( abs($g[0]->saturation-$g[2]->saturation) < 100, 1, "cut too large saturnation skews");
358 is( abs($g[0]->lightness-$g[2]->lightness) < 100, 1, "cut too large lightness skews");
359
360 @g = Chart::Color->new(15,12,13)->complementary(5, 10, 20);
361 is( $g[1]->saturation, $g[4]->saturation, "saturation is equal on opposing sides of odd and skewed circle 1");
362 is( $g[2]->saturation, $g[3]->saturation, "saturation is equal on opposing sides of odd and skewed circle 2");
363 is( $g[1]->lightness, $g[4]->lightness, "lightness is equal on opposing sides of odd and skewed circle 1");
364 is( $g[2]->lightness, $g[3]->lightness, "lightness is equal on opposing sides of odd and skewed circle 2");
365 is( $g[1]->saturation-4, $g[0]->saturation, "saturation moves on odd and skewed circle as predicted fore ");
366 is( $g[1]->saturation+4, $g[2]->saturation, "saturation moves on odd and skewed circle as predicted back");
367 is( $g[1]->lightness -8, $g[0]->lightness, "lightness moves on odd and skewed circle as predicted fore");
368 is( $g[1]->lightness +8, $g[2]->lightness, "lightness moves on odd and skewed circle as predicted back");
369
370
371 exit 0;