module Hsluv

Converts between RGB, HSLUV

Constants

EPSILON
KAPPA
M
M_INV
REF_U
REF_V
REF_X
REF_Y
REF_Z

Public Instance Methods

# File _plugins/gtn/hsluv.rb, line 219
def degrees_to_radians(degrees)
  degrees * Math::PI / 180.0
end
# File _plugins/gtn/hsluv.rb, line 275
def distance_from_pole(point)
  Math.sqrt((point[0]**2) + (point[1]**2))
end
# File _plugins/gtn/hsluv.rb, line 295
def dot_product(a, b)
  a.zip(b).map { |i, j| i * j }.inject(:+)
end
# File _plugins/gtn/hsluv.rb, line 279
def f(t)
  t > EPSILON ? (116 * ((t / REF_Y)**(1.0 / 3.0))) - 16.0 : t / REF_Y * KAPPA
end
# File _plugins/gtn/hsluv.rb, line 283
def f_inv(t)
  t > 8 ? REF_Y * (((t + 16.0) / 116.0)**3.0) : REF_Y * t / KAPPA
end
# File _plugins/gtn/hsluv.rb, line 291
def from_linear(c)
  c <= 0.0031308 ? 12.92 * c : ((1.055 * (c**(1.0 / 2.4))) - 0.055)
end
# File _plugins/gtn/hsluv.rb, line 246
def get_bounds(l)
  sub1 = ((l + 16.0)**3.0) / 1_560_896.0
  sub2 = sub1 > EPSILON ? sub1 : l / KAPPA
  ret = []

  M.each do |m1, m2, m3|
    [0, 1].each do |t|
      top1 = ((284_517.0 * m1) - (94_839.0 * m3)) * sub2
      top2 = (((838_422.0 * m3) + (769_860.0 * m2) + (731_718.0 * m1)) * l * sub2) - (769_860.0 * t * l)
      bottom = (((632_260.0 * m3) - (126_452.0 * m2)) * sub2) + (126_452.0 * t)
      ret << [top1 / bottom, top2 / bottom]
    end
  end

  ret
end
# File _plugins/gtn/hsluv.rb, line 66
def hex_to_hpluv(hex)
  rgb_to_hpluv(*hex_to_rgb(hex))
end
# File _plugins/gtn/hsluv.rb, line 62
def hex_to_hsluv(hex)
  rgb_to_hsluv(*hex_to_rgb(hex))
end
# File _plugins/gtn/hsluv.rb, line 98
def hex_to_rgb(hex)
  hex = hex.tr('#', '')
  [].tap { |arr| hex.chars.each_slice(2) { |block| arr << (block.join.to_i(16) / 255.0) } }
end
# File _plugins/gtn/hsluv.rb, line 58
def hpluv_to_hex(h, s, l)
  rgb_to_hex(*hpluv_to_rgb(h, s, l))
end
# File _plugins/gtn/hsluv.rb, line 201
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
# File _plugins/gtn/hsluv.rb, line 78
def hpluv_to_rgb(h, s, l)
  lch_to_rgb(*hpluv_to_lch([h, s, l]))
end
# File _plugins/gtn/hsluv.rb, line 54
def hsluv_to_hex(h, s, l)
  rgb_to_hex(*hsluv_to_rgb(h, s, l))
end
# File _plugins/gtn/hsluv.rb, line 189
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
# File _plugins/gtn/hsluv.rb, line 70
def hsluv_to_rgb(h, s, l)
  xyz_to_rgb(luv_to_xyz(lch_to_luv(hsluv_to_lch([h, s, l]))))
end
# File _plugins/gtn/hsluv.rb, line 271
def intersect_line_line(line1, line2)
  (line1[1] - line2[1]) / (line2[0] - line1[0])
end
# File _plugins/gtn/hsluv.rb, line 144
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
# File _plugins/gtn/hsluv.rb, line 133
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
# File _plugins/gtn/hsluv.rb, line 179
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
# File _plugins/gtn/hsluv.rb, line 86
def lch_to_rgb(l, c, h)
  xyz_to_rgb(luv_to_xyz(lch_to_luv([l, c, h])))
end
# File _plugins/gtn/hsluv.rb, line 263
def length_of_ray_until_intersect(theta, line)
  m1, b1 = line
  length = b1 / (Math.sin(theta) - (m1 * Math.cos(theta)))
  return nil if length.negative?

  length
end
# File _plugins/gtn/hsluv.rb, line 124
def luv_to_lch(arr)
  l, u, v = arr
  c = ((u**2) + (v**2))**(1 / 2.0)
  hrad = Math.atan2(v, u)
  h = radians_to_degrees(hrad)
  h += 360.0 if h < 0.0
  [l, c, h]
end
# File _plugins/gtn/hsluv.rb, line 163
def luv_to_xyz(arr)
  l, u, v = arr

  return [0.0, 0.0, 0.0] if l.zero?

  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
# File _plugins/gtn/hsluv.rb, line 223
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
# File _plugins/gtn/hsluv.rb, line 235
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
# File _plugins/gtn/hsluv.rb, line 215
def radians_to_degrees(rad)
  rad * 180.0 / Math::PI
end
# File _plugins/gtn/hsluv.rb, line 299
def rgb_prepare(arr)
  arr.map! do |ch|
    ch = ch.round(3)
    ch = [0, ch].max
    ch = [1, ch].min
    (ch * 255).round
  end
end
# File _plugins/gtn/hsluv.rb, line 94
def rgb_to_hex(r, g, b)
  '#%02x%02x%02x' % rgb_prepare([r, g, b])
end
# File _plugins/gtn/hsluv.rb, line 82
def rgb_to_hpluv(r, g, b)
  lch_to_hpluv(rgb_to_lch(r, g, b))
end
# File _plugins/gtn/hsluv.rb, line 74
def rgb_to_hsluv(r, g, b)
  lch_to_hsluv(rgb_to_lch(r, g, b))
end
# File _plugins/gtn/hsluv.rb, line 90
def rgb_to_lch(r, g, b)
  luv_to_lch(xyz_to_luv(rgb_to_xyz([r, g, b])))
end
# File _plugins/gtn/hsluv.rb, line 105
def rgb_to_xyz(arr)
  rgbl = arr.map { |val| to_linear(val) }
  M_INV.map { |i| dot_product(i, rgbl) }
end
# File _plugins/gtn/hsluv.rb, line 287
def to_linear(c)
  c > 0.04045 ? ((c + 0.055) / 1.055)**2.4 : c / 12.92
end
# File _plugins/gtn/hsluv.rb, line 110
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.zero?

  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
# File _plugins/gtn/hsluv.rb, line 158
def xyz_to_rgb(arr)
  xyz = M.map { |i| dot_product(i, arr) }
  xyz.map { |i| from_linear(i) }
end