stylix/palette-generator/Data/Colour.hs
Daniel Thwaites ba5565d698
Refactor palette generator ♻️
Simplified a lot of code which was unnecessarily generic.

Now using monads to manage the state of the random number generator
rather than passing it around by hand.

Also made some performance improvements, then increased the population
size so more combinations are tried in a similar length of time.
2023-07-08 14:28:15 +01:00

70 lines
2.6 KiB
Haskell

module Data.Colour ( LAB(..), RGB(..), deltaE, lab2rgb, rgb2lab ) where
data LAB = LAB { lightness :: Double
, channelA :: Double
, channelB :: Double
}
data RGB = RGB { red :: Double
, green :: Double
, blue :: Double
}
-- Based on https://github.com/antimatter15/rgb-lab/blob/master/color.js
deltaE :: LAB -> LAB -> Double
deltaE (LAB l1 a1 b1) (LAB l2 a2 b2) =
let deltaL = l1 - l2
deltaA = a1 - a2
deltaB = b1 - b2
c1 = sqrt $ a1^2 + b1^2
c2 = sqrt $ a2^2 + b2^2
deltaC = c1 - c2
deltaH = deltaA^2 + deltaB^2 - deltaC^2
deltaH' = if deltaH < 0 then 0 else sqrt deltaH
sc = 1 + 0.045 * c1
sh = 1 + 0.015 * c1
deltaCkcsc = deltaC / sc
deltaHkhsh = deltaH' / sh
i = deltaL^2 + deltaCkcsc^2 + deltaHkhsh^2
in if i < 0 then 0 else sqrt i
-- | Convert a 'LAB' colour to a 'RGB' colour
lab2rgb :: LAB -> RGB
lab2rgb (LAB l a bx) =
let y = (l + 16) / 116
x = a / 500 + y
z = y - bx / 200
x' = 0.95047 * (if x^3 > 0.008856 then x^3 else (x - 16/116) / 7.787)
y' = if y^3 > 0.008856 then y^3 else (y - 16/116) / 7.787
z' = 1.08883 * (if z^3 > 0.008856 then z^3 else (z - 16/116) / 7.787)
r = x' * 3.2406 + y' * (-1.5372) + z' * (-0.4986)
g = x' * (-0.9689) + y' * 1.8758 + z' * 0.0415
b = x' * 0.0557 + y' * (-0.204) + z' * 1.0570
r' = if r > 0.0031308 then 1.055 * r**(1/2.4) - 0.055 else 12.92 * r
g' = if g > 0.0031308 then 1.055 * g**(1/2.4) - 0.055 else 12.92 * g
b' = if b > 0.0031308 then 1.055 * b**(1/2.4) - 0.055 else 12.92 * b
in RGB { red = max 0 (min 1 r') * 255
, green = max 0 (min 1 g') * 255
, blue = max 0 (min 1 b') * 255
}
-- | Convert a 'RGB' colour to a 'LAB' colour
rgb2lab :: RGB -> LAB
rgb2lab (RGB r g b) =
let r' = r / 255
g' = g / 255
b' = b / 255
r'' = if r' > 0.04045 then ((r' + 0.055) / 1.055)**2.4 else r' / 12.92
g'' = if g' > 0.04045 then ((g' + 0.055) / 1.055)**2.4 else g' / 12.92
b'' = if b' > 0.04045 then ((b' + 0.055) / 1.055)**2.4 else b' / 12.92
x = (r'' * 0.4124 + g'' * 0.3576 + b'' * 0.1805) / 0.95047
y = r'' * 0.2126 + g'' * 0.7152 + b'' * 0.0722
z = (r'' * 0.0193 + g'' * 0.1192 + b'' * 0.9505) / 1.08883
x' = if x > 0.008856 then x**(1/3) else (7.787 * x) + 16/116
y' = if y > 0.008856 then y**(1/3) else (7.787 * y) + 16/116
z' = if z > 0.008856 then z**(1/3) else (7.787 * z) + 16/116
in LAB { lightness = (116 * y') - 16
, channelA = 500 * (x' - y')
, channelB = 200 * (y' - z')
}