red-colors 0.1.2 → 0.1.3
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.
- checksums.yaml +4 -4
- data/lib/colors/convert.rb +62 -18
- data/lib/colors/rgb.rb +1 -1
- data/lib/colors/version.rb +1 -1
- data/lib/colors/xyy.rb +1 -3
- data/red-colors.gemspec +2 -0
- data/test/test-husl.rb +45 -26
- data/test/test-rgb.rb +13 -25
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f5bd8e883bdf940ae77ece7135e3667a5d953534fda4780e344fd9413b59fa7
|
4
|
+
data.tar.gz: c275f21b6223f399f50b4265b32d652753d9a6d5297d1bdff1597aa9c2956371
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1dbc251a4dda958ada6163601b876ab7572f6aa2a71bb84e728f6b1bd180ef90a6937ef5db3ca3724951be8a329f2abc655f162d8849a4f2868be9d78eb4c73
|
7
|
+
data.tar.gz: b0d5952ff67aa28147f85105ea7d89f59a526cfa856d5ab1b6826eec35f50d60dde5a69d1f28b1c73720b4edcd8eff948c656880feda2e1e943f7decbc9c0859
|
data/lib/colors/convert.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "matrix"
|
2
|
+
|
1
3
|
module Colors
|
2
4
|
module Convert
|
3
5
|
module_function
|
@@ -16,6 +18,11 @@ module Colors
|
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
21
|
+
private def matrix_inv(matrix)
|
22
|
+
matrix = Matrix[*matrix]
|
23
|
+
matrix.inv.to_a
|
24
|
+
end
|
25
|
+
|
19
26
|
def max_chroma(l, h)
|
20
27
|
h_rad = degree_to_radian(h)
|
21
28
|
sin_h = Math.sin(h_rad).to_r
|
@@ -35,9 +42,9 @@ module Colors
|
|
35
42
|
|
36
43
|
bounds = Array.new(6) { [0r, 0r] }
|
37
44
|
0.upto(2) do |ch|
|
38
|
-
m1 =
|
39
|
-
m2 =
|
40
|
-
m3 =
|
45
|
+
m1 = M_XYZ_RGB[ch][0].to_r
|
46
|
+
m2 = M_XYZ_RGB[ch][1].to_r
|
47
|
+
m3 = M_XYZ_RGB[ch][2].to_r
|
41
48
|
|
42
49
|
[0, 1].each do |t|
|
43
50
|
top1 = (284517r * m1 - 94839r * m3) * sub2
|
@@ -109,14 +116,9 @@ module Colors
|
|
109
116
|
|
110
117
|
def luv_to_lch(l, u, v)
|
111
118
|
c = Math.sqrt(u*u + v*v).to_r
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
else
|
116
|
-
h = Math.atan2(v, u).to_r * 180/Math::PI.to_r
|
117
|
-
h += 360r if h < 0
|
118
|
-
end
|
119
|
-
|
119
|
+
hard = Math.atan2(v, u).to_r
|
120
|
+
h = hard * 180 / Math::PI.to_r
|
121
|
+
h += 360r if h < 0
|
120
122
|
[l, c, h]
|
121
123
|
end
|
122
124
|
|
@@ -195,26 +197,68 @@ module Colors
|
|
195
197
|
|
196
198
|
# sRGB -> ???
|
197
199
|
|
200
|
+
def srgb_from_linear_srgb(r, g, b)
|
201
|
+
a = 0.055r
|
202
|
+
[r, g, b].map do |v|
|
203
|
+
if v < 0.0031308
|
204
|
+
12.92r * v
|
205
|
+
else
|
206
|
+
(1 + a) * v**(1/2.4r) - a
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
198
211
|
def srgb_to_linear_srgb(r, g, b)
|
212
|
+
a = 0.055r
|
199
213
|
[r, g, b].map do |v|
|
200
214
|
if v > 0.04045
|
201
|
-
((v +
|
215
|
+
((v + a) / (1 + a)) ** 2.4r
|
202
216
|
else
|
203
217
|
v / 12.92r
|
204
218
|
end
|
205
219
|
end
|
206
220
|
end
|
207
221
|
|
222
|
+
# xyY -> ???
|
223
|
+
|
224
|
+
def xyy_to_xyz(x, y, large_y)
|
225
|
+
large_x = large_y*x/y
|
226
|
+
large_z = large_y*(1 - x - y)/y
|
227
|
+
[large_x, large_y, large_z]
|
228
|
+
end
|
229
|
+
|
208
230
|
# XYZ -> ???
|
209
231
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
232
|
+
# sRGB reference points
|
233
|
+
R_xyY = [0.64r, 0.33r, 1r]
|
234
|
+
G_xyY = [0.30r, 0.60r, 1r]
|
235
|
+
B_xyY = [0.15r, 0.06r, 1r]
|
236
|
+
D65_xyY = [0.3127r, 0.3290r, 1r]
|
237
|
+
|
238
|
+
R_XYZ = xyy_to_xyz(*R_xyY)
|
239
|
+
G_XYZ = xyy_to_xyz(*G_xyY)
|
240
|
+
B_XYZ = xyy_to_xyz(*B_xyY)
|
241
|
+
D65_XYZ = xyy_to_xyz(*D65_xyY)
|
242
|
+
|
243
|
+
M_P = [
|
244
|
+
[R_XYZ[0], G_XYZ[0], B_XYZ[0]],
|
245
|
+
[R_XYZ[1], G_XYZ[1], B_XYZ[1]],
|
246
|
+
[R_XYZ[2], G_XYZ[2], B_XYZ[2]]
|
214
247
|
]
|
248
|
+
|
249
|
+
M_S = dot_product(matrix_inv(M_P), D65_XYZ)
|
250
|
+
|
251
|
+
M_RGB_XYZ = (0 ... 3).map do |i|
|
252
|
+
(0 ... 3).map {|j| (M_S[j] * M_P[i][j]).round(4) }
|
253
|
+
end
|
254
|
+
|
255
|
+
M_XYZ_RGB = matrix_inv(M_RGB_XYZ).map do |row|
|
256
|
+
row.map {|v| v.round(4) }
|
257
|
+
end
|
258
|
+
|
215
259
|
def xyz_to_rgb(x, y, z)
|
216
|
-
r, g, b = dot_product(
|
217
|
-
r, g, b =
|
260
|
+
r, g, b = dot_product(M_XYZ_RGB, [x, y, z])
|
261
|
+
r, g, b = srgb_from_linear_srgb(r, g, b)
|
218
262
|
[
|
219
263
|
r.clamp(0r, 1r),
|
220
264
|
g.clamp(0r, 1r),
|
data/lib/colors/rgb.rb
CHANGED
@@ -118,7 +118,7 @@ module Colors
|
|
118
118
|
c = RGB.new(r, g, b).to_xyz
|
119
119
|
l, u, v = c.luv_components(WHITE_POINT_D65)
|
120
120
|
h, s, l = Convert.luv_to_husl(l, u, v)
|
121
|
-
HUSL.new(h, s.
|
121
|
+
HUSL.new(h, s.clamp(0r, 1r).to_r, l.clamp(0r, 1r).to_r)
|
122
122
|
end
|
123
123
|
|
124
124
|
def to_xyz
|
data/lib/colors/version.rb
CHANGED
data/lib/colors/xyy.rb
CHANGED
data/red-colors.gemspec
CHANGED
@@ -32,6 +32,8 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.files += Dir.glob("doc/text/*")
|
33
33
|
spec.test_files += Dir.glob("test/**/*")
|
34
34
|
|
35
|
+
spec.add_runtime_dependency("matrix")
|
36
|
+
|
35
37
|
spec.add_development_dependency("bundler")
|
36
38
|
spec.add_development_dependency("rake")
|
37
39
|
spec.add_development_dependency("test-unit")
|
data/test/test-husl.rb
CHANGED
@@ -177,7 +177,7 @@ class ColorsHUSLTest < Test::Unit::TestCase
|
|
177
177
|
test("#desaturate") do
|
178
178
|
c = Colors::RGB.new(1r, 1r, 0r).to_husl.desaturate(0.8)
|
179
179
|
assert_instance_of(Colors::HUSL, c)
|
180
|
-
assert_near(Colors::HUSL.new(85.87432021817473r, 0.9838589961976354r, 0.8850923805142681r), c)
|
180
|
+
assert_near(Colors::HUSL.new(85.87432021817473r, 0.9838589961976354r, 0.8850923805142681r), c, 0.01)
|
181
181
|
end
|
182
182
|
|
183
183
|
test("#to_husl") do
|
@@ -185,30 +185,49 @@ class ColorsHUSLTest < Test::Unit::TestCase
|
|
185
185
|
assert_same(black, black.to_hsl)
|
186
186
|
end
|
187
187
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
188
|
+
sub_test_case("basic colors") do
|
189
|
+
colors = [
|
190
|
+
{ rgb: "#ff0000", husl: [ 12.16, 1.0, 0.532], name: "red" },
|
191
|
+
{ rgb: "#ffff00", husl: [ 85.90, 1.0, 0.971], name: "yellow" },
|
192
|
+
{ rgb: "#00ff00", husl: [127.72, 1.0, 0.877], name: "green" },
|
193
|
+
{ rgb: "#00ffff", husl: [192.20, 1.0, 0.911], name: "cyan" },
|
194
|
+
{ rgb: "#0000ff", husl: [265.87, 1.0, 0.323], name: "blue" },
|
195
|
+
{ rgb: "#ff00ff", husl: [307.71, 1.0, 0.603], name: "magenta" },
|
196
|
+
]
|
197
|
+
data(colors.map {|r| [r[:name], r] }.to_h)
|
198
|
+
def test_husl_to_rgb(data)
|
199
|
+
husl, rgb = data.values_at(:husl, :rgb)
|
200
|
+
assert_equal(rgb,
|
201
|
+
Colors::HUSL.new(*husl).to_rgb.to_hex_string)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
sub_test_case("reproducing color conversion in seaborn") do
|
206
|
+
s = 0.9 * 99/100r
|
207
|
+
l = 0.65 * 99/100r
|
208
|
+
palette = [
|
209
|
+
{ rgb: "#f77189", husl: [ 3.590, s, l] },
|
210
|
+
{ rgb: "#e68332", husl: [ 33.50666666666666, s, l] },
|
211
|
+
{ rgb: "#bb9832", husl: [ 63.42333333333333, s, l] },
|
212
|
+
{ rgb: "#97a431", husl: [ 93.3400, s, l] },
|
213
|
+
{ rgb: "#50b131", husl: [123.25666666666666, s, l] },
|
214
|
+
{ rgb: "#34af84", husl: [153.17333333333332, s, l] },
|
215
|
+
{ rgb: "#36ada4", husl: [183.0900, s, l] },
|
216
|
+
{ rgb: "#38aabf", husl: [213.00666666666663, s, l] },
|
217
|
+
{ rgb: "#3ba3ec", husl: [242.92333333333332, s, l] },
|
218
|
+
{ rgb: "#a48cf4", husl: [272.8400, s, l] },
|
219
|
+
{ rgb: "#e867f4", husl: [302.75666666666666, s, l] }, # TODO: #e866f4 is the best
|
220
|
+
{ rgb: "#f668c2", husl: [332.67333333333335, s, l] }
|
221
|
+
]
|
222
|
+
data(palette.map {|r|
|
223
|
+
name = r[:name] || "husl(#{r[:husl]}) => #{r[:rgb]}"
|
224
|
+
[name, r]
|
225
|
+
}.to_h)
|
226
|
+
def test_husl_to_rgb(data)
|
227
|
+
husl_components, hex_string = data.values_at(:husl, :rgb)
|
228
|
+
husl = Colors::HUSL.new(*husl_components)
|
229
|
+
assert_equal(hex_string,
|
230
|
+
husl.to_rgb.to_hex_string)
|
231
|
+
end
|
213
232
|
end
|
214
233
|
end
|
data/test/test-rgb.rb
CHANGED
@@ -292,31 +292,19 @@ class ColorsRGBTest < Test::Unit::TestCase
|
|
292
292
|
Colors::RGB.new(1r, 1r, 1r).to_hsl)
|
293
293
|
end
|
294
294
|
|
295
|
-
|
296
|
-
#
|
297
|
-
|
298
|
-
|
299
|
-
#
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
# cyan
|
309
|
-
assert_near(Colors::HUSL.new(192.17705063006116r, 1r, 0.9111475231670507r),
|
310
|
-
Colors::RGB.new(0r, 1r, 1r).to_husl)
|
311
|
-
# blue
|
312
|
-
assert_near(Colors::HUSL.new(265.8743202181779r, 1r, 0.3230087290398002r),
|
313
|
-
Colors::RGB.new(0r, 0r, 1r).to_husl)
|
314
|
-
# magenta
|
315
|
-
assert_near(Colors::HUSL.new(307.7150129492436r, 1r, 0.60322731354551294r),
|
316
|
-
Colors::RGB.new(1r, 0r, 1r).to_husl)
|
317
|
-
# white
|
318
|
-
assert_near(Colors::HUSL.new(0r, 0r, 1r),
|
319
|
-
Colors::RGB.new(1r, 1r, 1r).to_husl)
|
295
|
+
data(
|
296
|
+
"red" => { rgb: "#ff0000", husl: [ 12.16, 1.0, 0.532] },
|
297
|
+
"yellow" => { rgb: "#ffff00", husl: [ 85.90, 1.0, 0.971] },
|
298
|
+
"green" => { rgb: "#00ff00", husl: [127.72, 1.0, 0.877] },
|
299
|
+
"cyan" => { rgb: "#00ffff", husl: [192.20, 1.0, 0.911] },
|
300
|
+
"blue" => { rgb: "#0000ff", husl: [265.87, 1.0, 0.323] },
|
301
|
+
"magenta" => { rgb: "#ff00ff", husl: [307.71, 1.0, 0.603] }
|
302
|
+
)
|
303
|
+
def test_to_husl(data)
|
304
|
+
husl_components, hex_string = data.values_at(:husl, :rgb)
|
305
|
+
husl = Colors::HUSL.new(*husl_components)
|
306
|
+
rgb = Colors::RGB.parse(hex_string)
|
307
|
+
assert_near(husl, rgb.to_husl, 0.03)
|
320
308
|
end
|
321
309
|
|
322
310
|
data do
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: red-colors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenta Murata
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: matrix
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|