{-# LANGUAGE LambdaCase #-}
module Main where
import qualified Data.ByteString.Lazy as BS
import Data.IORef
import Graphics.UI.Gtk.Cairo
import Graphics.UI.Gtk hiding (eventButton, eventKeyVal)
import Graphics.UI.Gtk.Gdk.Events(Event(..),eventX,eventY,eventButton)
import qualified Graphics.Rendering.Cairo as C
import qualified Image.VK as VK
data ApplicationState = ApplicationState
{ apLines :: [[(Int, Int)]]
, apCursor :: Maybe (Int, Int)
, apSize :: (Int,Int)
, apGrid :: Maybe (Int, Int)
} deriving (Eq, Show)
rnd :: Double -> Int
rnd x = ((floor x) `div` 10) * 10
dLine :: [(Int, Int)] -> C.Render ()
dLine [] = return ()
dLine [(x,y)] = do
C.moveTo (fromIntegral x) (fromIntegral y)
C.lineTo (fromIntegral x) (fromIntegral y)
C.stroke
dLine ((x,y):rs) = do
C.moveTo (fromIntegral x) (fromIntegral y)
sequence_ [ C.lineTo (fromIntegral x') (fromIntegral y')
| (x', y') <- rs
]
C.stroke
dPoints :: [(Int, Int)] -> C.Render ()
dPoints ps =
sequence_ [ C.arc (fromIntegral x) (fromIntegral y) 2.0 0.0 (2 * pi) >>
C.stroke
| (x, y) <- ps
]
render :: WidgetClass d => d -> ApplicationState -> IO ()
render d state = do
dw <- widgetGetDrawWindow d
(w, h) <- drawableGetSize dw
region <- regionRectangle (Rectangle 0 0 w h)
drawWindowBeginPaintRegion dw region
renderWithDrawable dw $ do
C.setSourceRGB 1.0 1.0 1.0
C.rectangle 0 0 (fromIntegral w) (fromIntegral h)
C.fill
case apGrid state of
Nothing -> return ()
Just (xW, yW) -> do
C.setSourceRGB 0.8 0.8 0.8
C.setLineWidth 2
C.setLineCap C.LineCapRound
sequence_ [ dot (fromIntegral (x * xW)) (fromIntegral (y * yW))
| x <- [0..(w `div` xW)]
, y <- [0..(h `div` yW)]
]
C.setSourceRGB 0.0 0.0 0.0
mapM_ dLine (apLines state)
case apLines state of
[] -> return ()
(x:xs) -> do
mapM_ dPoints xs
C.setSourceRGB 0.0 1.0 1.0
dPoints x
C.setSourceRGB 1.0 0.0 0.0
case apCursor state of
Nothing -> return ()
Just (x, y) -> do
C.arc (fromIntegral x) (fromIntegral y) 5.0 0.0 (2 * pi)
C.stroke
drawWindowEndPaint dw
dot :: Double -> Double -> C.Render ()
dot x y = do
C.moveTo x y
C.lineTo x y
C.stroke
main :: IO ()
main = do
_ <- initGUI
window <- windowNew
vbox <- vBoxNew False 10
da <- drawingAreaNew
minibuf <- labelNew (Just ":" :: Maybe String)
stRef <- newIORef (ApplicationState [[]] Nothing (800, 800) (Just (10, 10)))
set window [ windowDefaultWidth := 800
, windowDefaultHeight := 800
, containerChild := vbox
, containerBorderWidth := 10
]
boxPackStart vbox da PackGrow 0
boxPackStart vbox minibuf PackRepel 0
_ <- onMotionNotify da True $ \ event -> do
st <- readIORef stRef
let (x, y) = (rnd (eventX event), rnd (eventY event))
let st' = st { apCursor = Just (x, y) }
render da st'
writeIORef stRef st'
return True
_ <- onButtonPress da $ \ event ->
if eventButton event == RightButton
then do
st <- readIORef stRef
let st' = st { apLines = [] : apLines st }
writeIORef stRef st'
return True
else do
st <- readIORef stRef
let (x, y) = (rnd (eventX event), rnd (eventY event))
let st' = st { apLines = ((x, y) : head (apLines st)) : tail (apLines st) }
writeIORef stRef st'
render da st'
return True
_ <- onKeyPress window $ \ case
Key { eventKeyChar = Just 'c' } -> do
modifyIORef stRef (\ st -> st { apLines = [[]] })
readIORef stRef >>= render da
return True
Key { eventKeyChar = Just ':' } -> do
labelSetText minibuf ("command" :: String)
return True
_ -> return True
_ <- window `onDestroy` (saveFile stRef >> mainQuit)
widgetShowAll window
mainGUI
saveFile :: IORef ApplicationState -> IO ()
saveFile ref = do
st <- readIORef ref
let (w, h) = apSize st
let bs = VK.encode $ VK.VKImage
{ VK.vkLines = [ [ (fromIntegral x, fromIntegral y)
| (x, y) <- line
]
| line <- apLines st
]
, VK.vkMeta = VK.VKMeta (fromIntegral w, fromIntegral h)
}
BS.writeFile "/tmp/tmp.vk" bs