packages feed

weekdaze-0.0.0.1: src-lib/WeekDaze/Colour/HTMLColour.hs

{-
	Copyright (C) 2014-2015 Dr. Alistair Ward

	This file is part of WeekDaze.

	WeekDaze is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	WeekDaze is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with WeekDaze.  If not, see <http://www.gnu.org/licenses/>.
-}
{- |
 [@AUTHOR@]	Dr. Alistair Ward

 [@DESCRIPTION@] Exports some colour-composition functions for HTML.
-}

module WeekDaze.Colour.HTMLColour(
-- * Functions
	unitIntervalToHTMLColourCode,
	unitIntervalToRGB,
-- ** Translation
	htmlColourCodeToRGB,
	rgbUnitIntervalToHTMLColourCode
) where

import qualified	Text.Printf
import qualified	ToolShed.Data.Triple
import qualified	WeekDaze.Colour.HTMLColourCode	as Colour.HTMLColourCode
import qualified	WeekDaze.Colour.RGB		as Colour.RGB

-- | Represents the specified HTML colour-code as RGB 24-bit.
htmlColourCodeToRGB :: (Integral i, Show i) => Colour.HTMLColourCode.HTMLColourCode -> Colour.RGB.RGB i
htmlColourCodeToRGB	= Colour.RGB.fromIntegral' . Colour.HTMLColourCode.readHex . tail

-- | Represents the specified RGB as an HTML colour-code.
rgbUnitIntervalToHTMLColourCode
	:: RealFrac unitInterval
	=> Colour.RGB.RGB unitInterval	-- ^ Red, green & blue component-colours, each in the unit-interval.
	-> Colour.HTMLColourCode.HTMLColourCode
rgbUnitIntervalToHTMLColourCode rgb	= ToolShed.Data.Triple.uncurry3 (Text.Printf.printf $ Colour.HTMLColourCode.htmlColourCodePrefix : "%02x%02x%02x") $ Colour.RGB.toTriple (Colour.RGB.fromRGBUnitInterval rgb :: Colour.RGB.RGB Int)

{- |
	* Maps the specified value from the unit-interval to the corresponding spectral colour represented as RGB.

	* The mapping from the spectrum to RGB is performed by modeling the spectrum as a circular arc drawn on the colour triangle,
	of radius equal to the specified saturation, & centered on green.
	CAVEAT: this algorithm has no solid basis.

	* One may specify the angular span & angular offset of this arc, to define the values to which /0/ & /1/ map.
-}
unitIntervalToRGB :: (
	Fractional	alpha,
	Fractional	unitInterval,
	Real		alpha,
	Real		unitInterval,
	Show		alpha,
	Show		unitInterval
 )
	=> alpha	-- ^ The angular span in the range [0 .. 2 * pi radians], to which the unit-interval is mapped.
	-> alpha	-- ^ The angular offset in radians.
	-> unitInterval	-- ^ The saturation in the unit-interval; /0/ will always result in black.
	-> unitInterval	-- ^ The value in the unit-interval which is to be represented by a colour.
	-> Colour.RGB.RGB unitInterval
unitIntervalToRGB _ _ 0 _	= Colour.RGB.black
unitIntervalToRGB angularSpan angularOffset saturation x
	| angularSpan < 0 || realToFrac angularSpan > 2 * pi'	= error $ "WeekDaze.Colour.HTMLColour.unitIntervalToRGB:\tthe specified angular span must be in the range [0 .. 2 * Pi]; " ++ show angularSpan
	| saturation < 0 || saturation > 1			= error $ "WeekDaze.Colour.HTMLColour.unitIntervalToRGB:\tthe specified saturation must be in the unit-interval; " ++ show saturation
	| x < 0 || x > 1					= error $ "WeekDaze.Colour.HTMLColour.unitIntervalToRGB:\tthe specified value must be in the unit-interval; " ++ show x
	| otherwise						= let
		alpha	= realToFrac $ angularSpan * realToFrac x + angularOffset	-- Map the specified value onto a circular arc spanning the specified range of angles, offset by the specified angle.
	in (
		realToFrac . (* realToFrac saturation) . (/ 2) . (+ 1)	-- Translate & scale the result from [-1 .. 1] to [0 .. saturation].
	) `fmap` Colour.RGB.mkRGB (
		cos $ alpha + 5 * pi' / 6
	) (
		sin alpha
	) (
		cos $ alpha + pi' / 6
	)
	where
		pi' :: Double	-- Choose a working floating-point type.
		pi'	= pi

{- |
	* Calls 'unitIntervalToRGB', with default parameters to make /0/ map to /blue/ & /1/ to /red/, & with a moderately high saturation.

	* Calls 'rgbUnitIntervalToHTMLColourCode' to convert to an HTML colour-code.
-}
unitIntervalToHTMLColourCode :: (
	RealFrac	unitInterval,
	Show		unitInterval
 ) => unitInterval -> Colour.HTMLColourCode.HTMLColourCode
unitIntervalToHTMLColourCode	= rgbUnitIntervalToHTMLColourCode . unitIntervalToRGB (4 * pi / 3 :: Double) (negate pi / 6) (3 / 4) {-rather arbitrary-}