abachrome 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +10 -0
  3. data/CHANGELOG.md +5 -0
  4. data/README.md +99 -0
  5. data/demos/ncurses/plasma.rb +124 -0
  6. data/devenv.lock +100 -0
  7. data/devenv.nix +51 -0
  8. data/devenv.yaml +15 -0
  9. data/lib/abachrome/abc_decimal.rb +161 -0
  10. data/lib/abachrome/color.rb +74 -0
  11. data/lib/abachrome/color_mixins/blend.rb +45 -0
  12. data/lib/abachrome/color_mixins/lighten.rb +39 -0
  13. data/lib/abachrome/color_mixins/to_colorspace.rb +38 -0
  14. data/lib/abachrome/color_mixins/to_lrgb.rb +49 -0
  15. data/lib/abachrome/color_mixins/to_oklab.rb +48 -0
  16. data/lib/abachrome/color_mixins/to_oklch.rb +48 -0
  17. data/lib/abachrome/color_mixins/to_srgb.rb +63 -0
  18. data/lib/abachrome/color_models/hsv.rb +22 -0
  19. data/lib/abachrome/color_models/oklab.rb +16 -0
  20. data/lib/abachrome/color_models/oklch.rb +47 -0
  21. data/lib/abachrome/color_models/rgb.rb +28 -0
  22. data/lib/abachrome/color_space.rb +97 -0
  23. data/lib/abachrome/converter.rb +59 -0
  24. data/lib/abachrome/converters/base.rb +57 -0
  25. data/lib/abachrome/converters/lrgb_to_oklab.rb +27 -0
  26. data/lib/abachrome/converters/lrgb_to_srgb.rb +30 -0
  27. data/lib/abachrome/converters/oklab_to_lrgb.rb +42 -0
  28. data/lib/abachrome/converters/oklab_to_oklch.rb +23 -0
  29. data/lib/abachrome/converters/oklab_to_srgb.rb +17 -0
  30. data/lib/abachrome/converters/oklch_to_lrgb.rb +15 -0
  31. data/lib/abachrome/converters/oklch_to_oklab.rb +23 -0
  32. data/lib/abachrome/converters/oklch_to_srgb.rb +18 -0
  33. data/lib/abachrome/converters/srgb_to_lrgb.rb +27 -0
  34. data/lib/abachrome/converters/srgb_to_oklab.rb +15 -0
  35. data/lib/abachrome/converters/srgb_to_oklch.rb +18 -0
  36. data/lib/abachrome/gamut/base.rb +72 -0
  37. data/lib/abachrome/gamut/p3.rb +25 -0
  38. data/lib/abachrome/gamut/rec2020.rb +23 -0
  39. data/lib/abachrome/gamut/srgb.rb +27 -0
  40. data/lib/abachrome/illuminants/base.rb +33 -0
  41. data/lib/abachrome/illuminants/d50.rb +31 -0
  42. data/lib/abachrome/illuminants/d55.rb +27 -0
  43. data/lib/abachrome/illuminants/d65.rb +35 -0
  44. data/lib/abachrome/illuminants/d75.rb +27 -0
  45. data/lib/abachrome/named/css.rb +164 -0
  46. data/lib/abachrome/outputs/css.rb +117 -0
  47. data/lib/abachrome/palette.rb +131 -0
  48. data/lib/abachrome/palette_mixins/interpolate.rb +31 -0
  49. data/lib/abachrome/palette_mixins/resample.rb +59 -0
  50. data/lib/abachrome/palette_mixins/stretch_luminance.rb +70 -0
  51. data/lib/abachrome/parsers/hex.rb +50 -0
  52. data/lib/abachrome/to_abcd.rb +13 -0
  53. data/lib/abachrome/version.rb +5 -0
  54. data/lib/abachrome.rb +99 -0
  55. metadata +172 -0
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Converters
5
+ class OklabToLrgb < Abachrome::Converters::Base
6
+ def self.convert(oklab_color)
7
+ raise_unless oklab_color, :oklab
8
+
9
+ l, a, b = oklab_color.coordinates.map { |_| AbcDecimal(_) }
10
+
11
+ l_ = AbcDecimal((l * AD("0.99999999845051981432")) +
12
+ (AD("0.39633779217376785678") * a) +
13
+ (AD("0.21580375806075880339") * b))
14
+
15
+ m_ = AbcDecimal((l * AD("1.0000000088817607767")) -
16
+ (a * AD("0.1055613423236563494")) -
17
+ (b * AD("0.063854174771705903402")))
18
+ s_ = AbcDecimal((l * AD("1.000000054672410917")) -
19
+ (a * AD("0.089484182094965759684")) -
20
+ (b * AD("1.2914855378640917399")))
21
+
22
+ l = AbcDecimal(l_)**3
23
+ m = AbcDecimal(m_)**3
24
+ s = AbcDecimal(s_)**3
25
+
26
+ r = (l * AD("4.07674166134799")) -
27
+ (m * AD("3.307711590408193")) +
28
+ (s * AD("0.230969928729428"))
29
+ g = (l * AD("-1.2684380040921763")) +
30
+ (m * AD("2.6097574006633715")) -
31
+ (s * AD("0.3413193963102197"))
32
+ b = (l * AD("-0.004196086541837188")) -
33
+ (m * AD("0.7034186144594493")) +
34
+ (s * AD("1.7076147009309444"))
35
+
36
+ output_coords = [r, g, b].map { |it| [it, 0].max }
37
+
38
+ Color.new(ColorSpace.find(:lrgb), output_coords, oklab_color.alpha)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Converters
5
+ class OklabToOklch < Abachrome::Converters::Base
6
+ def self.convert(oklab_color)
7
+ raise_unless oklab_color, :oklab
8
+
9
+ l, a, b = oklab_color.coordinates.map { |_| AbcDecimal(_) }
10
+
11
+ c = ((a * a) + (b * b)).sqrt
12
+ h = (AbcDecimal.atan2(b, a) * 180) / Math::PI
13
+ h += 360 if h.negative?
14
+
15
+ Color.new(
16
+ ColorSpace.find(:oklch),
17
+ [l, c, h],
18
+ oklab_color.alpha
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Converters
5
+ class OklabToSrgb < Abachrome::Converters::Base
6
+ def self.convert(oklab_color)
7
+ raise_unless oklab_color, :oklab
8
+
9
+ # First convert Oklab to linear RGB
10
+ lrgb_color = OklabToLrgb.convert(oklab_color)
11
+
12
+ # Then use the LrgbToSrgb converter to go from linear RGB to sRGB
13
+ LrgbToSrgb.convert(lrgb_color)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "oklab_to_lrgb"
4
+ require_relative "oklch_to_oklab"
5
+
6
+ module Abachrome
7
+ module Converters
8
+ class OklchToLrgb < Abachrome::Converters::Base
9
+ def self.convert(oklch_color)
10
+ oklab_color = OklchToOklab.convert(oklch_color)
11
+ OklabToLrgb.convert(oklab_color)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Converters
5
+ class OklchToOklab < Abachrome::Converters::Base
6
+ def self.convert(oklch_color)
7
+ raise_unless oklch_color, :oklch
8
+
9
+ l, c, h = oklch_color.coordinates.map { |_| AbcDecimal(_) }
10
+
11
+ h_rad = h * Rational(Math::PI, 180)
12
+ a = c * Math.cos(h_rad.value)
13
+ b = c * Math.sin(h_rad.value)
14
+
15
+ Color.new(
16
+ ColorSpace.find(:oklab),
17
+ [l, a, b],
18
+ oklch_color.alpha
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "oklch_to_oklab"
4
+ require_relative "oklab_to_srgb"
5
+
6
+ module Abachrome
7
+ module Converters
8
+ class OklchToSrgb < Abachrome::Converters::Base
9
+ def self.convert(oklch_color)
10
+ # Convert OKLCh to OKLab first
11
+ oklab_color = OklchToOklab.convert(oklch_color)
12
+
13
+ # Then convert OKLab to sRGB
14
+ OklabToSrgb.convert(oklab_color)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Converters
5
+ class SrgbToLrgb
6
+ def self.convert(srgb_color)
7
+ r, g, b = srgb_color.coordinates.map { |c| to_linear(AbcDecimal(c)) }
8
+
9
+ Color.new(
10
+ ColorSpace.find(:lrgb),
11
+ [r, g, b],
12
+ srgb_color.alpha
13
+ )
14
+ end
15
+
16
+ def self.to_linear(v)
17
+ v_abs = v.abs
18
+ v_sign = v.negative? ? -1 : 1
19
+ if v_abs <= AD("0.04045")
20
+ v / AD("12.92")
21
+ else
22
+ v_sign * (((v_abs + AD("0.055")) / AD("1.055"))**AD("2.4"))
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Converters
5
+ class SrgbToOklab
6
+ def self.convert(srgb_color)
7
+ # First convert sRGB to linear RGB
8
+ lrgb_color = SrgbToLrgb.convert(srgb_color)
9
+
10
+ # Then convert linear RGB to Oklab
11
+ LrgbToOklab.convert(lrgb_color)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "srgb_to_oklab"
4
+ require_relative "oklab_to_oklch"
5
+
6
+ module Abachrome
7
+ module Converters
8
+ class SrgbToOklch < Abachrome::Converters::Base
9
+ def self.convert(srgb_color)
10
+ # First convert sRGB to OKLab
11
+ oklab_color = SrgbToOklab.convert(srgb_color)
12
+
13
+ # Then convert OKLab to OKLCh
14
+ OklabToOklch.convert(oklab_color)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Gamut
5
+ def self.register(gamut)
6
+ @gamuts ||= {}
7
+ @gamuts[gamut.name] = gamut
8
+ end
9
+
10
+ def self.find(name)
11
+ @gamuts ||= {}
12
+ @gamuts[name.to_sym] or raise ArgumentError, "Unknown gamut: #{name}"
13
+ end
14
+
15
+ def self.gamuts
16
+ @gamuts ||= {}
17
+ end
18
+
19
+ class Base
20
+ attr_reader :name, :primaries, :white_point
21
+
22
+ def initialize(name, primaries, white_point)
23
+ @name = name
24
+ @primaries = primaries
25
+ @white_point = white_point
26
+ end
27
+
28
+ # TODO: - make this work properly
29
+ def contains?(coordinates)
30
+ x, y, z = coordinates
31
+ x >= 0 && x <= 1 &&
32
+ y >= 0 && y <= 1 &&
33
+ z >= 0 && z <= 1
34
+ end
35
+
36
+ def map(coordinates, method: :clip)
37
+ case method
38
+ when :clip
39
+ clip(coordinates)
40
+ when :scale
41
+ scale(coordinates)
42
+ else
43
+ raise ArgumentError, "Unknown mapping method: #{method}"
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def clip(coordinates)
50
+ coordinates.map do |c|
51
+ c.clamp(0, 1)
52
+ end
53
+ end
54
+
55
+ def scale(coordinates, channels = nil, min = nil, max = nil)
56
+ min ||= coordinates.min
57
+ max ||= coordinates.max
58
+ channels ||= (0..(coordinates.length - 1)).to_a
59
+
60
+ scale_factor = if max > 1
61
+ 1.0 / max
62
+ elsif min.negative?
63
+ 1.0 / (1 - min)
64
+ else
65
+ 1.0
66
+ end
67
+
68
+ coordinates.each_with_index.map { |c, channel_index| channels.include?(channel_index) ? c * scale_factor : c }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Gamut
5
+ class P3 < Base
6
+ def initialize
7
+ primaries = {
8
+ red: [0.680, 0.320],
9
+ green: [0.265, 0.690],
10
+ blue: [0.150, 0.060]
11
+ }
12
+ super(:p3, primaries, :D65)
13
+ end
14
+
15
+ def contains?(coordinates)
16
+ r, g, b = coordinates
17
+ r >= 0 && r <= 1 &&
18
+ g >= 0 && g <= 1 &&
19
+ b >= 0 && b <= 1
20
+ end
21
+ end
22
+
23
+ register(P3.new)
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib
4
+ abachrome
5
+ gamut
6
+ rec2020.rb
7
+
8
+ module Abachrome
9
+ module Gamut
10
+ class Rec2020 < Base
11
+ def initialize
12
+ primaries = {
13
+ red: [0.708, 0.292],
14
+ green: [0.170, 0.797],
15
+ blue: [0.131, 0.046]
16
+ }
17
+ super(:rec2020, primaries, :D65)
18
+ end
19
+ end
20
+
21
+ register(Rec2020.new)
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Abachrome
6
+ module Gamut
7
+ class SRGB < Base
8
+ def initialize
9
+ primaries = {
10
+ red: [0.6400, 0.3300],
11
+ green: [0.3000, 0.6000],
12
+ blue: [0.1500, 0.0600]
13
+ }
14
+ super(:srgb, primaries, :D65)
15
+ end
16
+
17
+ def contains?(coordinates)
18
+ r, g, b = coordinates
19
+ r >= 0 && r <= 1 &&
20
+ g >= 0 && g <= 1 &&
21
+ b >= 0 && b <= 1
22
+ end
23
+ end
24
+
25
+ register(SRGB.new)
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Illuminants
5
+ class Base
6
+ class << self
7
+ def whitepoint
8
+ raise NotImplementedError, "#{name}#whitepoint must be implemented"
9
+ end
10
+
11
+ def x
12
+ whitepoint[0]
13
+ end
14
+
15
+ def y
16
+ whitepoint[1]
17
+ end
18
+
19
+ def z
20
+ whitepoint[2]
21
+ end
22
+
23
+ def xyz
24
+ whitepoint
25
+ end
26
+
27
+ def to_s
28
+ name.split("::").last
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Illuminants
5
+ class D50 < Base
6
+ def self.x
7
+ 0.96422
8
+ end
9
+
10
+ def self.y
11
+ 1.0
12
+ end
13
+
14
+ def self.z
15
+ 0.82521
16
+ end
17
+
18
+ def self.temperature
19
+ 5003
20
+ end
21
+
22
+ def self.name
23
+ "D50"
24
+ end
25
+
26
+ def self.description
27
+ "CIE Standard Illuminant D50 (Horizon Light)"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Illuminants
5
+ class D55 < Base
6
+ def x
7
+ 0.33243
8
+ end
9
+
10
+ def y
11
+ 0.34744
12
+ end
13
+
14
+ def z
15
+ 0.32013
16
+ end
17
+
18
+ def temperature
19
+ 5503
20
+ end
21
+
22
+ def description
23
+ "D55 (mid-morning/mid-afternoon daylight)"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Illuminants
5
+ class D65 < Base
6
+ def self.x
7
+ 95.047
8
+ end
9
+
10
+ def self.y
11
+ 100.000
12
+ end
13
+
14
+ def self.z
15
+ 108.883
16
+ end
17
+
18
+ def self.white_point
19
+ [x, y, z]
20
+ end
21
+
22
+ def self.temperature
23
+ 6504
24
+ end
25
+
26
+ def self.name
27
+ "D65"
28
+ end
29
+
30
+ def self.description
31
+ "CIE Standard Illuminant D65 - represents average daylight with a correlated color temperature of 6504K"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Abachrome
4
+ module Illuminants
5
+ class D75 < Base
6
+ def x
7
+ 0.299
8
+ end
9
+
10
+ def y
11
+ 0.315
12
+ end
13
+
14
+ def z
15
+ 0.386
16
+ end
17
+
18
+ def temperature
19
+ 7500
20
+ end
21
+
22
+ def description
23
+ "North sky daylight / CIE standard illuminant D75"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Colors courtesy of w3c:
4
+ # https://www.w3.org/wiki/CSS/Properties/color/keywords
5
+ #
6
+ module Abachrome
7
+ module Named
8
+ module CSS
9
+ ColorNames = {
10
+ "aliceblue" => [240, 248, 255],
11
+ "antiquewhite" => [250, 235, 215],
12
+ "aqua" => [0, 255, 255],
13
+ "aquamarine" => [127, 255, 212],
14
+ "azure" => [240, 255, 255],
15
+ "beige" => [245, 245, 220],
16
+ "bisque" => [255, 228, 196],
17
+ "black" => [0, 0, 0],
18
+ "blanchedalmond" => [255, 235, 205],
19
+ "blue" => [0, 0, 255],
20
+ "blueviolet" => [138, 43, 226],
21
+ "brown" => [165, 42, 42],
22
+ "burlywood" => [222, 184, 135],
23
+ "cadetblue" => [95, 158, 160],
24
+ "chartreuse" => [127, 255, 0],
25
+ "chocolate" => [210, 105, 30],
26
+ "coral" => [255, 127, 80],
27
+ "cornflowerblue" => [100, 149, 237],
28
+ "cornsilk" => [255, 248, 220],
29
+ "crimson" => [220, 20, 60],
30
+ "cyan" => [0, 255, 255],
31
+ "darkblue" => [0, 0, 139],
32
+ "darkcyan" => [0, 139, 139],
33
+ "darkgoldenrod" => [184, 134, 11],
34
+ "darkgray" => [169, 169, 169],
35
+ "darkgreen" => [0, 100, 0],
36
+ "darkgrey" => [169, 169, 169],
37
+ "darkkhaki" => [189, 183, 107],
38
+ "darkmagenta" => [139, 0, 139],
39
+ "darkolivegreen" => [85, 107, 47],
40
+ "darkorange" => [255, 140, 0],
41
+ "darkorchid" => [153, 50, 204],
42
+ "darkred" => [139, 0, 0],
43
+ "darksalmon" => [233, 150, 122],
44
+ "darkseagreen" => [143, 188, 143],
45
+ "darkslateblue" => [72, 61, 139],
46
+ "darkslategray" => [47, 79, 79],
47
+ "darkslategrey" => [47, 79, 79],
48
+ "darkturquoise" => [0, 206, 209],
49
+ "darkviolet" => [148, 0, 211],
50
+ "deeppink" => [255, 20, 147],
51
+ "deepskyblue" => [0, 191, 255],
52
+ "dimgray" => [105, 105, 105],
53
+ "dimgrey" => [105, 105, 105],
54
+ "dodgerblue" => [30, 144, 255],
55
+ "firebrick" => [178, 34, 34],
56
+ "floralwhite" => [255, 250, 240],
57
+ "forestgreen" => [34, 139, 34],
58
+ "fuchsia" => [255, 0, 255],
59
+ "gainsboro" => [220, 220, 220],
60
+ "ghostwhite" => [248, 248, 255],
61
+ "gold" => [255, 215, 0],
62
+ "goldenrod" => [218, 165, 32],
63
+ "gray" => [128, 128, 128],
64
+ "green" => [0, 128, 0],
65
+ "greenyellow" => [173, 255, 47],
66
+ "grey" => [128, 128, 128],
67
+ "honeydew" => [240, 255, 240],
68
+ "hotpink" => [255, 105, 180],
69
+ "indianred" => [205, 92, 92],
70
+ "indigo" => [75, 0, 130],
71
+ "ivory" => [255, 255, 240],
72
+ "khaki" => [240, 230, 140],
73
+ "lavender" => [230, 230, 250],
74
+ "lavenderblush" => [255, 240, 245],
75
+ "lawngreen" => [124, 252, 0],
76
+ "lemonchiffon" => [255, 250, 205],
77
+ "lightblue" => [173, 216, 230],
78
+ "lightcoral" => [240, 128, 128],
79
+ "lightcyan" => [224, 255, 255],
80
+ "lightgoldenrodyellow" => [250, 250, 210],
81
+ "lightgray" => [211, 211, 211],
82
+ "lightgreen" => [144, 238, 144],
83
+ "lightgrey" => [211, 211, 211],
84
+ "lightpink" => [255, 182, 193],
85
+ "lightsalmon" => [255, 160, 122],
86
+ "lightseagreen" => [32, 178, 170],
87
+ "lightskyblue" => [135, 206, 250],
88
+ "lightslategray" => [119, 136, 153],
89
+ "lightslategrey" => [119, 136, 153],
90
+ "lightsteelblue" => [176, 196, 222],
91
+ "lightyellow" => [255, 255, 224],
92
+ "lime" => [0, 255, 0],
93
+ "limegreen" => [50, 205, 50],
94
+ "linen" => [250, 240, 230],
95
+ "magenta" => [255, 0, 255],
96
+ "maroon" => [128, 0, 0],
97
+ "mediumaquamarine" => [102, 205, 170],
98
+ "mediumblue" => [0, 0, 205],
99
+ "mediumorchid" => [186, 85, 211],
100
+ "mediumpurple" => [147, 112, 219],
101
+ "mediumseagreen" => [60, 179, 113],
102
+ "mediumslateblue" => [123, 104, 238],
103
+ "mediumspringgreen" => [0, 250, 154],
104
+ "mediumturquoise" => [72, 209, 204],
105
+ "mediumvioletred" => [199, 21, 133],
106
+ "midnightblue" => [25, 25, 112],
107
+ "mintcream" => [245, 255, 250],
108
+ "mistyrose" => [255, 228, 225],
109
+ "moccasin" => [255, 228, 181],
110
+ "navajowhite" => [255, 222, 173],
111
+ "navy" => [0, 0, 128],
112
+ "oldlace" => [253, 245, 230],
113
+ "olive" => [128, 128, 0],
114
+ "olivedrab" => [107, 142, 35],
115
+ "orange" => [255, 165, 0],
116
+ "orangered" => [255, 69, 0],
117
+ "orchid" => [218, 112, 214],
118
+ "palegoldenrod" => [238, 232, 170],
119
+ "palegreen" => [152, 251, 152],
120
+ "paleturquoise" => [175, 238, 238],
121
+ "palevioletred" => [219, 112, 147],
122
+ "papayawhip" => [255, 239, 213],
123
+ "peachpuff" => [255, 218, 185],
124
+ "peru" => [205, 133, 63],
125
+ "pink" => [255, 192, 203],
126
+ "plum" => [221, 160, 221],
127
+ "powderblue" => [176, 224, 230],
128
+ "purple" => [128, 0, 128],
129
+ "red" => [255, 0, 0],
130
+ "rosybrown" => [188, 143, 143],
131
+ "royalblue" => [65, 105, 225],
132
+ "saddlebrown" => [139, 69, 19],
133
+ "salmon" => [250, 128, 114],
134
+ "sandybrown" => [244, 164, 96],
135
+ "seagreen" => [46, 139, 87],
136
+ "seashell" => [255, 245, 238],
137
+ "sienna" => [160, 82, 45],
138
+ "silver" => [192, 192, 192],
139
+ "skyblue" => [135, 206, 235],
140
+ "slateblue" => [106, 90, 205],
141
+ "slategray" => [112, 128, 144],
142
+ "slategrey" => [112, 128, 144],
143
+ "snow" => [255, 250, 250],
144
+ "springgreen" => [0, 255, 127],
145
+ "steelblue" => [70, 130, 180],
146
+ "tan" => [210, 180, 140],
147
+ "teal" => [0, 128, 128],
148
+ "thistle" => [216, 191, 216],
149
+ "tomato" => [255, 99, 71],
150
+ "turquoise" => [64, 224, 208],
151
+ "violet" => [238, 130, 238],
152
+ "wheat" => [245, 222, 179],
153
+ "white" => [255, 255, 255],
154
+ "whitesmoke" => [245, 245, 245],
155
+ "yellow" => [255, 255, 0],
156
+ "yellowgreen" => [154, 205, 50]
157
+ }.freeze
158
+
159
+ def method_missing(name, *args, &block)
160
+ ColorNames[name] || super
161
+ end
162
+ end
163
+ end
164
+ end