Module: Hsluv

Extended by:
Hsluv
Included in:
Hsluv
Defined in:
lib/hsluv.rb

Constant Summary collapse

M =
[
    [3.240969941904521, -1.537383177570093, -0.498610760293],
    [-0.96924363628087, 1.87596750150772, 0.041555057407175],
    [0.055630079696993, -0.20397695888897, 1.056971514242878],
]
M_INV =
[
    [0.41239079926595, 0.35758433938387, 0.18048078840183],
    [0.21263900587151, 0.71516867876775, 0.072192315360733],
    [0.019330818715591, 0.11919477979462, 0.95053215224966],
]
REF_X =
0.95045592705167
REF_Y =
1.0
REF_Z =
1.089057750759878
REF_U =
0.19783000664283
REF_V =
0.46831999493879
KAPPA =
903.2962962
EPSILON =
0.0088564516

Instance Method Summary collapse

Instance Method Details

#degrees_to_radians(degrees) ⇒ Object



200
201
202
# File 'lib/hsluv.rb', line 200

def degrees_to_radians (degrees)
  degrees * Math::PI / 180.0
end

#distance_from_pole(point) ⇒ Object



255
256
257
# File 'lib/hsluv.rb', line 255

def distance_from_pole (point)
  Math.sqrt(point[0] ** 2 + point[1] ** 2)
end

#dot_product(a, b) ⇒ Object



275
276
277
# File 'lib/hsluv.rb', line 275

def dot_product (a, b)
  a.zip(b).map { |i, j| i * j }.inject(:+)
end

#f(t) ⇒ Object



259
260
261
# File 'lib/hsluv.rb', line 259

def f (t)
  t > EPSILON ? 116 * ((t / REF_Y) ** (1.0 / 3.0)) - 16.0 : t / REF_Y * KAPPA
end

#f_inv(t) ⇒ Object



263
264
265
# File 'lib/hsluv.rb', line 263

def f_inv (t)
  t > 8 ? REF_Y * ((t + 16.0) / 116.0) ** 3.0 : REF_Y * t / KAPPA
end

#from_linear(c) ⇒ Object



271
272
273
# File 'lib/hsluv.rb', line 271

def from_linear (c)
  c <= 0.0031308 ? 12.92 * c : (1.055 * (c ** (1.0 / 2.4)) - 0.055)
end

#get_bounds(l) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/hsluv.rb', line 227

def get_bounds (l)
  sub1 = ((l + 16.0) ** 3.0) / 1560896.0
  sub2 = sub1 > EPSILON ? sub1 : l / KAPPA
  ret = []

  M.each do |m1, m2, m3|
    [0, 1].each do |t|
      top1 = (284517.0 * m1 - 94839.0 * m3) * sub2
      top2 = (838422.0 * m3 + 769860.0 * m2 + 731718.0 * m1) * l * sub2 - 769860.0 * t * l
      bottom = (632260.0 * m3 - 126452.0 * m2) * sub2 + 126452.0 * t
      ret << [top1 / bottom, top2 / bottom]
    end
  end

  ret
end

#hex_to_hpluv(hex) ⇒ Object



38
39
40
# File 'lib/hsluv.rb', line 38

def hex_to_hpluv (hex)
  rgb_to_hpluv(*hex_to_rgb(hex))
end

#hex_to_hsluv(hex) ⇒ Object



34
35
36
# File 'lib/hsluv.rb', line 34

def hex_to_hsluv (hex)
  rgb_to_hsluv(*hex_to_rgb(hex))
end

#hex_to_rgb(hex) ⇒ Object



70
71
72
73
# File 'lib/hsluv.rb', line 70

def hex_to_rgb (hex)
  hex = hex.tr('#', '')
  [].tap { |arr| hex.split('').each_slice(2) { |block| arr << block.join.to_i(16) / 255.0 } }
end

#hpluv_to_hex(h, s, l) ⇒ Object



30
31
32
# File 'lib/hsluv.rb', line 30

def hpluv_to_hex (h, s, l)
  rgb_to_hex(*hpluv_to_rgb(h, s, l))
end

#hpluv_to_lch(arr) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
# File 'lib/hsluv.rb', line 182

def hpluv_to_lch (arr)
  h, s, l = arr

  return [100, 0.0, h] if l > 99.9999999
  return [0.0, 0.0, h] if l < 0.00000001

  mx = max_safe_chroma_for(l)
  c = mx / 100.0 * s

  [l, c, h]
end

#hpluv_to_rgb(h, s, l) ⇒ Object



50
51
52
# File 'lib/hsluv.rb', line 50

def hpluv_to_rgb (h, s, l)
  lch_to_rgb(*hpluv_to_lch([h, s, l]))
end

#hsluv_to_hex(h, s, l) ⇒ Object



26
27
28
# File 'lib/hsluv.rb', line 26

def hsluv_to_hex (h, s, l)
  rgb_to_hex(*hsluv_to_rgb(h, s, l))
end

#hsluv_to_lch(arr) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
# File 'lib/hsluv.rb', line 170

def hsluv_to_lch (arr)
  h, s, l = arr

  return [100, 0.0, h] if l > 99.9999999
  return [0.0, 0.0, h] if l < 0.00000001

  mx = max_chroma_for(l, h)
  c = mx / 100.0 * s

  [l, c, h]
end

#hsluv_to_rgb(h, s, l) ⇒ Object



42
43
44
# File 'lib/hsluv.rb', line 42

def hsluv_to_rgb (h, s, l)
  xyz_to_rgb(luv_to_xyz(lch_to_luv(hsluv_to_lch([h, s, l]))))
end

#intersect_line_line(line1, line2) ⇒ Object



251
252
253
# File 'lib/hsluv.rb', line 251

def intersect_line_line (line1, line2)
  (line1[1] - line2[1]) / (line2[0] - line1[0])
end

#lch_to_hpluv(arr) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
# File 'lib/hsluv.rb', line 124

def lch_to_hpluv (arr)
  l, c, h = arr

  return [h, 0.0, 100.0] if l > 99.9999999
  return [h, 0.0, 0.0] if l < 0.00000001

  mx = max_safe_chroma_for(l)
  s = c / mx * 100.0

  [h, s, l]
end

#lch_to_hsluv(arr) ⇒ Object



113
114
115
116
117
118
119
120
121
122
# File 'lib/hsluv.rb', line 113

def lch_to_hsluv (arr)
  l, c, h = arr
  return [h, 0.0, 100.0] if l > 99.9999999
  return [h, 0.0, 0.0] if l < 0.00000001

  mx = max_chroma_for(l, h)
  s = c / mx * 100.0

  [h, s, l]
end

#lch_to_luv(arr) ⇒ Object



160
161
162
163
164
165
166
167
168
# File 'lib/hsluv.rb', line 160

def lch_to_luv (arr)
  l, c, h = arr

  hrad = degrees_to_radians(h)
  u = Math.cos(hrad) * c
  v = Math.sin(hrad) * c

  [l, u, v]
end

#lch_to_rgb(l, c, h) ⇒ Object



58
59
60
# File 'lib/hsluv.rb', line 58

def lch_to_rgb (l, c, h)
  xyz_to_rgb(luv_to_xyz(lch_to_luv([l, c, h])))
end

#length_of_ray_until_intersect(theta, line) ⇒ Object



244
245
246
247
248
249
# File 'lib/hsluv.rb', line 244

def length_of_ray_until_intersect (theta, line)
  m1, b1 = line
  length = b1 / (Math.sin(theta) - m1 * Math.cos(theta))
  return nil if length < 0
  length
end

#luv_to_lch(arr) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/hsluv.rb', line 96

def luv_to_lch (arr)
  l, u, v = arr

  c = Math.sqrt(u * u + v * v)

  h = if c < 0.00000001
    0.0
  else
    hrad = Math.atan2(v, u)
    h = hrad * 180.0 / Math::PI
    h += 360.0 if h < 0.0
    h
  end

  [l, c, h]
end

#luv_to_xyz(arr) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/hsluv.rb', line 143

def luv_to_xyz (arr)
  l, u, v = arr

  return [0.0, 0.0, 0.0] if l == 0

  var_y = f_inv(l)
  var_u = u / (13.0 * l) + REF_U
  var_v = v / (13.0 * l) + REF_V


  y = var_y * REF_Y
  x = 0.0 - (9.0 * y * var_u) / ((var_u - 4.0) * var_v - var_u * var_v)
  z = (9.0 * y - (15.0 * var_v * y) - (var_v * x)) / (3.0 * var_v)

  [x, y, z]
end

#max_chroma_for(l, h) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
# File 'lib/hsluv.rb', line 204

def max_chroma_for (l, h)
  hrad = h / 360.0 * Math::PI * 2.0
  lengths = []

  get_bounds(l).each do |line|
    l = length_of_ray_until_intersect(hrad, line)
    lengths << l if l
  end

  lengths.min
end

#max_safe_chroma_for(l) ⇒ Object



216
217
218
219
220
221
222
223
224
225
# File 'lib/hsluv.rb', line 216

def max_safe_chroma_for (l)
  lengths = []

  get_bounds(l).each do |m1, b1|
    x = intersect_line_line([m1, b1], [-1.0 / m1, 0.0])
    lengths << distance_from_pole([x, b1 + x * m1])
  end

  lengths.min
end

#radians_to_degrees(rad) ⇒ Object



196
197
198
# File 'lib/hsluv.rb', line 196

def radians_to_degrees (rad)
  rad * 180.0 / Math::PI
end

#rgb_prepare(arr) ⇒ Object



279
280
281
# File 'lib/hsluv.rb', line 279

def rgb_prepare (arr)
  arr.map! { |ch| ch = ch.round(3); ch = [0, ch].max; ch = [1, ch].min; (ch * 255).round }
end

#rgb_to_hex(r, g, b) ⇒ Object



66
67
68
# File 'lib/hsluv.rb', line 66

def rgb_to_hex (r, g, b)
  '#%02x%02x%02x' % rgb_prepare([r, g, b])
end

#rgb_to_hpluv(r, g, b) ⇒ Object



54
55
56
# File 'lib/hsluv.rb', line 54

def rgb_to_hpluv (r, g, b)
  lch_to_hpluv(rgb_to_lch(r, g, b))
end

#rgb_to_hsluv(r, g, b) ⇒ Object



46
47
48
# File 'lib/hsluv.rb', line 46

def rgb_to_hsluv (r, g, b)
  lch_to_hsluv(rgb_to_lch(r, g, b))
end

#rgb_to_lch(r, g, b) ⇒ Object



62
63
64
# File 'lib/hsluv.rb', line 62

def rgb_to_lch (r, g, b)
  luv_to_lch(xyz_to_luv(rgb_to_xyz([r, g, b])))
end

#rgb_to_xyz(arr) ⇒ Object



77
78
79
80
# File 'lib/hsluv.rb', line 77

def rgb_to_xyz (arr)
  rgbl = arr.map { |val| to_linear(val) }
  M_INV.map { |i| dot_product(i, rgbl) }
end

#to_linear(c) ⇒ Object



267
268
269
# File 'lib/hsluv.rb', line 267

def to_linear (c)
  c > 0.04045 ? ((c + 0.055) / 1.055) ** 2.4 : c / 12.92
end

#xyz_to_luv(arr) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/hsluv.rb', line 82

def xyz_to_luv (arr)
  x, y, z = arr
  l = f(y)

  return [0.0, 0.0, 0.0] if [x, y, z, 0.0].uniq.length == 1 || l == 0.0

  var_u = (4.0 * x) / (x + (15.0 * y) + (3.0 * z))
  var_v = (9.0 * y) / (x + (15.0 * y) + (3.0 * z))
  u = 13.0 * l * (var_u - REF_U)
  v = 13.0 * l * (var_v - REF_V)

  [l, u, v]
end

#xyz_to_rgb(arr) ⇒ Object



138
139
140
141
# File 'lib/hsluv.rb', line 138

def xyz_to_rgb (arr)
  xyz = M.map { |i| dot_product(i, arr) }
  xyz.map { |i| from_linear(i) }
end