{-# OPTIONS -Wall #-} -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Graphic.Image -- Copyright : (c) Stephen Tetley 2010 -- License : BSD3 -- -- Maintainer : stephen.tetley@gmail.com -- Stability : highly unstable -- Portability : GHC -- -- Base types for Drawing Objects, Graphics / Images (a Graphic -- that also returns an answer), etc. -- -- \*\* WARNING \*\* - some names are expected to change -- particularly the naming of the /append/ and /concat/ functions. -- -------------------------------------------------------------------------------- module Wumpus.Basic.Graphic.BaseTypes ( HPrim , hprimToList , singleH , Point2F , DPoint2F , DrawingF , LocDrawingF , DLocDrawingF , runDF , pureDF , askDF , asksDF , localDF , Graphic , DGraphic , runGraphic , xlinkGraphic , LocGraphic , DLocGraphic , localLG , lgappend , Image , DImage , LocImage , DLocImage , runImage , intoImage , intoLocImage , xlinkImage , ConnDrawingF , DConnDrawingF , ConnGraphic , DConnGraphic , ConnImage , DConnImage , intoConnImage ) where import Wumpus.Basic.Graphic.DrawingContext import Wumpus.Basic.Utils.HList import Wumpus.Core -- package: wumpus-core import Control.Applicative import Data.Monoid -- | Graphics objects, even simple ones (line, arrow, dot) might -- need more than one primitive (path or text label) for their -- construction. Hence, the primary representation that all the -- others are built upon must support /concatenation/ of -- primitives. -- -- Wumpus-Core has a type Picture - made from one or more -- Primitives - but Pictures include support for affine frames. -- For drawing many simple graphics (dots, connector lines...) -- that do not need individual affine transformations this is a -- penalty. A list of Primitives is therefore more suitable -- representation, and a Hughes list which supports -- efficient concatenation is wise. -- newtype HPrim u = HPrim { getHPrim :: H (PrimElement u) } -- Note - only a Monoid instance for HPrim - they cannot be -- shown, fmapped etc. instance Monoid (HPrim u) where mempty = HPrim emptyH ha `mappend` hb = HPrim $ getHPrim ha `appendH` getHPrim hb hprimToList :: HPrim u -> [PrimElement u] hprimToList = toListH . getHPrim singleH :: PrimElement u -> HPrim u singleH = HPrim . wrapH -- | Point transformation function. -- type Point2F u = Point2 u -> Point2 u type DPoint2F = Point2F Double -------------------------------------------------------------------------------- -- -- | Drawings in Wumpus-Basic have an implicit /graphics state/ -- the @DrawingContext@, the most primitive building block is -- a function from the DrawingContext to some polymorphic answer. -- -- This functional type is represented concretely as @DrawingF@. -- -- > DrawingF :: DrawingContext -> a -- newtype DrawingF a = DrawingF { getDrawingF :: DrawingContext -> a } instance Functor DrawingF where fmap f ma = DrawingF $ \ctx -> f $ getDrawingF ma ctx -- The monoid instance seems sensible... -- instance Monoid a => Monoid (DrawingF a) where mempty = DrawingF $ \_ -> mempty fa `mappend` fb = DrawingF $ \ctx -> getDrawingF fa ctx `mappend` getDrawingF fb ctx -- Applicative instance Applicative DrawingF where pure a = DrawingF $ \_ -> a mf <*> ma = DrawingF $ \ctx -> let f = getDrawingF mf ctx a = getDrawingF ma ctx in f a -- Monad instance Monad DrawingF where return a = DrawingF $ \_ -> a ma >>= k = DrawingF $ \ctx -> let a = getDrawingF ma ctx in (getDrawingF . k) a ctx -- | Run a /Drawing Function/ with the supplied /Drawing Context/. -- runDF :: DrawingContext -> DrawingF a -> a runDF ctx df = getDrawingF df ctx -- | Wrap a value into a DrawingF. -- -- Note the value is /pure/ it does depend on the DrawingContext -- (it is /context free/). -- pureDF :: a -> DrawingF a pureDF a = DrawingF $ \ _ctx -> a askDF :: DrawingF DrawingContext askDF = DrawingF id asksDF :: (DrawingContext -> a) -> DrawingF a asksDF fn = DrawingF $ \ctx -> fn ctx localDF :: (DrawingContext -> DrawingContext) -> DrawingF a -> DrawingF a localDF upd gf = DrawingF $ \ctx -> getDrawingF gf (upd ctx) type LocDrawingF u a = Point2 u -> DrawingF a type DLocDrawingF a = LocDrawingF Double a -------------------------------------------------------------------------------- -- Simple drawing - representing one or more prims type Graphic u = DrawingF (HPrim u) type DGraphic = Graphic Double runGraphic :: DrawingContext -> Graphic u -> HPrim u runGraphic ctx gf = (getDrawingF gf) ctx xlinkGraphic :: XLink -> Graphic u -> Graphic u xlinkGraphic xlink gf = askDF >>= \ctx -> let xs = hprimToList $ runGraphic ctx gf in pureDF (singleH $ xlinkGroup xlink xs) -------------------------------------------------------------------------------- -- | Commonly graphics take a start point as well as a drawing -- context. -- -- Here they are called a LocGraphic - graphic with a (starting) -- location. -- type LocGraphic u = Point2 u -> Graphic u type DLocGraphic = LocGraphic Double localLG :: (DrawingContext -> DrawingContext) -> LocGraphic u -> LocGraphic u localLG upd img = \pt -> localDF upd (img pt) -- | Composition operator for LocGraphic - both LocGraphics -- are drawn at the same origin and the results concatenated. -- -- lgappend :: LocGraphic u -> LocGraphic u -> LocGraphic u lgappend f g = \pt -> f pt `mappend` g pt -------------------------------------------------------------------------------- -- | Images return a value as well as drawing. A /node/ is a -- typical example - nodes are drawing but the also support -- taking anchor points. -- type Image u a = DrawingF (a, HPrim u) type DImage a = Image Double a type LocImage u a = Point2 u -> Image u a type DLocImage a = LocImage Double a runImage :: DrawingContext -> Image u a -> (a,HPrim u) runImage ctx img = (getDrawingF img) ctx intoImage :: DrawingF a -> Graphic u -> Image u a intoImage f g = DrawingF $ \ctx -> let a = getDrawingF f ctx; o = getDrawingF g ctx in (a,o) intoLocImage :: LocDrawingF u a -> LocGraphic u -> LocImage u a intoLocImage f g pt = DrawingF $ \ctx -> let a = getDrawingF (f pt) ctx; o = getDrawingF (g pt) ctx in (a,o) xlinkImage :: XLink -> Image u a -> Image u a xlinkImage xlink img = askDF >>= \ctx -> let (a,hp) = runImage ctx img in pureDF (a, singleH $ xlinkGroup xlink $ hprimToList hp) -------------------------------------------------------------------------------- -- type ConnDrawingF u a = Point2 u -> Point2 u -> DrawingF a type DConnDrawingF a = ConnDrawingF Double a -- | ConnGraphic is a connector drawn between two points -- contructing a Graphic. -- type ConnGraphic u = Point2 u -> Point2 u -> Graphic u type DConnGraphic = ConnGraphic Double -- | ConImage is a connector drawn between two points -- constructing an Image. -- type ConnImage u a = Point2 u -> Point2 u -> Image u a type DConnImage a = ConnImage Double a intoConnImage :: ConnDrawingF u a -> ConnGraphic u -> ConnImage u a intoConnImage f g p1 p2 = DrawingF $ \ctx -> let a = getDrawingF (f p1 p2) ctx; o = getDrawingF (g p1 p2) ctx in (a,o)