1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
{- |
Module : Data.GraphViz.Types.Monadic
Description : A monadic interface for making Dot graphs.
Copyright : (c) Ivan Lazar Miljenovic
License : 3-Clause BSD-style
Maintainer : Ivan.Miljenovic@gmail.com
This module is based upon the /dotgen/ library by Andy Gill:
<http://hackage.haskell.org/package/dotgen>
It provides a monadic interface for constructing generalised Dot
graphs. Note that this does /not/ have an instance for @DotRepr@
(e.g. what would be the point of the @fromCanonical@ function, as
you can't do anything with the result): it is purely for
construction purposes. Use the generalised Dot graph instance for
printing, etc.
Note that the generalised Dot graph types are /not/ re-exported, in
case it causes a clash with other modules you may choose to import.
The example graph in "Data.GraphViz.Types" can be written as:
> digraph (Str "G") $ do
>
> cluster (Int 0) $ do
> graphAttrs [style filled, color LightGray]
> nodeAttrs [style filled, color White]
> "a0" --> "a1"
> "a1" --> "a2"
> "a2" --> "a3"
> graphAttrs [textLabel "process #1"]
>
> cluster (Int 1) $ do
> nodeAttrs [style filled]
> "b0" --> "b1"
> "b1" --> "b2"
> "b2" --> "b3"
> graphAttrs [textLabel "process #2", color Blue]
>
> "start" --> "a0"
> "start" --> "b0"
> "a1" --> "b3"
> "b2" --> "a3"
> "a3" --> "end"
> "b3" --> "end"
>
> node "start" [shape MDiamond]
> node "end" [shape MSquare]
-}
module Data.GraphViz.Types.Monadic
( Dot
, DotM
, GraphID(..)
-- * Creating a generalised DotGraph.
, digraph
, digraph'
, graph
, graph'
-- * Adding global attributes.
, graphAttrs
, nodeAttrs
, edgeAttrs
-- * Adding items to the graph.
-- ** Clusters
, cluster
-- ** Nodes
, node
, node'
-- ** Edges
, edge
, (-->)
, (<->)
) where
import Data.GraphViz.Attributes (Attributes)
import Data.GraphViz.Types.Generalised
import Control.Applicative (Applicative (..))
import Data.DList (DList)
import qualified Data.DList as DL
import qualified Data.Sequence as Seq
-- -----------------------------------------------------------------------------
-- The Dot monad.
-- | The monadic representation of a Dot graph.
type Dot n = DotM n ()
-- | The actual monad; as with 'Dot' but allows you to return a value
-- within the do-block. The actual implementation is based upon the
-- Writer monad.
newtype DotM n a = DotM { runDot :: (a, DotStmts n) }
execDot :: DotM n a -> DotStmts n
execDot = snd . runDot
instance Functor (DotM n) where
fmap f (DotM (a,stmts)) = DotM (f a, stmts)
instance Applicative (DotM n) where
pure = DotM . flip (,) DL.empty
(DotM (f,stmts1)) <*> (DotM (a,stmts2)) = DotM (f a, stmts1 `DL.append` stmts2)
instance Monad (DotM n) where
return = pure
dt >>= f = DotM
$ let ~(a,stmts) = runDot dt
~(b,stmts') = runDot $ f a
in (b, stmts `DL.append` stmts')
tell :: DotStmts n -> Dot n
tell = DotM . (,) ()
tellStmt :: DotStmt n -> Dot n
tellStmt = tell . DL.singleton
-- -----------------------------------------------------------------------------
-- Creating the DotGraph
-- | Create a directed dot graph with the specified graph ID.
digraph :: GraphID -> DotM n a -> DotGraph n
digraph = mkGraph True . Just
-- | Create a directed dot graph with no graph ID.
digraph' :: DotM n a -> DotGraph n
digraph' = mkGraph True Nothing
-- | Create a undirected dot graph with the specified graph ID.
graph :: GraphID -> DotM n a -> DotGraph n
graph = mkGraph False . Just
-- | Create a undirected dot graph with no graph ID.
graph' :: DotM n a -> DotGraph n
graph' = mkGraph False Nothing
mkGraph :: Bool -> Maybe GraphID -> DotM n a -> DotGraph n
mkGraph isDir mid dot = DotGraph { strictGraph = False
, directedGraph = isDir
, graphID = mid
, graphStatements = execStmts dot
}
-- -----------------------------------------------------------------------------
-- Statements
type DotStmts n = DList (DotStmt n)
execStmts :: DotM n a -> DotStatements n
execStmts = convertStatements . execDot
convertStatements :: DotStmts n -> DotStatements n
convertStatements = Seq.fromList . map convertStatement . DL.toList
data DotStmt n = MA GlobalAttributes
| MC (Cluster n)
| MN (DotNode n)
| ME (DotEdge n)
convertStatement :: DotStmt n -> DotStatement n
convertStatement (MA gas) = GA gas
convertStatement (MC cl) = SG . DotSG True (Just $ clID cl)
. execStmts $ clStmts cl
convertStatement (MN dn) = DN dn
convertStatement (ME de) = DE de
-- -----------------------------------------------------------------------------
-- Global Attributes
-- | Add graph/sub-graph/cluster attributes.
graphAttrs :: Attributes -> Dot n
graphAttrs = tellStmt . MA . GraphAttrs
-- | Add global node attributes.
nodeAttrs :: Attributes -> Dot n
nodeAttrs = tellStmt . MA . NodeAttrs
-- | Add global edge attributes
edgeAttrs :: Attributes -> Dot n
edgeAttrs = tellStmt . MA . EdgeAttrs
-- -----------------------------------------------------------------------------
-- Clusters
data Cluster n = Cl { clID :: GraphID
, clStmts :: Dot n
}
-- | Add a named cluster to the graph.
cluster :: GraphID -> DotM n a -> Dot n
cluster cid = tellStmt . MC . Cl cid . (>> return ())
-- -----------------------------------------------------------------------------
-- Nodes
-- | Add a node to the graph.
node :: n -> Attributes -> Dot n
node n = tellStmt . MN . DotNode n
-- | Add a node with no attributes to the graph.
node' :: n -> Dot n
node' = (`node` [])
-- -----------------------------------------------------------------------------
-- Edges
-- | Add an edge to the graph.
edge :: n -> n -> Attributes -> Dot n
edge f t = tellStmt . ME . DotEdge f t
-- | Add an edge with no attributes.
(-->) :: n -> n -> Dot n
f --> t = edge f t []
infixr 9 -->
-- | An alias for '-->' to make edges look more undirected.
(<->) :: n -> n -> Dot n
(<->) = (-->)
infixr 9 <->
-- -----------------------------------------------------------------------------
|