-- Based on the algorithm from the CSS3 specification.
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
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
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