lib/camalian/color.rb
# frozen_string_literal: true
module Camalian
# Camalian color object
class Color
attr_reader :r, :g, :b, :h, :s, :l, :hsv
def initialize(r, g, b)
build_components(r, g, b)
end
def self.from_hex(hex_value)
color_hash = hex_value[0..6]
color_hash = color_hash[1..6] if color_hash[0] == '#'
r = color_hash[0..1].to_i(16)
g = color_hash[2..3].to_i(16)
b = color_hash[4..5].to_i(16)
Color.new(r, g, b)
end
def to_s
"red=#{r} green=#{g} blue=#{b} hue=#{h} saturation=#{s} lightness=#{l}"
end
def to_hex
"##{r.to_s(16).rjust(2, '0')}#{g.to_s(16).rjust(2, '0')}#{b.to_s(16).rjust(2, '0')}"
end
# Used for array uniqueness
def hash
"#{r}#{g}#{b}".to_i
end
# Used for object comparison
def ==(other)
r == other.r && g == other.g && b == other.b
end
alias eql? ==
def hue_distance(color)
[(h - color.h) % 360, (color.h - h) % 360].min
end
def rgb_distance(color)
Math.sqrt(((r - color.r)**2) + ((g - color.g)**2) + ((b - color.b)**2))
end
private
def build_components(r, g, b)
@r = r
@g = g
@b = b
ri = @r / 255.0
gi = @g / 255.0
bi = @b / 255.0
cmax = [ri, gi, bi].max
cmin = [ri, gi, bi].min
delta = cmax - cmin
@l = (cmax + cmin) / 2.0
if delta.zero?
@h = 0
elsif cmax == ri
@h = 60 * (((gi - bi) / delta) % 6)
elsif cmax == gi
@h = 60 * (((bi - ri) / delta) + 2)
elsif cmax == bi
@h = 60 * (((ri - gi) / delta) + 4)
end
@s = if delta.zero?
0
else
delta / (1 - (2 * @l - 1).abs)
end
@h = @h.round(2)
@s = (@s * 100).round(2)
@l = (@l * 100).round(2)
# HSV Calculation
# Hue calculation
if delta.zero?
@hsv = [0]
elsif cmax == ri
@hsv = [60 * (((gi - bi) / delta) % 6)]
elsif cmax == gi
@hsv = [60 * (((bi - ri) / delta) + 2)]
elsif cmax == bi
@hsv = [60 * (((ri - gi) / delta) + 4)]
end
# Saturation calculation
@hsv << if cmax.zero?
0
else
delta / cmax
end
# Value calculation
@hsv << cmax
@hsv = [@hsv[0].round(2), (@hsv[1] * 100).round(2), (@hsv[2] * 100).round(2)]
end
end
end