L-seed-0.1: src/Lseed/Renderer/Cairo.hs
module Lseed.Renderer.Cairo where
import Graphics.UI.Gtk hiding (fill)
import Graphics.Rendering.Cairo
import Control.Monad
import Control.Concurrent
import Data.IORef
import Data.Maybe
import Lseed.Data
import Lseed.Data.Functions
import Lseed.Constants
import Lseed.Geometry
import Text.Printf
import System.Time
cairoObserver :: IO Observer
cairoObserver = do
initGUI
-- global renderer state
currentGardenRef <- newIORef (const (ScreenContent [] (pi/2) "No time yet"))
-- widgets
canvas <- drawingAreaNew
window <- windowNew
set window [windowDefaultWidth := 800, windowDefaultHeight := 600,
containerChild := canvas, containerBorderWidth := 0]
widgetShowAll window
-- Make gtk and haskell threading compatible
timeoutAdd (yield >> return True) 50
-- a thread for our GUI
forkIO $ mainGUI
-- The actual drawing function
onExpose canvas$ \e -> do scGen <- readIORef currentGardenRef
ScreenContent garden angle timeInfo <-
scGen `fmap` getClockTime
dwin <- widgetGetDrawWindow canvas
(w,h) <- drawableGetSize dwin
renderWithDrawable dwin $ do
-- Set up coordinates
translate 0 (fromIntegral h)
scale 1 (-1)
scale (fromIntegral w) (fromIntegral (w))
translate 0 groundLevel
setLineWidth stipeWidth
render angle garden
renderTimeInfo timeInfo
return True
timeoutAdd (widgetQueueDraw canvas >> return True) 20
return $ nullObserver
{ obGrowingState = \scGen -> do
writeIORef currentGardenRef scGen
widgetQueueDraw canvas
, obFinished = \_ ->
mainQuit
}
render :: Double -> AnnotatedGarden -> Render ()
render angle garden = do
-- TODO the following can be optimized to run allKindsOfStuffWithAngle only once.
-- by running it here. This needs modification to lightenGarden and mapLine
renderGround
mapM_ renderLightedPoly (lightPolygons angle (gardenToLines garden))
--mapM_ renderLightedLine (lightenLines angle (gardenToLines garden))
--mapM_ renderLine (gardenToLines garden)
--mapM_ renderLightedPlanted garden
mapM_ renderPlanted garden
renderInfo angle garden
renderPlanted :: AnnotatedPlanted -> Render ()
renderPlanted planted = preserve $ do
translate (plantPosition planted) 0
setSourceRGB 0 0.8 0
setLineCap LineCapRound
renderPlant (phenotype planted)
renderPlant :: AnnotatedPlant -> Render ()
renderPlant (Plant si len ang ut ps) = preserve $ do
rotate ang
setLineWidth (stipeWidth*(0.5 + 0.5 * sqrt (siSubLength si)))
moveTo 0 0
lineTo 0 (len * stipeLength)
setSourceRGB 0 0.8 0
stroke
translate 0 (len * stipeLength)
mapM_ renderPlant ps
case siGrowth si of
GrowingSeed done -> do
setSourceRGB 1 1 0
arc 0 0 (done * blossomSize/2) 0 (2*pi)
fill
_ -> return ()
renderLightedPlanted :: AnnotatedPlanted -> Render ()
renderLightedPlanted planted = preserve $ do
translate (plantPosition planted) 0
renderLightedPlant (phenotype planted)
renderLightedPlant :: AnnotatedPlant -> Render ()
renderLightedPlant (Plant si len ang ut ps) = preserve $ do
rotate ang
moveTo 0 0
lineTo 0 (len * stipeLength)
let normalized = siLight si / (len * stipeLength)
when (normalized > 0) $ do
--liftIO $ print normalized
setLineWidth (2*stipeWidth)
setSourceRGBA 1 1 0 normalized
stroke
translate 0 (len * stipeLength)
mapM_ renderLightedPlant ps
{- Line based rendering deprecated
renderLine (l@((x1,y1),(x2,y2)), _) = do
setSourceRGB 0 1 0
setLineWidth (0.5*stipeWidth)
moveTo x1 y1
lineTo x2 y2
stroke
renderLightedLine (l@((x1,y1),(x2,y2)), _, intensity) = do
moveTo x1 y1
lineTo x2 y2
let normalized = intensity / lineLength l
when (normalized > 0) $ do
setLineWidth (1.5*stipeWidth)
setSourceRGBA 1 1 0 normalized
strokePreserve
setSourceRGB 0 1 0
setLineWidth (0.5*stipeWidth)
stroke
-}
renderLightedPoly ((x1,y1),(x2,y2),(x3,y3),(x4,y4), intensity) = do
when (intensity > 0) $ do
moveTo x1 y1
lineTo x2 y2
lineTo x3 y3
lineTo x4 y4
closePath
setSourceRGB 0 0 intensity
fill
renderInfo angle garden = do
forM_ garden $ \planted -> do
let x = plantPosition planted
let text1 = printf "Light: %.2f" $
siSubLight . pData . phenotype $ planted
let text2 = printf "Size: %.2f" $
siSubLength . pData . phenotype $ planted
preserve $ do
scale 1 (-1)
setSourceRGB 0 0 0
setFontSize (groundLevel/2)
moveTo x (0.9*groundLevel)
showText text1
moveTo x (0.5*groundLevel)
showText text2
renderTimeInfo timeStr = do
preserve $ do
scale 1 (-1)
setSourceRGB 0 0 0
setFontSize (groundLevel/2)
moveTo 0 (0.5*groundLevel)
showText timeStr
renderGround :: Render ()
renderGround = do
-- Clear Background
rectangle 0 0 1 100
setSourceRGB 0 0 1
fill
setSourceRGB (140/255) (80/255) (21/255)
rectangle 0 0 1 (-groundLevel)
fill
-- | Wrapper that calls 'save' and 'restore' before and after the argument
preserve :: Render () -> Render ()
preserve r = save >> r >> restore