color_converters 0.1.4 → 0.1.5

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.
@@ -1,184 +1,182 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support/core_ext/object/blank'
4
- require 'bigdecimal'
5
- require 'bigdecimal/util'
6
-
7
- module ColorConverters
8
- class BaseConverter
9
- IMPORT_DP = 8
10
- OUTPUT_DP = 2
11
-
12
- attr_reader :original_value, :rgba
13
-
14
- # keep track of subclasses for factory
15
- class << self
16
- attr_reader :converters
17
- end
18
-
19
- @converters = []
20
-
21
- def self.inherited(subclass)
22
- BaseConverter.converters << subclass
23
- end
24
-
25
- def self.factory(colour)
26
- converter = BaseConverter.converters.find { |klass| klass.matches?(colour) }
27
- converter&.new(colour)
28
- end
29
-
30
- def initialize(colour_input, limit_override = false)
31
- @original_value = colour_input
32
-
33
- # self.clamp_input(colour_input) if limit_clamp == true
34
-
35
- validation_errors = self.validate_input(colour_input)
36
- if limit_override == false && validation_errors.present?
37
- raise InvalidColorError, "Invalid color input: #{validation_errors.join(', ')}" # validation method is defined in each convertor
38
- end
39
-
40
- r, g, b, a = self.input_to_rgba(colour_input) # conversion method is defined in each convertor
41
-
42
- @rgba = { r: r.to_f.round(IMPORT_DP), g: g.to_f.round(IMPORT_DP), b: b.to_f.round(IMPORT_DP), a: a.to_f.round(IMPORT_DP) }
43
- end
44
-
45
- def rgb
46
- { r: @rgba[:r].to_f.round(OUTPUT_DP), g: @rgba[:g].to_f.round(OUTPUT_DP), b: @rgba[:b].to_f.round(OUTPUT_DP) }
47
- end
48
-
49
- def hex
50
- HexConverter.rgb_to_hex(self.rgb_array)
51
- end
52
-
53
- # not refactored to SubClass methods due to needing so many of the private methods
54
- def hsl
55
- @r, @g, @b = self.rgb_array_frac
56
-
57
- { h: self.hue.to_f.round(OUTPUT_DP), s: self.hsl_saturation.to_f.round(OUTPUT_DP), l: self.hsl_lightness.to_f.round(OUTPUT_DP) }
58
- end
59
-
60
- # not refactored to SubClass methods due to needing so many of the private methods
61
- def hsv
62
- @r, @g, @b = self.rgb_array
63
-
64
- { h: self.hue.to_f.round(OUTPUT_DP), s: self.hsv_saturation.to_f.round(OUTPUT_DP), v: self.hsv_value.to_f.round(OUTPUT_DP) }
65
- end
66
-
67
- def hsb
68
- hsb_hash = self.hsv
69
- hsb_hash[:b] = hsb_hash.delete(:v)
70
- hsb_hash
71
- end
72
-
73
- def cmyk
74
- c, m, y, k = CmykConverter.rgb_to_cmyk(self.rgb_array_frac)
75
-
76
- { c: c.to_f.round(OUTPUT_DP), m: m.to_f.round(OUTPUT_DP), y: y.to_f.round(OUTPUT_DP), k: k.to_f.round(OUTPUT_DP) }
77
- end
78
-
79
- def xyz
80
- x, y, z = XyzConverter.rgb_to_xyz(self.rgb_array_frac)
81
-
82
- { x: x.to_f.round(OUTPUT_DP), y: y.to_f.round(OUTPUT_DP), z: z.to_f.round(OUTPUT_DP) }
83
- end
84
-
85
- def cielab
86
- l, a, b = CielabConverter.xyz_to_cielab(XyzConverter.rgb_to_xyz(self.rgb_array_frac))
87
-
88
- { l: l.to_f.round(OUTPUT_DP), a: a.to_f.round(OUTPUT_DP), b: b.to_f.round(OUTPUT_DP) }
89
- end
90
-
91
- def cielch
92
- l, c, h = CielchConverter.cielab_to_cielch(CielabConverter.xyz_to_cielab(XyzConverter.rgb_to_xyz(self.rgb_array_frac)))
93
-
94
- { l: l.to_f.round(OUTPUT_DP), c: c.to_f.round(OUTPUT_DP), h: h.to_f.round(OUTPUT_DP) }
95
- end
96
-
97
- def oklab
98
- l, a, b = OklabConverter.xyz_to_oklab(XyzConverter.rgb_to_xyz(self.rgb_array_frac))
99
-
100
- { l: l.to_f.round(OUTPUT_DP), a: a.to_f.round(OUTPUT_DP), b: b.to_f.round(OUTPUT_DP) }
101
- end
102
-
103
- def oklch
104
- l, c, h = OklchConverter.oklab_to_oklch(OklabConverter.xyz_to_oklab(XyzConverter.rgb_to_xyz(self.rgb_array_frac)))
105
-
106
- { l: l.to_f.round(OUTPUT_DP), c: c.to_f.round(OUTPUT_DP), h: h.to_f.round(OUTPUT_DP) }
107
- end
108
-
109
- def alpha
110
- @rgba[:a]
111
- end
112
-
113
- def name(fuzzy: false)
114
- NameConverter.rgb_to_name(self.rgb_array, fuzzy)
115
- end
116
-
117
- protected
118
-
119
- def rgb_array
120
- [@rgba[:r].to_f, @rgba[:g].to_f, @rgba[:b].to_f]
121
- end
122
-
123
- def rgb_array_frac
124
- [@rgba[:r] / 255.0, @rgba[:g] / 255.0, @rgba[:b] / 255.0]
125
- end
126
-
127
- def rgb_min
128
- [@r, @g, @b].min
129
- end
130
-
131
- def rgb_max
132
- [@r, @g, @b].max
133
- end
134
-
135
- def rgb_delta
136
- self.rgb_max - self.rgb_min
137
- end
138
-
139
- def hue
140
- h = 0
141
-
142
- case true
143
- when self.rgb_max == self.rgb_min
144
- h = 0
145
- when self.rgb_max == @r
146
- h = (@g - @b) / self.rgb_delta
147
- when self.rgb_max == @g
148
- h = 2 + (@b - @r) / self.rgb_delta
149
- when self.rgb_max == @b
150
- h = 4 + (@r - @g) / self.rgb_delta
151
- end
152
-
153
- h = [h * 60, 360].min
154
- h % 360
155
- end
156
-
157
- def hsl_saturation
158
- s = 0
159
-
160
- case true
161
- when self.rgb_max == self.rgb_min
162
- s = 0
163
- when (self.hsl_lightness / 100.0) <= 0.5
164
- s = self.rgb_delta / (self.rgb_max + self.rgb_min)
165
- else
166
- s = self.rgb_delta / (2.0 - self.rgb_max - self.rgb_min)
167
- end
168
-
169
- s * 100
170
- end
171
-
172
- def hsl_lightness
173
- (self.rgb_min + self.rgb_max) / 2.0 * 100
174
- end
175
-
176
- def hsv_saturation
177
- self.rgb_max.zero? ? 0 : ((self.rgb_delta / self.rgb_max * 1000) / 10.0)
178
- end
179
-
180
- def hsv_value
181
- ((self.rgb_max / 255.0) * 1000) / 10.0
182
- end
183
- end
184
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'bigdecimal'
5
+ require 'bigdecimal/util'
6
+
7
+ module ColorConverters
8
+ class BaseConverter
9
+ IMPORT_DP = 8
10
+ OUTPUT_DP = 2
11
+
12
+ attr_reader :original_value, :rgba
13
+
14
+ # keep track of subclasses for factory
15
+ class << self
16
+ attr_reader :converters
17
+ end
18
+
19
+ @converters = []
20
+
21
+ def self.inherited(subclass)
22
+ BaseConverter.converters << subclass
23
+ end
24
+
25
+ def self.factory(colour_input, limit_override = false, limit_clamp = false)
26
+ converter = BaseConverter.converters.find { |klass| klass.matches?(colour_input) }
27
+ converter&.new(colour_input, limit_override, limit_clamp)
28
+ end
29
+
30
+ def initialize(colour_input, limit_override = false, limit_clamp = false)
31
+ colour_input.delete(:space) if colour_input.is_a?(Hash)
32
+
33
+ # colour_input = self.clamp_input(colour_input) if limit_clamp == true
34
+
35
+ validation_errors = self.validate_input(colour_input) # validation method is defined in each convertor
36
+ raise InvalidColorError, "Invalid color input: #{validation_errors.join(', ')}" if limit_override == false && validation_errors.present?
37
+
38
+ r, g, b, a = self.input_to_rgba(colour_input) # conversion method is defined in each convertor
39
+
40
+ @rgba = { r: r.to_f.round(IMPORT_DP), g: g.to_f.round(IMPORT_DP), b: b.to_f.round(IMPORT_DP), a: a.to_f.round(IMPORT_DP) }
41
+ end
42
+
43
+ def rgb
44
+ { r: @rgba[:r].to_f.round(OUTPUT_DP), g: @rgba[:g].to_f.round(OUTPUT_DP), b: @rgba[:b].to_f.round(OUTPUT_DP) }
45
+ end
46
+
47
+ def hex
48
+ HexConverter.rgb_to_hex(self.rgb_array)
49
+ end
50
+
51
+ # not refactored to SubClass methods due to needing so many of the private methods
52
+ def hsl
53
+ @r, @g, @b = self.rgb_array_frac
54
+
55
+ { h: self.hue.to_f.round(OUTPUT_DP), s: self.hsl_saturation.to_f.round(OUTPUT_DP), l: self.hsl_lightness.to_f.round(OUTPUT_DP) }
56
+ end
57
+
58
+ # not refactored to SubClass methods due to needing so many of the private methods
59
+ def hsv
60
+ @r, @g, @b = self.rgb_array
61
+
62
+ { h: self.hue.to_f.round(OUTPUT_DP), s: self.hsv_saturation.to_f.round(OUTPUT_DP), v: self.hsv_value.to_f.round(OUTPUT_DP) }
63
+ end
64
+
65
+ def hsb
66
+ hsb_hash = self.hsv
67
+ hsb_hash[:b] = hsb_hash.delete(:v)
68
+ hsb_hash
69
+ end
70
+
71
+ def cmyk
72
+ c, m, y, k = CmykConverter.rgb_to_cmyk(self.rgb_array_frac)
73
+
74
+ { c: c.to_f.round(OUTPUT_DP), m: m.to_f.round(OUTPUT_DP), y: y.to_f.round(OUTPUT_DP), k: k.to_f.round(OUTPUT_DP) }
75
+ end
76
+
77
+ def xyz
78
+ x, y, z = XyzConverter.rgb_to_xyz(self.rgb_array_frac)
79
+
80
+ { x: x.to_f.round(OUTPUT_DP), y: y.to_f.round(OUTPUT_DP), z: z.to_f.round(OUTPUT_DP) }
81
+ end
82
+
83
+ def cielab
84
+ l, a, b = CielabConverter.xyz_to_cielab(XyzConverter.rgb_to_xyz(self.rgb_array_frac))
85
+
86
+ { l: l.to_f.round(OUTPUT_DP), a: a.to_f.round(OUTPUT_DP), b: b.to_f.round(OUTPUT_DP) }
87
+ end
88
+
89
+ def cielch
90
+ l, c, h = CielchConverter.cielab_to_cielch(CielabConverter.xyz_to_cielab(XyzConverter.rgb_to_xyz(self.rgb_array_frac)))
91
+
92
+ { l: l.to_f.round(OUTPUT_DP), c: c.to_f.round(OUTPUT_DP), h: h.to_f.round(OUTPUT_DP) }
93
+ end
94
+
95
+ def oklab
96
+ l, a, b = OklabConverter.xyz_to_oklab(XyzConverter.rgb_to_xyz(self.rgb_array_frac))
97
+
98
+ { l: l.to_f.round(OUTPUT_DP), a: a.to_f.round(OUTPUT_DP), b: b.to_f.round(OUTPUT_DP) }
99
+ end
100
+
101
+ def oklch
102
+ l, c, h = OklchConverter.oklab_to_oklch(OklabConverter.xyz_to_oklab(XyzConverter.rgb_to_xyz(self.rgb_array_frac)))
103
+
104
+ { l: l.to_f.round(OUTPUT_DP), c: c.to_f.round(OUTPUT_DP), h: h.to_f.round(OUTPUT_DP) }
105
+ end
106
+
107
+ def alpha
108
+ @rgba[:a]
109
+ end
110
+
111
+ def name(fuzzy: false)
112
+ NameConverter.rgb_to_name(self.rgb_array, fuzzy)
113
+ end
114
+
115
+ protected
116
+
117
+ def rgb_array
118
+ [@rgba[:r].to_f, @rgba[:g].to_f, @rgba[:b].to_f]
119
+ end
120
+
121
+ def rgb_array_frac
122
+ [@rgba[:r] / 255.0, @rgba[:g] / 255.0, @rgba[:b] / 255.0]
123
+ end
124
+
125
+ def rgb_min
126
+ [@r, @g, @b].min
127
+ end
128
+
129
+ def rgb_max
130
+ [@r, @g, @b].max
131
+ end
132
+
133
+ def rgb_delta
134
+ self.rgb_max - self.rgb_min
135
+ end
136
+
137
+ def hue
138
+ h = 0
139
+
140
+ case true
141
+ when self.rgb_max == self.rgb_min
142
+ h = 0
143
+ when self.rgb_max == @r
144
+ h = (@g - @b) / self.rgb_delta
145
+ when self.rgb_max == @g
146
+ h = 2 + (@b - @r) / self.rgb_delta
147
+ when self.rgb_max == @b
148
+ h = 4 + (@r - @g) / self.rgb_delta
149
+ end
150
+
151
+ h = [h * 60, 360].min
152
+ h % 360
153
+ end
154
+
155
+ def hsl_saturation
156
+ s = 0
157
+
158
+ case true
159
+ when self.rgb_max == self.rgb_min
160
+ s = 0
161
+ when (self.hsl_lightness / 100.0) <= 0.5
162
+ s = self.rgb_delta / (self.rgb_max + self.rgb_min)
163
+ else
164
+ s = self.rgb_delta / (2.0 - self.rgb_max - self.rgb_min)
165
+ end
166
+
167
+ s * 100
168
+ end
169
+
170
+ def hsl_lightness
171
+ (self.rgb_min + self.rgb_max) / 2.0 * 100
172
+ end
173
+
174
+ def hsv_saturation
175
+ self.rgb_max.zero? ? 0 : ((self.rgb_delta / self.rgb_max * 1000) / 10.0)
176
+ end
177
+
178
+ def hsv_value
179
+ ((self.rgb_max / 255.0) * 1000) / 10.0
180
+ end
181
+ end
182
+ end
@@ -1,18 +1,31 @@
1
- # frozen_string_literal: true
2
-
3
- module ColorConverters
4
- class Color
5
- extend Forwardable
6
- def_delegators :@converter, :rgb, :hex, :hsl, :hsv, :hsb, :cmyk, :xyz, :cielab, :cielch, :oklab, :oklch, :name, :alpha
7
-
8
- def initialize(colour)
9
- @converter = BaseConverter.factory(colour)
10
- end
11
-
12
- def ==(other)
13
- return false unless other.is_a?(Color)
14
-
15
- rgb == other.rgb && alpha == other.alpha
16
- end
17
- end
18
- end
1
+ # frozen_string_literal: true
2
+
3
+ module ColorConverters
4
+ class Color
5
+ extend Forwardable
6
+ def_delegators :@converter, :rgb, :hex, :hsl, :hsv, :hsb, :cmyk, :xyz, :cielab, :cielch, :oklab, :oklch, :name, :alpha
7
+
8
+ # the two arguments mean we can support calling styles of a hash of colour keys, flattened hash, and string
9
+ # colour_input is nil when key-values are passed and must be pulled from the kwargs hash
10
+ # colour_input is set when a string is passed and kwargs is a hash of the extra options
11
+ def initialize(colour_input = nil, **kwargs)
12
+ if colour_input.nil?
13
+ colour_input = kwargs.except(:limit_override, :limit_clamp)
14
+ kwargs = kwargs.slice(:limit_override, :limit_clamp)
15
+ end
16
+
17
+ @original_value = colour_input
18
+
19
+ limit_override = kwargs.delete(:limit_override) || false
20
+ limit_clamp = kwargs.delete(:limit_clamp) || false
21
+
22
+ @converter = BaseConverter.factory(colour_input, limit_override, limit_clamp)
23
+ end
24
+
25
+ def ==(other)
26
+ return false unless other.is_a?(Color)
27
+
28
+ rgb == other.rgb && alpha == other.alpha
29
+ end
30
+ end
31
+ end
@@ -1,95 +1,99 @@
1
- # frozen_string_literal: true
2
-
3
- module ColorConverters
4
- class CielabConverter < BaseConverter
5
- def self.matches?(colour_input)
6
- return false unless colour_input.is_a?(Hash)
7
-
8
- colour_input.keys - [:l, :a, :b, :space] == [] && colour_input[:space].to_s == 'cie'
9
- end
10
-
11
- def self.bounds
12
- { l: [0.0, 100.0], a: [-128.0, 127.0], b: [-128.0, 127.0] }
13
- end
14
-
15
- private
16
-
17
- def validate_input(colour_input)
18
- CielabConverter.bounds.collect do |key, range|
19
- "#{key} must be between #{range[0]} and #{range[1]}" unless colour_input[key].to_f.between?(*range)
20
- end.compact
21
- end
22
-
23
- def input_to_rgba(colour_input)
24
- x, y, z = CielabConverter.cielab_to_xyz(colour_input)
25
- r, g, b = XyzConverter.xyz_to_rgb({ x: x, y: y, z: z })
26
-
27
- [r, g, b, 1.0]
28
- end
29
-
30
- def self.cielab_to_xyz(colour_input)
31
- l = colour_input[:l].to_d
32
- a = colour_input[:a].to_d
33
- b = colour_input[:b].to_d
34
-
35
- yy = (l + 16.0.to_d) / 116.0.to_d
36
- xx = (a / 500.0.to_d) + yy
37
- zz = yy - (b / 200.0.to_d)
38
-
39
- e = 216.0.to_d / 24_389.0.to_d
40
-
41
- x, y, z = [xx, yy, zz].map do
42
- if _1**3.to_d <= e
43
- (3.0.to_d * (6.0.to_d / 29.0.to_d) * (6.0.to_d / 29.0.to_d) * (_1 - (4.0.to_d / 29.0.to_d)))
44
- else
45
- _1**3.to_d
46
- end
47
- end
48
-
49
- x *= 95.047.to_d
50
- y *= 100.0.to_d
51
- z *= 108.883.to_d
52
-
53
- [x, y, z]
54
- end
55
-
56
- def self.xyz_to_cielab(xyz_array)
57
- x, y, z = xyz_array.map(&:to_d)
58
-
59
- # https://www.w3.org/TR/css-color-4/#color-conversion-code
60
- # # The D50 & D65 standard illuminant white point
61
- # wp_rel = [0.3457 / 0.3585, 1.0, (1.0 - 0.3457 - 0.3585) / 0.3585]
62
- wp_rel = [0.3127.to_d / 0.3290.to_d, 1.0.to_d, (1.0.to_d - 0.3127.to_d - 0.3290.to_d) / 0.3290.to_d].map { _1 * 100.0.to_d }
63
-
64
- xr, yr, zr = wp_rel
65
-
66
- # # Calculate the ratio of the XYZ values to the reference white.
67
- # # http://www.brucelindbloom.com/index.html?Equations.html
68
- rel = [x / xr, y / yr, z / zr]
69
-
70
- e = 216.0.to_d / 24_389.0.to_d
71
- k = 841.0.to_d / 108.0.to_d
72
-
73
- # And now transform
74
- # http:#en.wikipedia.org/wiki/Lab_color_space#Forward_transformation
75
- # There is a brief explanation there as far as the nature of the calculations,
76
- # as well as a much nicer looking modeling of the algebra.
77
- xx, yy, zz = rel.map do
78
- if _1 > e
79
- _1**(1.0.to_d / 3.0.to_d)
80
- else
81
- (k * _1) + (4.0.to_d / 29.0.to_d)
82
- # The 4/29 here is for when t = 0 (black). 4/29 * 116 = 16, and 16 -
83
- # 16 = 0, which is the correct value for L* with black.
84
- # ((1.0/3)*((29.0/6)**2) * t) + (4.0/29)
85
- end
86
- end
87
-
88
- l = ((116.0.to_d * yy) - 16.0.to_d)
89
- a = (500.0.to_d * (xx - yy))
90
- b = (200.0.to_d * (yy - zz))
91
-
92
- [l, a, b]
93
- end
94
- end
95
- end
1
+ # frozen_string_literal: true
2
+
3
+ module ColorConverters
4
+ class CielabConverter < BaseConverter
5
+ def self.matches?(colour_input)
6
+ return false unless colour_input.is_a?(Hash)
7
+
8
+ colour_input.keys - [:l, :a, :b, :space] == [] && colour_input[:space].to_s == 'cie'
9
+ end
10
+
11
+ def self.bounds
12
+ { l: [0.0, 100.0], a: [-128.0, 127.0], b: [-128.0, 127.0] }
13
+ end
14
+
15
+ private
16
+
17
+ # def clamp_input(colour_input)
18
+ # colour_input.each { |key, value| colour_input[key] = value.clamp(*CielabConverter.bounds[key]) }
19
+ # end
20
+
21
+ def validate_input(colour_input)
22
+ CielabConverter.bounds.collect do |key, range|
23
+ "#{key} must be between #{range[0]} and #{range[1]}" unless colour_input[key].to_f.between?(*range)
24
+ end.compact
25
+ end
26
+
27
+ def input_to_rgba(colour_input)
28
+ x, y, z = CielabConverter.cielab_to_xyz(colour_input)
29
+ r, g, b = XyzConverter.xyz_to_rgb({ x: x, y: y, z: z })
30
+
31
+ [r, g, b, 1.0]
32
+ end
33
+
34
+ def self.cielab_to_xyz(colour_input)
35
+ l = colour_input[:l].to_d
36
+ a = colour_input[:a].to_d
37
+ b = colour_input[:b].to_d
38
+
39
+ yy = (l + 16.0.to_d) / 116.0.to_d
40
+ xx = (a / 500.0.to_d) + yy
41
+ zz = yy - (b / 200.0.to_d)
42
+
43
+ e = 216.0.to_d / 24_389.0.to_d
44
+
45
+ x, y, z = [xx, yy, zz].map do
46
+ if _1**3.to_d <= e
47
+ (3.0.to_d * (6.0.to_d / 29.0.to_d) * (6.0.to_d / 29.0.to_d) * (_1 - (4.0.to_d / 29.0.to_d)))
48
+ else
49
+ _1**3.to_d
50
+ end
51
+ end
52
+
53
+ x *= 95.047.to_d
54
+ y *= 100.0.to_d
55
+ z *= 108.883.to_d
56
+
57
+ [x, y, z]
58
+ end
59
+
60
+ def self.xyz_to_cielab(xyz_array)
61
+ x, y, z = xyz_array.map(&:to_d)
62
+
63
+ # https://www.w3.org/TR/css-color-4/#color-conversion-code
64
+ # # The D50 & D65 standard illuminant white point
65
+ # wp_rel = [0.3457 / 0.3585, 1.0, (1.0 - 0.3457 - 0.3585) / 0.3585]
66
+ wp_rel = [0.3127.to_d / 0.3290.to_d, 1.0.to_d, (1.0.to_d - 0.3127.to_d - 0.3290.to_d) / 0.3290.to_d].map { _1 * 100.0.to_d }
67
+
68
+ xr, yr, zr = wp_rel
69
+
70
+ # # Calculate the ratio of the XYZ values to the reference white.
71
+ # # http://www.brucelindbloom.com/index.html?Equations.html
72
+ rel = [x / xr, y / yr, z / zr]
73
+
74
+ e = 216.0.to_d / 24_389.0.to_d
75
+ k = 841.0.to_d / 108.0.to_d
76
+
77
+ # And now transform
78
+ # http:#en.wikipedia.org/wiki/Lab_color_space#Forward_transformation
79
+ # There is a brief explanation there as far as the nature of the calculations,
80
+ # as well as a much nicer looking modeling of the algebra.
81
+ xx, yy, zz = rel.map do
82
+ if _1 > e
83
+ _1**(1.0.to_d / 3.0.to_d)
84
+ else
85
+ (k * _1) + (4.0.to_d / 29.0.to_d)
86
+ # The 4/29 here is for when t = 0 (black). 4/29 * 116 = 16, and 16 -
87
+ # 16 = 0, which is the correct value for L* with black.
88
+ # ((1.0/3)*((29.0/6)**2) * t) + (4.0/29)
89
+ end
90
+ end
91
+
92
+ l = ((116.0.to_d * yy) - 16.0.to_d)
93
+ a = (500.0.to_d * (xx - yy))
94
+ b = (200.0.to_d * (yy - zz))
95
+
96
+ [l, a, b]
97
+ end
98
+ end
99
+ end