module System.Drawille ( empty -- drawing API , frame , get , set , unset , toggle , fromList , toPs -- utility functions , toPx , pxMap , pxOff ) where import qualified Data.Map as M (Map, empty, lookup, insertWith, keys) import Data.Bits ((.|.), (.&.), complement, xor) import Data.Char (chr) type Canvas = M.Map (Int, Int) Int pxMap :: Num a => [[a]] pxMap = [ [0x01, 0x08] , [0x02, 0x10] , [0x04, 0x20] , [0x40, 0x80] ] pxOff :: Num a => a pxOff = 0x2800 toPx :: (Int, Int) -> Int toPx (px, py) = pxMap !! mod py 4 !! mod px 2 toPs :: (Int, Int) -> (Int, Int) toPs (x, y) = (x `div` 2, y `div` 4) empty :: Canvas empty = M.empty frame :: Canvas -> String frame c = unlines $ map (row c mX) [minY..maxY] where keys = M.keys c mX = maximumMinimum $ map fst keys (maxY, minY) = maximumMinimum $ map snd keys row :: Canvas -> (Int, Int) -> Int -> String row c (maxX, minX) y = map helper vs where vs = map (\x -> M.lookup (x, y) c) [minX..maxX] helper (Just v) = chr $ v + pxOff helper Nothing = ' ' set :: Canvas -> (Int, Int) -> Canvas set c p = M.insertWith (.|.) (toPs p) (toPx p) c get :: Canvas -> (Int, Int) -> Bool get c p = case M.lookup (toPs p) c of Just x -> let px = toPx p in x .&. px == px Nothing -> False unset :: Canvas -> (Int, Int) -> Canvas unset c p = M.insertWith (.&.) (toPs p) ((complement . toPx) p) c toggle :: Canvas -> (Int, Int) -> Canvas toggle c p = M.insertWith xor (toPs p) (toPx p) c fromList :: [(Int, Int)] -> Canvas fromList = foldr (flip set) empty maximumMinimum :: Ord a => [a] -> (a, a) maximumMinimum (x:xs) = foldr maxMin (x, x) xs maximumMinimum [] = error "Empty list" maxMin :: Ord a => a -> (a, a) -> (a, a) maxMin x (b, s) = (max x b, min x s)