{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
module CodeWorld.Color where
import Data.Fixed (mod')
import Data.List (unfoldr)
import System.Random (mkStdGen)
import System.Random.Shuffle (shuffle')
data Color =
RGBA !Double
!Double
!Double
!Double
deriving (Show, Eq)
type Colour = Color
pattern RGB :: Double -> Double -> Double -> Color
pattern RGB r g b = RGBA r g b 1
pattern HSL :: Double -> Double -> Double -> Color
pattern HSL h s l <- (toHSL -> Just (h, s, l))
where HSL h s l = fromHSL h s l
fence :: Double -> Double
fence = max 0 . min 1
wrapNum :: Double -> Double -> Double
wrapNum lim x = x - fromInteger (floor (x / lim)) * lim
fenceColor :: Color -> Color
fenceColor (RGBA r g b a) = RGBA (fence r) (fence g) (fence b) (fence a)
fromHSL :: Double -> Double -> Double -> Color
fromHSL (wrapNum (2 * pi) -> h) (fence -> s) (fence -> l) = RGBA r g b 1
where
m1 = l * 2 - m2
m2
| l <= 0.5 = l * (s + 1)
| otherwise = l + s - l * s
r = convert m1 m2 (h / 2 / pi + 1 / 3)
g = convert m1 m2 (h / 2 / pi)
b = convert m1 m2 (h / 2 / pi - 1 / 3)
convert m1 m2 h
| h < 0 = convert m1 m2 (h + 1)
| h > 1 = convert m1 m2 (h - 1)
| h * 6 < 1 = m1 + (m2 - m1) * h * 6
| h * 2 < 1 = m2
| h * 3 < 2 = m1 + (m2 - m1) * (2 / 3 - h) * 6
| otherwise = m1
toHSL :: Color -> Maybe (Double, Double, Double)
toHSL c@(RGBA _ _ _ 1) = Just (hue c, saturation c, luminosity c)
toHSL _ = Nothing
mixed :: [Color] -> Color
mixed colors = go 0 0 0 0 0 colors
where go rr gg bb aa n ((fenceColor -> RGBA r g b a) : cs) =
go (rr + r^2 * a) (gg + g^2 * a) (bb + b^2 * a) (aa + a) (n + 1) cs
go rr gg bb aa n []
| aa == 0 = RGBA 0 0 0 0
| otherwise = RGBA (sqrt (rr/aa)) (sqrt (gg/aa)) (sqrt (bb/aa)) (aa/n)
sameAlpha :: Color -> Color -> Color
sameAlpha (fenceColor -> RGBA r1 g1 b1 a1) (fenceColor -> RGBA r2 g2 b2 a2) =
RGBA r2 g2 b2 a1
lighter :: Double -> Color -> Color
lighter d c =
sameAlpha c $ HSL (hue c) (saturation c) (fence (luminosity c + d))
light :: Color -> Color
light = lighter 0.15
darker :: Double -> Color -> Color
darker d = lighter (-d)
dark :: Color -> Color
dark = darker 0.15
brighter :: Double -> Color -> Color
brighter d c =
sameAlpha c $ HSL (hue c) (fence (saturation c + d)) (luminosity c)
bright :: Color -> Color
bright = brighter 0.25
duller :: Double -> Color -> Color
duller d = brighter (-d)
dull :: Color -> Color
dull = duller 0.25
translucent :: Color -> Color
translucent (fenceColor -> RGBA r g b a) = RGBA r g b (a / 2)
assortedColors :: [Color]
assortedColors = [ HSL (adjusted h) 0.75 0.5 | h <- [0, 2 * pi / phi ..] ]
where
phi = (1 + sqrt 5) / 2
adjusted x = x + a0
+ a1 * sin (1*x) + b1 * cos (1*x)
+ a2 * sin (2*x) + b2 * cos (2*x)
+ a3 * sin (3*x) + b3 * cos (3*x)
+ a4 * sin (4*x) + b4 * cos (4*x)
a0 = -8.6870353473225553e-02
a1 = 8.6485747604766350e-02
b1 = -9.6564816819163041e-02
a2 = -3.0072759267059756e-03
b2 = 1.5048456422494966e-01
a3 = 9.3179137558373148e-02
b3 = 2.9002513227535595e-03
a4 = -6.6275768228887290e-03
b4 = -1.0451841243520298e-02
hue :: Color -> Double
hue (fenceColor -> RGBA r g b a)
| hi - lo < epsilon = 0
| r == hi && g >= b = (g - b) / (hi - lo) * pi / 3
| r == hi = (g - b) / (hi - lo) * pi / 3 + 2 * pi
| g == hi = (b - r) / (hi - lo) * pi / 3 + 2 / 3 * pi
| otherwise = (r - g) / (hi - lo) * pi / 3 + 4 / 3 * pi
where
hi = max r (max g b)
lo = min r (min g b)
epsilon = 0.000001
saturation :: Color -> Double
saturation (fenceColor -> RGBA r g b a)
| hi - lo < epsilon = 0
| otherwise = (hi - lo) / (1 - abs (hi + lo - 1))
where
hi = max r (max g b)
lo = min r (min g b)
epsilon = 0.000001
luminosity :: Color -> Double
luminosity (fenceColor -> RGBA r g b a) = (lo + hi) / 2
where
hi = max r (max g b)
lo = min r (min g b)
alpha :: Color -> Double
alpha (RGBA r g b a) = fence a
pattern White :: Color
pattern White = HSL 0.00 0.00 1.00
pattern Black :: Color
pattern Black = HSL 0.00 0.00 0.00
pattern Gray :: Color
pattern Gray = HSL 0.00 0.00 0.50
pattern Grey :: Color
pattern Grey = HSL 0.00 0.00 0.50
pattern Red :: Color
pattern Red = HSL 0.00 0.75 0.50
pattern Orange :: Color
pattern Orange = HSL 0.61 0.75 0.50
pattern Yellow :: Color
pattern Yellow = HSL 0.98 0.75 0.50
pattern Green :: Color
pattern Green = HSL 2.09 0.75 0.50
pattern Blue :: Color
pattern Blue = HSL 3.84 0.75 0.50
pattern Purple :: Color
pattern Purple = HSL 4.80 0.75 0.50
pattern Pink :: Color
pattern Pink = HSL 5.76 0.75 0.75
pattern Brown :: Color
pattern Brown = HSL 0.52 0.60 0.40
white, black, red, green, blue, cyan, magenta, yellow :: Color
orange, rose, chartreuse, aquamarine, violet, azure :: Color
gray, grey :: Color
white = White
black = Black
red = Red
yellow = Yellow
green = Green
blue = Blue
orange = Orange
brown = Brown
purple = Purple
pink = Pink
gray = Gray
grey = Grey
cyan = HSL (3 / 3 * pi) 0.75 0.5
magenta = HSL (5 / 3 * pi) 0.75 0.5
chartreuse = HSL (3 / 6 * pi) 0.75 0.5
aquamarine = HSL (5 / 6 * pi) 0.75 0.5
azure = HSL (7 / 6 * pi) 0.75 0.5
violet = HSL (9 / 6 * pi) 0.75 0.5
rose = HSL (11 / 6 * pi) 0.75 0.5
{-# WARNING White [ "Please use white (lower case) instead of White."
, "The value White may be removed July 2019." ] #-}
{-# WARNING Black [ "Please use black (lower case) instead of Black."
, "The value Black may be removed July 2019." ] #-}
{-# WARNING Red [ "Please use red (lower case) instead of Red."
, "The value Red may be removed July 2019." ] #-}
{-# WARNING Green [ "Please use green (lower case) instead of Green."
, "The value Green may be removed July 2019." ] #-}
{-# WARNING Blue [ "Please use blue (lower case) instead of Blue."
, "The value Blue may be removed July 2019." ] #-}
{-# WARNING Yellow [ "Please use yellow (lower case) instead of Yellow."
, "The value Yellow may be removed July 2019." ] #-}
{-# WARNING Orange [ "Please use orange (lower case) instead of Orange."
, "The value Orange may be removed July 2019." ] #-}
{-# WARNING Brown [ "Please use brown (lower case) instead of Brown."
, "The value Brown may be removed July 2019." ] #-}
{-# WARNING Purple [ "Please use purple (lower case) instead of Purple."
, "The value Purple may be removed July 2019." ] #-}
{-# WARNING Pink [ "Please use pink (lower case) instead of Pink."
, "The value Pink may be removed July 2019." ] #-}
{-# WARNING Gray [ "Please use gray (lower case) instead of Gray."
, "The value Gray may be removed July 2019." ] #-}
{-# WARNING Grey [ "Please use grey (lower case) instead of Grey."
, "The value Grey may be removed July 2019." ] #-}
{-# WARNING magenta [ "Please use the RGB function instead of magenta."
, "The variable magenta may be removed July 2020." ] #-}
{-# WARNING cyan [ "Please use the RGB function instead of cyan."
, "The variable cyan may be removed July 2020." ] #-}
{-# WARNING chartreuse [ "Please use the RGB function instead of chartreuse."
, "The variable chartreuse may be removed July 2020." ] #-}
{-# WARNING aquamarine [ "Please use the RGB function instead of aquamarine."
, "The variable aquamarine may be removed July 2020." ] #-}
{-# WARNING azure [ "Please use the RGB function instead of azure."
, "The variable azure may be removed July 2020." ] #-}
{-# WARNING rose [ "Please use the RGB function instead of rose."
, "The variable rose may be removed July 2020." ] #-}
{-# WARNING violet [ "Please use Purple instead of violet."
, "The variable violet may be removed July 2020." ] #-}