wumpus-basic-0.13.0: src/Wumpus/Basic/Graphic/TraceDrawing.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS -Wall #-}
--------------------------------------------------------------------------------
-- |
-- Module : Wumpus.Basic.Graphic.TraceDrawing
-- Copyright : (c) Stephen Tetley 2010
-- License : BSD3
--
-- Maintainer : stephen.tetley@gmail.com
-- Stability : unstable
-- Portability : GHC
--
-- Drawing with /trace/ - a Writer like monad collecting
-- intermediate graphics - and /drawing context/ - a reader monad
-- of attributes - font_face, fill_colour etc.
--
--------------------------------------------------------------------------------
module Wumpus.Basic.Graphic.TraceDrawing
(
TraceDrawing
, TraceDrawingT
, runTraceDrawing
, execTraceDrawing
, evalTraceDrawing
, runTraceDrawingT
, execTraceDrawingT
, evalTraceDrawingT
, liftToPictureU
, liftToPictureMb
, mbPictureU
, query
, draw
, xdraw
, drawi
, drawi_
, xdrawi
, xdrawi_
, node
, nodei
) where
import Wumpus.Basic.Graphic.Base
import Wumpus.Basic.Graphic.ContextFunction
import Wumpus.Basic.Graphic.DrawingContext
import Wumpus.Basic.Graphic.GraphicTypes
import Wumpus.Core -- package: wumpus-core
import Control.Applicative
import Control.Monad
import Data.Monoid
-- Note - TraceDrawing run \once\ - it is supplied with the starting
-- environment (DrawingContext) and returns a Picture.
--
-- Other Wumpus monads (e.g. Turtle) will typically be run inside
-- the TraceDrawing monad as a local effect, rather than built into a
-- transformer stack.
--
newtype TraceDrawing u a = TraceDrawing {
getTraceDrawing :: DrawingContext -> (a, HPrim u) }
newtype TraceDrawingT u m a = TraceDrawingT {
getTraceDrawingT :: DrawingContext -> m (a, HPrim u) }
type instance MonUnit (TraceDrawing u) = u
type instance MonUnit (TraceDrawingT u m) = u
-- Functor
instance Functor (TraceDrawing u) where
fmap f ma = TraceDrawing $ \ctx ->
let (a,w) = getTraceDrawing ma ctx in (f a,w)
instance Monad m => Functor (TraceDrawingT u m) where
fmap f ma = TraceDrawingT $ \ctx ->
getTraceDrawingT ma ctx >>= \(a,w) -> return (f a,w)
-- Applicative
instance Applicative (TraceDrawing u) where
pure a = TraceDrawing $ \_ -> (a, mempty)
mf <*> ma = TraceDrawing $ \ctx ->
let (f,w1) = getTraceDrawing mf ctx
(a,w2) = getTraceDrawing ma ctx
in (f a, w1 `mappend` w2)
instance Monad m => Applicative (TraceDrawingT u m) where
pure a = TraceDrawingT $ \_ -> return (a,mempty)
mf <*> ma = TraceDrawingT $ \ctx ->
getTraceDrawingT mf ctx >>= \(f,w1) ->
getTraceDrawingT ma ctx >>= \(a,w2) ->
return (f a, w1 `mappend` w2)
-- Monad
instance Monad (TraceDrawing u) where
return a = TraceDrawing $ \_ -> (a, mempty)
ma >>= k = TraceDrawing $ \ctx ->
let (a,w1) = getTraceDrawing ma ctx
(b,w2) = (getTraceDrawing . k) a ctx
in (b,w1 `mappend` w2)
instance Monad m => Monad (TraceDrawingT u m) where
return a = TraceDrawingT $ \_ -> return (a, mempty)
ma >>= k = TraceDrawingT $ \ctx ->
getTraceDrawingT ma ctx >>= \(a,w1) ->
(getTraceDrawingT . k) a ctx >>= \(b,w2) ->
return (b, w1 `mappend` w2)
-- TraceM
--
-- Note - @ state `mappend` a @ means the first expression in a
-- monadic drawing is the first element in the output file. It is
-- also \*\* at the back \*\* in the the Z-Order.
--
-- Some control over the Z-Order, possibly by adding /layers/ to
-- the drawing model would be valuable.
--
instance TraceM (TraceDrawing u) where
trace a = TraceDrawing $ \_ -> ((), a)
instance Monad m => TraceM (TraceDrawingT u m) where
trace a = TraceDrawingT $ \_ -> return ((), a)
-- DrawingCtxM
instance DrawingCtxM (TraceDrawing u) where
askDC = TraceDrawing $ \ctx -> (ctx, mempty)
localize upd ma = TraceDrawing $ \ctx -> getTraceDrawing ma (upd ctx)
instance Monad m => DrawingCtxM (TraceDrawingT u m) where
askDC = TraceDrawingT $ \ctx -> return (ctx,mempty)
localize upd ma = TraceDrawingT $ \ctx -> getTraceDrawingT ma (upd ctx)
-- Note - the result type of runTraceDrawing and friends needs more
-- thought and may change.
--
-- Possibly a wrapped HPrim that only supports concat and safe
-- extraction is best.
--
-- Or it could generate a picture, but then separate drawings
-- need the picture combinators to put them together.
--
runTraceDrawing :: DrawingContext -> TraceDrawing u a -> (a, HPrim u)
runTraceDrawing ctx ma = getTraceDrawing ma ctx
-- | Run the drawing returning only the output it produces, drop
-- any answer from the monadic computation.
--
execTraceDrawing :: DrawingContext -> TraceDrawing u a -> HPrim u
execTraceDrawing ctx ma = snd $ runTraceDrawing ctx ma
-- | Run the drawing ignoring the output it produces, return the
-- answer from the monadic computation.
--
-- Note - this useful for testing, generally one would want the
-- opposite behaviour (return the drawing, ignore than the
-- answer).
--
evalTraceDrawing :: DrawingContext -> TraceDrawing u a -> a
evalTraceDrawing ctx ma = fst $ runTraceDrawing ctx ma
runTraceDrawingT :: Monad m
=> DrawingContext -> TraceDrawingT u m a -> m (a, HPrim u)
runTraceDrawingT ctx ma = getTraceDrawingT ma ctx
execTraceDrawingT :: Monad m
=> DrawingContext -> TraceDrawingT u m a -> m (HPrim u)
execTraceDrawingT ctx ma = liftM snd $ runTraceDrawingT ctx ma
evalTraceDrawingT :: Monad m
=> DrawingContext -> TraceDrawingT u m a -> m a
evalTraceDrawingT ctx ma = liftM fst $ runTraceDrawingT ctx ma
-- | /Unsafe/ promotion of @HPrim@ to @Picture@.
--
-- If the HPrim is empty, a run-time error is thrown.
--
liftToPictureU :: (Real u, Floating u, FromPtSize u) => HPrim u -> Picture u
liftToPictureU hf =
let prims = hprimToList hf in if null prims then errK else frame prims
where
errK = error "toPictureU - empty prims list."
-- | /Safe/ promotion of @HPrim@ to @(Maybe Picture)@.
--
-- If the HPrim is empty, then @Nothing@ is returned.
--
liftToPictureMb :: (Real u, Floating u, FromPtSize u)
=> HPrim u -> Maybe (Picture u)
liftToPictureMb hf = let prims = hprimToList hf in
if null prims then Nothing else Just (frame prims)
-- | /Unsafe/ promotion of @(Maybe Picture)@ to @Picture@.
--
-- This is equivalent to:
--
-- > fromMaybe (error "empty") $ pic
--
-- This function is solely a convenience, using it saves one
-- import and a few characters.
--
-- If the supplied value is @Nothing@ a run-time error is thrown.
--
mbPictureU :: (Real u, Floating u, FromPtSize u)
=> Maybe (Picture u) -> Picture u
mbPictureU Nothing = error "mbPictureU - empty picture."
mbPictureU (Just a) = a
-- Note - need an equivalent to Parsec\`s parseTest that provides
-- a very simple way to run graphics without concern for return
-- type or initial drawing context.
--------------------------------------------------------------------------------
query :: DrawingCtxM m => CF a -> m a
query df = askDC >>= \ctx -> return $ runCF ctx df
-- | Draw a Graphic taking the drawing style from the
-- /drawing context/.
--
-- This operation is analogeous to @tell@ in a Writer monad.
--
draw :: (TraceM m, DrawingCtxM m, u ~ MonUnit m) => Graphic u -> m ()
draw gf = askDC >>= \ctx -> trace (collectH $ runGraphic ctx gf)
-- | Hyperlink version of 'draw'.
--
xdraw :: (TraceM m, DrawingCtxM m, u ~ MonUnit m)
=> XLink -> Graphic u -> m ()
xdraw xl gf = draw (xlinkGraphic xl gf)
-- | Draw an Image taking the drawing style from the
-- /drawing context/.
--
-- The graphic representation of the Image is drawn in the Trace
-- monad, and the result is returned.
--
drawi :: (TraceM m, DrawingCtxM m, u ~ MonUnit m) => Image u a -> m a
drawi img = askDC >>= \ctx ->
let (a,o) = runImage ctx img in trace (collectH o) >> return a
-- | Forgetful 'drawi'.
--
drawi_ :: (TraceM m, DrawingCtxM m, u ~ MonUnit m) => Image u a -> m ()
drawi_ img = drawi img >> return ()
-- | Hyperlink version of 'drawi'.
--
xdrawi :: (TraceM m, DrawingCtxM m, u ~ MonUnit m)
=> XLink -> Image u a -> m a
xdrawi xl img = drawi (xlinkImage xl img)
-- | Forgetful 'xdrawi'.
--
xdrawi_ :: (TraceM m, DrawingCtxM m, u ~ MonUnit m)
=> XLink -> Image u a -> m ()
xdrawi_ xl img = xdrawi xl img >> return ()
node :: (TraceM m, DrawingCtxM m, PointSupplyM m, u ~ MonUnit m)
=> LocGraphic u -> m ()
node gf = askDC >>= \ctx ->
position >>= \pt ->
let f = runCF ctx gf in trace (collectH $ f pt)
nodei :: (TraceM m, DrawingCtxM m, PointSupplyM m, u ~ MonUnit m)
=> LocImage u a -> m a
nodei imgL = askDC >>= \ctx ->
position >>= \pt ->
let (a,o) = runLocImage ctx pt imgL
in trace (collectH o) >> return a