hsluv 1.0.0 → 1.0.1

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 (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hsluv.rb +274 -0
  3. metadata +13 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 73a3f107ee6cb5c00f486efdf69f13175f00fb2d
4
- data.tar.gz: e9f833f7e232992a5538e5ae79d22ae9f63b51b3
3
+ metadata.gz: ef572dfa305acb1580e620aca808dc0a1a333a28
4
+ data.tar.gz: a8c9d21375d2d7f814403517a1a3375ff36c5315
5
5
  SHA512:
6
- metadata.gz: 49e37de82548f2ea7931779a6dc000798735dbaa20bf62872e23e97931a823ad7248674240755c0fc29073cf94cb6989cb373f66c789e8d2cf3c389facfab0f2
7
- data.tar.gz: b0dcf8343d2eb492bac3acc36551b6189b8fda24205498f974a4a285bdb6ac7039521176bb24540961e0f42be54abb2841e4863a4a5e22fff5e1450a3369fc76
6
+ metadata.gz: 7716cd233a9bdd8b98d5a6ddc0e4cd5060ec314d0f125eb2357caba3efe8d7deb9275325ff484ea1b1389357fd860b73fb611505fc8e7d5cfbbbaa0e87b25d6e
7
+ data.tar.gz: 7115ab48803d3770ed365dc7c6562463df91bdbcad7821a087ed2af20293d3f6b19f8ba89e50208f1cab64b3bd70ee262e1dc0db088e3a0e7b9ded66692d5a4b
@@ -0,0 +1,274 @@
1
+ module Hsluv
2
+ extend self
3
+
4
+ M = [
5
+ [3.240969941904521, -1.537383177570093, -0.498610760293],
6
+ [-0.96924363628087, 1.87596750150772, 0.041555057407175],
7
+ [0.055630079696993, -0.20397695888897, 1.056971514242878],
8
+ ]
9
+
10
+ M_INV = [
11
+ [0.41239079926595, 0.35758433938387, 0.18048078840183],
12
+ [0.21263900587151, 0.71516867876775, 0.072192315360733],
13
+ [0.019330818715591, 0.11919477979462, 0.95053215224966],
14
+ ]
15
+
16
+ REF_X = 0.95045592705167
17
+ REF_Y = 1.0
18
+ REF_Z = 1.089057750759878
19
+ REF_U = 0.19783000664283
20
+ REF_V = 0.46831999493879
21
+ KAPPA = 903.2962962
22
+ EPSILON = 0.0088564516
23
+
24
+ ###
25
+
26
+ def hsluv_to_hex (h, s, l)
27
+ rgb_to_hex(*hsluv_to_rgb(h, s, l))
28
+ end
29
+
30
+ def hpluv_to_hex (h, s, l)
31
+ rgb_to_hex(*hpluv_to_rgb(h, s, l))
32
+ end
33
+
34
+ def hex_to_hsluv (hex)
35
+ rgb_to_hsluv(*hex_to_rgb(hex))
36
+ end
37
+
38
+ def hex_to_hpluv (hex)
39
+ rgb_to_hpluv(*hex_to_rgb(hex))
40
+ end
41
+
42
+ def hsluv_to_rgb (h, s, l)
43
+ xyz_to_rgb(luv_to_xyz(lch_to_luv(hsluv_to_lch([h, s, l]))))
44
+ end
45
+
46
+ def rgb_to_hsluv (r, g, b)
47
+ lch_to_hsluv(rgb_to_lch(r, g, b))
48
+ end
49
+
50
+ def hpluv_to_rgb (h, s, l)
51
+ lch_to_rgb(*hpluv_to_lch([h, s, l]))
52
+ end
53
+
54
+ def rgb_to_hpluv (r, g, b)
55
+ lch_to_hpluv(rgb_to_lch(r, g, b))
56
+ end
57
+
58
+ def lch_to_rgb (l, c, h)
59
+ xyz_to_rgb(luv_to_xyz(lch_to_luv([l, c, h])))
60
+ end
61
+
62
+ def rgb_to_lch (r, g, b)
63
+ luv_to_lch(xyz_to_luv(rgb_to_xyz([r, g, b])))
64
+ end
65
+
66
+ def rgb_to_hex (r, g, b)
67
+ '#%02x%02x%02x' % rgb_prepare([r, g, b])
68
+ end
69
+
70
+ def hex_to_rgb (hex)
71
+ hex = hex.tr('#', '')
72
+ [].tap { |arr| hex.split('').each_slice(2) { |block| arr << block.join.to_i(16) / 255.0 } }
73
+ end
74
+
75
+ ###
76
+
77
+ def rgb_to_xyz (arr)
78
+ rgbl = arr.map { |val| to_linear(val) }
79
+ M_INV.map { |i| dot_product(i, rgbl) }
80
+ end
81
+
82
+ def xyz_to_luv (arr)
83
+ x, y, z = arr
84
+ l = f(y)
85
+
86
+ return [0.0, 0.0, 0.0] if [x, y, z, 0.0].uniq.length == 1 || l == 0.0
87
+
88
+ var_u = (4.0 * x) / (x + (15.0 * y) + (3.0 * z))
89
+ var_v = (9.0 * y) / (x + (15.0 * y) + (3.0 * z))
90
+ u = 13.0 * l * (var_u - REF_U)
91
+ v = 13.0 * l * (var_v - REF_V)
92
+
93
+ [l, u, v]
94
+ end
95
+
96
+ def luv_to_lch (arr)
97
+ l, u, v = arr
98
+ c = ((u ** 2) + (v ** 2)) ** (1 / 2.0)
99
+ hrad = Math.atan2(v, u)
100
+ h = radians_to_degrees(hrad)
101
+ h += 360.0 if h < 0.0
102
+ [l, c, h]
103
+ end
104
+
105
+ def lch_to_hsluv (arr)
106
+ l, c, h = arr
107
+ return [h, 0.0, 100.0] if l > 99.9999999
108
+ return [h, 0.0, 0.0] if l < 0.00000001
109
+
110
+ mx = max_chroma_for(l, h)
111
+ s = c / mx * 100.0
112
+
113
+ [h, s, l]
114
+ end
115
+
116
+ def lch_to_hpluv (arr)
117
+ l, c, h = arr
118
+
119
+ return [h, 0.0, 100.0] if l > 99.9999999
120
+ return [h, 0.0, 0.0] if l < 0.00000001
121
+
122
+ mx = max_safe_chroma_for(l)
123
+ s = c / mx * 100.0
124
+
125
+ [h, s, l]
126
+ end
127
+
128
+ ###
129
+
130
+ def xyz_to_rgb (arr)
131
+ xyz = M.map { |i| dot_product(i, arr) }
132
+ xyz.map { |i| from_linear(i) }
133
+ end
134
+
135
+ def luv_to_xyz (arr)
136
+ l, u, v = arr
137
+
138
+ return [0.0, 0.0, 0.0] if l == 0
139
+
140
+ var_y = f_inv(l)
141
+ var_u = u / (13.0 * l) + REF_U
142
+ var_v = v / (13.0 * l) + REF_V
143
+
144
+
145
+ y = var_y * REF_Y
146
+ x = 0.0 - (9.0 * y * var_u) / ((var_u - 4.0) * var_v - var_u * var_v)
147
+ z = (9.0 * y - (15.0 * var_v * y) - (var_v * x)) / (3.0 * var_v)
148
+
149
+ [x, y, z]
150
+ end
151
+
152
+ def lch_to_luv (arr)
153
+ l, c, h = arr
154
+
155
+ hrad = degrees_to_radians(h)
156
+ u = Math.cos(hrad) * c
157
+ v = Math.sin(hrad) * c
158
+
159
+ [l, u, v]
160
+ end
161
+
162
+ def hsluv_to_lch (arr)
163
+ h, s, l = arr
164
+
165
+ return [100, 0.0, h] if l > 99.9999999
166
+ return [0.0, 0.0, h] if l < 0.00000001
167
+
168
+ mx = max_chroma_for(l, h)
169
+ c = mx / 100.0 * s
170
+
171
+ [l, c, h]
172
+ end
173
+
174
+ def hpluv_to_lch (arr)
175
+ h, s, l = arr
176
+
177
+ return [100, 0.0, h] if l > 99.9999999
178
+ return [0.0, 0.0, h] if l < 0.00000001
179
+
180
+ mx = max_safe_chroma_for(l)
181
+ c = mx / 100.0 * s
182
+
183
+ [l, c, h]
184
+ end
185
+
186
+ ###
187
+
188
+ def radians_to_degrees (rad)
189
+ rad * 180.0 / Math::PI
190
+ end
191
+
192
+ def degrees_to_radians (degrees)
193
+ degrees * Math::PI / 180.0
194
+ end
195
+
196
+ def max_chroma_for (l, h)
197
+ hrad = h / 360.0 * Math::PI * 2.0
198
+ lengths = []
199
+
200
+ get_bounds(l).each do |line|
201
+ l = length_of_ray_until_intersect(hrad, line)
202
+ lengths << l if l
203
+ end
204
+
205
+ lengths.min
206
+ end
207
+
208
+ def max_safe_chroma_for (l)
209
+ lengths = []
210
+
211
+ get_bounds(l).each do |m1, b1|
212
+ x = intersect_line_line([m1, b1], [-1.0 / m1, 0.0])
213
+ lengths << distance_from_pole([x, b1 + x * m1])
214
+ end
215
+
216
+ lengths.min
217
+ end
218
+
219
+ def get_bounds (l)
220
+ sub1 = ((l + 16.0) ** 3.0) / 1560896.0
221
+ sub2 = sub1 > EPSILON ? sub1 : l / KAPPA
222
+ ret = []
223
+
224
+ M.each do |m1, m2, m3|
225
+ [0, 1].each do |t|
226
+ top1 = (284517.0 * m1 - 94839.0 * m3) * sub2
227
+ top2 = (838422.0 * m3 + 769860.0 * m2 + 731718.0 * m1) * l * sub2 - 769860.0 * t * l
228
+ bottom = (632260.0 * m3 - 126452.0 * m2) * sub2 + 126452.0 * t
229
+ ret << [top1 / bottom, top2 / bottom]
230
+ end
231
+ end
232
+
233
+ ret
234
+ end
235
+
236
+ def length_of_ray_until_intersect (theta, line)
237
+ m1, b1 = line
238
+ length = b1 / (Math.sin(theta) - m1 * Math.cos(theta))
239
+ return nil if length < 0
240
+ length
241
+ end
242
+
243
+ def intersect_line_line (line1, line2)
244
+ (line1[1] - line2[1]) / (line2[0] - line1[0])
245
+ end
246
+
247
+ def distance_from_pole (point)
248
+ Math.sqrt(point[0] ** 2 + point[1] ** 2)
249
+ end
250
+
251
+ def f (t)
252
+ t > EPSILON ? 116 * ((t / REF_Y) ** (1.0 / 3.0)) - 16.0 : t / REF_Y * KAPPA
253
+ end
254
+
255
+ def f_inv (t)
256
+ t > 8 ? REF_Y * ((t + 16.0) / 116.0) ** 3.0 : REF_Y * t / KAPPA
257
+ end
258
+
259
+ def to_linear (c)
260
+ c > 0.04045 ? ((c + 0.055) / 1.055) ** 2.4 : c / 12.92
261
+ end
262
+
263
+ def from_linear (c)
264
+ c <= 0.0031308 ? 12.92 * c : (1.055 * (c ** (1.0 / 2.4)) - 0.055)
265
+ end
266
+
267
+ def dot_product (a, b)
268
+ a.zip(b).map { |i, j| i * j }.inject(:+)
269
+ end
270
+
271
+ def rgb_prepare (arr)
272
+ arr.map! { |ch| ch = ch.round(3); ch = [0, ch].max; ch = [1, ch].min; (ch * 255).round }
273
+ end
274
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hsluv
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radu-Bogdan Croitoru
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-01-30 00:00:00.000000000 Z
12
+ date: 2017-03-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -43,30 +43,30 @@ dependencies:
43
43
  name: pry
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - ">="
46
+ - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '0'
48
+ version: '0.0'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - ">="
53
+ - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: '0'
55
+ version: '0.0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: rspec
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ">="
60
+ - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: '0'
62
+ version: '0.0'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - ">="
67
+ - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: '0'
69
+ version: '0.0'
70
70
  description: HSLuv is implemented as a set of functions to convert colors between
71
71
  RGB, HSLuv and HPLuv.
72
72
  email:
@@ -75,7 +75,8 @@ email:
75
75
  executables: []
76
76
  extensions: []
77
77
  extra_rdoc_files: []
78
- files: []
78
+ files:
79
+ - lib/hsluv.rb
79
80
  homepage: https://github.com/hsluv/hsluv-ruby
80
81
  licenses:
81
82
  - MIT
@@ -96,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
97
  version: '0'
97
98
  requirements: []
98
99
  rubyforge_project:
99
- rubygems_version: 2.6.8
100
+ rubygems_version: 2.5.1
100
101
  signing_key:
101
102
  specification_version: 4
102
103
  summary: Human friendly alternative to HSL.