module Language.Piet.Types
(
DirectionPointer(..), addCoordinates, rotate
, CodelChooser(..), toggle
, PietType(..)
, Program(..), isBlocked
, Colour(..), rgba2Colour, rgb2Colour
, HueColour(..), hueChange
, Lightness(..), lightnessChange
, Image, imgWidth, imgHeight, imgInRange, imgNew, imgPixel, imgSetPixel
, LabelKey, LabelInfo(..), labelSize, addPixel, LabelBorder(..)
) where
import Data.Array.IArray
import Data.IntMap hiding ((!))
import Data.Monoid
import Data.Ord
data DirectionPointer
= DPRight
| DPDown
| DPLeft
| DPUp
deriving (Show, Read, Eq, Ord, Enum)
addCoordinates :: DirectionPointer
-> Int
-> Int
-> (Int, Int)
addCoordinates DPRight x y = (x + 1, y)
addCoordinates DPDown x y = (x, y + 1)
addCoordinates DPLeft x y = (x 1, y)
addCoordinates DPUp x y = (x, y 1)
rotate :: Int -> DirectionPointer -> DirectionPointer
rotate n dp = let n' = (n + fromEnum dp) `rem` 4
in toEnum $ if n' < 0 then n' + 4 else n'
data CodelChooser
= CCLeft
| CCRight
deriving (Show, Read, Eq, Ord, Enum)
toggle :: Int -> CodelChooser -> CodelChooser
toggle n cc = let n' = (n + fromEnum cc) `rem` 2
in toEnum $ if n' < 0 then n' + 2 else n'
data PietType
= PietNumber
| PietChar
deriving (Show, Read, Eq, Ord)
data Program = Program
{ image :: Image Colour
, mask :: Image LabelKey
, info :: IntMap LabelInfo
}
isBlocked :: Int -> Int -> Program -> Bool
isBlocked x y program = (not (imgInRange x y (image program)))
|| (Black == imgPixel x y (image program))
data Colour
= Black
| White
| Hue !Lightness !HueColour
deriving (Show, Read, Eq, Ord)
rgba2Colour :: Num w => w
-> w
-> w
-> w
-> Colour
rgba2Colour r g b _ = rgb2Colour r g b
rgb2Colour :: Num w => w
-> w
-> w
-> Colour
rgb2Colour 0x00 0x00 0x00 = Black
rgb2Colour 0xff 0xff 0xff = White
rgb2Colour 0xff 0xc0 0xc0 = Hue Light Red
rgb2Colour 0xff 0x00 0x00 = Hue Normal Red
rgb2Colour 0xc0 0x00 0x00 = Hue Dark Red
rgb2Colour 0xff 0xff 0xc0 = Hue Light Yellow
rgb2Colour 0xff 0xff 0x00 = Hue Normal Yellow
rgb2Colour 0xc0 0xc0 0x00 = Hue Dark Yellow
rgb2Colour 0xc0 0xff 0xc0 = Hue Light Green
rgb2Colour 0x00 0xff 0x00 = Hue Normal Green
rgb2Colour 0x00 0xc0 0x00 = Hue Dark Green
rgb2Colour 0xc0 0xff 0xff = Hue Light Cyan
rgb2Colour 0x00 0xff 0xff = Hue Normal Cyan
rgb2Colour 0x00 0xc0 0xc0 = Hue Dark Cyan
rgb2Colour 0xc0 0xc0 0xff = Hue Light Blue
rgb2Colour 0x00 0x00 0xff = Hue Normal Blue
rgb2Colour 0x00 0x00 0xc0 = Hue Dark Blue
rgb2Colour 0xff 0xc0 0xff = Hue Light Magenta
rgb2Colour 0xff 0x00 0xff = Hue Normal Magenta
rgb2Colour 0xc0 0x00 0xc0 = Hue Dark Magenta
rgb2Colour _ _ _ = White
data HueColour
= Red
| Yellow
| Green
| Cyan
| Blue
| Magenta
deriving (Show, Read, Eq, Ord, Enum)
hueChange :: HueColour -> HueColour -> HueColour
hueChange c1 c2 = toEnum $ (fromEnum c2 fromEnum c1) `mod` 6
data Lightness
= Light
| Normal
| Dark
deriving (Show, Read, Eq, Ord, Enum)
lightnessChange :: Lightness -> Lightness -> Lightness
lightnessChange l1 l2 = toEnum $ (fromEnum l2 fromEnum l1) `mod` 3
data Image a = Image
{ imgWidth :: !Int
, imgHeight :: !Int
, imgPixels :: !(Array (Int, Int) a)
}
#ifndef __HADDOCK__
deriving (Show, Eq, Ord)
#else
instance (Show a) => Show (Image a)
instance (Eq a) => Eq (Image a)
instance (Ord a) => Ord (Image a)
#endif
instance Functor Image where
fmap f img = img { imgPixels = amap f (imgPixels img) }
imgNew :: Int
-> Int
-> [((Int, Int), a)]
-> Image a
imgNew width height entries = Image
{ imgWidth = width
, imgHeight = height
, imgPixels = array ((0, 0), (width 1, height 1)) entries
}
imgInRange :: Int
-> Int
-> Image a
-> Bool
imgInRange x y img = 0 <= x && x < imgWidth img && 0 <= y && y < imgHeight img
imgPixel :: Int
-> Int
-> Image a -> a
imgPixel x y img = (imgPixels img) ! (x, y)
imgSetPixel :: Int -> Int -> a -> Image a -> Image a
imgSetPixel x y pixel img = img { imgPixels = (imgPixels img) // [((x, y), pixel)] }
type LabelKey = Int
data LabelInfo
= EmptyInfo
| LabelInfo
{ _labelSize :: !Int
, labelTop :: !LabelBorder
, labelLeft :: !LabelBorder
, labelBottom :: !LabelBorder
, labelRight :: !LabelBorder
}
deriving (Show, Eq, Ord)
labelSize :: LabelInfo -> Int
labelSize EmptyInfo = 0
labelSize _info@(LabelInfo { }) = _labelSize _info
instance Monoid LabelInfo where
mempty = EmptyInfo
mappend EmptyInfo i = i
mappend i EmptyInfo = i
mappend i1 i2 = LabelInfo
{ _labelSize = labelSize i1 + labelSize i2
, labelTop = mergeMin (labelTop i1) (labelTop i2)
, labelLeft = mergeMin (labelLeft i1) (labelLeft i2)
, labelBottom = mergeMax (labelBottom i1) (labelBottom i2)
, labelRight = mergeMax (labelRight i1) (labelRight i2)
}
data LabelBorder = LabelBorder
{ borderCoord :: !Int
, borderMin :: !Int
, borderMax :: !Int
} deriving (Show, Eq, Ord)
mergeMin :: LabelBorder -> LabelBorder -> LabelBorder
mergeMin = merge (comparing borderCoord)
mergeMax :: LabelBorder -> LabelBorder -> LabelBorder
mergeMax = merge (comparing (negate . borderCoord))
merge :: (LabelBorder -> LabelBorder -> Ordering) -> LabelBorder -> LabelBorder -> LabelBorder
merge comp b1 b2 = case comp b1 b2 of
EQ -> b1
{ borderMin = min (borderMin b1) (borderMin b2)
, borderMax = max (borderMax b1) (borderMax b2)
}
LT -> b1
GT -> b2
addPixel :: Int -> Int -> LabelInfo -> LabelInfo
addPixel x y EmptyInfo = LabelInfo
{ _labelSize = 1
, labelTop = LabelBorder y x x
, labelLeft = LabelBorder x y y
, labelBottom = LabelBorder y x x
, labelRight = LabelBorder x y y
}
addPixel x y nonEmpty = nonEmpty
{ _labelSize = 1 + labelSize nonEmpty
, labelTop = mergeMin (labelTop nonEmpty) (LabelBorder y x x)
, labelLeft = mergeMin (labelLeft nonEmpty) (LabelBorder x y y)
, labelBottom = mergeMax (labelBottom nonEmpty) (LabelBorder y x x)
, labelRight = mergeMax (labelRight nonEmpty) (LabelBorder x y y)
}