packages feed

graphviz 2008.9.20 → 2009.5.1

raw patch · 8 files changed

+614/−285 lines, 8 filesdep +arraydep ~basedep ~polyparsenew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: array

Dependency ranges changed: base, polyparse

API changes (from Hackage documentation)

- Data.GraphViz: DotCluster :: String -> [Attribute] -> [DotNode] -> DotNode
- Data.GraphViz: DotEdge :: Int -> Int -> [Attribute] -> Bool -> DotEdge
- Data.GraphViz: DotGraph :: [Attribute] -> [DotNode] -> [DotEdge] -> Bool -> DotGraph
- Data.GraphViz: DotNode :: Int -> [Attribute] -> DotNode
- Data.GraphViz: clusterAttributes :: DotNode -> [Attribute]
- Data.GraphViz: clusterElems :: DotNode -> [DotNode]
- Data.GraphViz: clusterID :: DotNode -> String
- Data.GraphViz: commandFor :: DotGraph -> String
- Data.GraphViz: data DotEdge
- Data.GraphViz: data DotGraph
- Data.GraphViz: data DotNode
- Data.GraphViz: directedEdge :: DotEdge -> Bool
- Data.GraphViz: directedGraph :: DotGraph -> Bool
- Data.GraphViz: edgeAttributes :: DotEdge -> [Attribute]
- Data.GraphViz: edgeHeadNodeID :: DotEdge -> Int
- Data.GraphViz: edgeTailNodeID :: DotEdge -> Int
- Data.GraphViz: graphAttributes :: DotGraph -> [Attribute]
- Data.GraphViz: graphEdges :: DotGraph -> [DotEdge]
- Data.GraphViz: graphNodes :: DotGraph -> [DotNode]
- Data.GraphViz: instance (Show c, Show a) => Show (ClusterTree c a)
- Data.GraphViz: instance (Show c, Show a) => Show (NodeCluster c a)
- Data.GraphViz: instance Show DotEdge
- Data.GraphViz: instance Show DotGraph
- Data.GraphViz: instance Show DotNode
- Data.GraphViz: nodeAttributes :: DotNode -> [Attribute]
- Data.GraphViz: nodeID :: DotNode -> Int
- Data.GraphViz: readDotGraph :: Parser Char DotGraph
- Data.GraphViz.ParserCombinators: char :: Char -> Parser Char Char
- Data.GraphViz.ParserCombinators: digit :: Parser Char Char
- Data.GraphViz.ParserCombinators: floatingNumber :: (Floating a, Read a) => Parser Char a
- Data.GraphViz.ParserCombinators: newline :: Parser Char String
- Data.GraphViz.ParserCombinators: noneOf :: (Eq a) => [a] -> Parser a a
- Data.GraphViz.ParserCombinators: number :: (Num a, Read a) => Parser Char a
- Data.GraphViz.ParserCombinators: optionalQuoted :: Parser Char a -> Parser Char a
- Data.GraphViz.ParserCombinators: optionalQuotedString :: String -> Parser Char String
- Data.GraphViz.ParserCombinators: skipToNewline :: Parser Char ()
- Data.GraphViz.ParserCombinators: string :: String -> Parser Char String
- Data.GraphViz.ParserCombinators: strings :: [String] -> Parser Char String
- Data.GraphViz.ParserCombinators: whitespace :: Parser Char String
+ Data.GraphViz: dotizeGraph :: (DynGraph gr, Ord b) => gr a b -> gr (AttributeNode a) (AttributeEdge b)
+ Data.GraphViz.Commands: Canon :: GraphvizOutput
+ Data.GraphViz.Commands: Circo :: GraphvizCommand
+ Data.GraphViz.Commands: Cmap :: GraphvizOutput
+ Data.GraphViz.Commands: Cmapx :: GraphvizOutput
+ Data.GraphViz.Commands: Cmapx_np :: GraphvizOutput
+ Data.GraphViz.Commands: Dia :: GraphvizOutput
+ Data.GraphViz.Commands: DotCmd :: GraphvizCommand
+ Data.GraphViz.Commands: DotOutput :: GraphvizOutput
+ Data.GraphViz.Commands: Eps :: GraphvizOutput
+ Data.GraphViz.Commands: Fdp :: GraphvizCommand
+ Data.GraphViz.Commands: Fig :: GraphvizOutput
+ Data.GraphViz.Commands: Gd :: GraphvizOutput
+ Data.GraphViz.Commands: Gd2 :: GraphvizOutput
+ Data.GraphViz.Commands: Gif :: GraphvizOutput
+ Data.GraphViz.Commands: Gtk :: GraphvizOutput
+ Data.GraphViz.Commands: Hpgl :: GraphvizOutput
+ Data.GraphViz.Commands: Imap :: GraphvizOutput
+ Data.GraphViz.Commands: Imap_np :: GraphvizOutput
+ Data.GraphViz.Commands: Ismap :: GraphvizOutput
+ Data.GraphViz.Commands: Jpe :: GraphvizOutput
+ Data.GraphViz.Commands: Jpeg :: GraphvizOutput
+ Data.GraphViz.Commands: Jpg :: GraphvizOutput
+ Data.GraphViz.Commands: Mif :: GraphvizOutput
+ Data.GraphViz.Commands: Mp :: GraphvizOutput
+ Data.GraphViz.Commands: Neato :: GraphvizCommand
+ Data.GraphViz.Commands: Pcl :: GraphvizOutput
+ Data.GraphViz.Commands: Pdf :: GraphvizOutput
+ Data.GraphViz.Commands: Pic :: GraphvizOutput
+ Data.GraphViz.Commands: Plain :: GraphvizOutput
+ Data.GraphViz.Commands: PlainExt :: GraphvizOutput
+ Data.GraphViz.Commands: Png :: GraphvizOutput
+ Data.GraphViz.Commands: Ps :: GraphvizOutput
+ Data.GraphViz.Commands: Ps2 :: GraphvizOutput
+ Data.GraphViz.Commands: Svg :: GraphvizOutput
+ Data.GraphViz.Commands: Svgz :: GraphvizOutput
+ Data.GraphViz.Commands: Tk :: GraphvizOutput
+ Data.GraphViz.Commands: TwoPi :: GraphvizCommand
+ Data.GraphViz.Commands: Vml :: GraphvizOutput
+ Data.GraphViz.Commands: Vmlz :: GraphvizOutput
+ Data.GraphViz.Commands: Vrml :: GraphvizOutput
+ Data.GraphViz.Commands: Vtx :: GraphvizOutput
+ Data.GraphViz.Commands: Wbmp :: GraphvizOutput
+ Data.GraphViz.Commands: Xdot :: GraphvizOutput
+ Data.GraphViz.Commands: Xlib :: GraphvizOutput
+ Data.GraphViz.Commands: commandFor :: DotGraph -> GraphvizCommand
+ Data.GraphViz.Commands: data GraphvizCommand
+ Data.GraphViz.Commands: data GraphvizOutput
+ Data.GraphViz.Commands: dirCommand :: GraphvizCommand
+ Data.GraphViz.Commands: graphvizWithHandle :: (Show a) => GraphvizCommand -> DotGraph -> GraphvizOutput -> (Handle -> IO a) -> IO (Maybe a)
+ Data.GraphViz.Commands: instance Show GraphvizCommand
+ Data.GraphViz.Commands: instance Show GraphvizOutput
+ Data.GraphViz.Commands: runGraphviz :: DotGraph -> GraphvizOutput -> FilePath -> IO Bool
+ Data.GraphViz.Commands: runGraphvizCommand :: GraphvizCommand -> DotGraph -> GraphvizOutput -> FilePath -> IO Bool
+ Data.GraphViz.Commands: undirCommand :: GraphvizCommand
+ Data.GraphViz.Types: DotCluster :: String -> [Attribute] -> [DotNode] -> DotNode
+ Data.GraphViz.Types: DotEdge :: Int -> Int -> [Attribute] -> Bool -> DotEdge
+ Data.GraphViz.Types: DotGraph :: [Attribute] -> [DotNode] -> [DotEdge] -> Bool -> DotGraph
+ Data.GraphViz.Types: DotNode :: Int -> [Attribute] -> DotNode
+ Data.GraphViz.Types: clusterAttributes :: DotNode -> [Attribute]
+ Data.GraphViz.Types: clusterElems :: DotNode -> [DotNode]
+ Data.GraphViz.Types: clusterID :: DotNode -> String
+ Data.GraphViz.Types: data DotEdge
+ Data.GraphViz.Types: data DotGraph
+ Data.GraphViz.Types: data DotNode
+ Data.GraphViz.Types: directedEdge :: DotEdge -> Bool
+ Data.GraphViz.Types: directedGraph :: DotGraph -> Bool
+ Data.GraphViz.Types: edgeAttributes :: DotEdge -> [Attribute]
+ Data.GraphViz.Types: edgeHeadNodeID :: DotEdge -> Int
+ Data.GraphViz.Types: edgeTailNodeID :: DotEdge -> Int
+ Data.GraphViz.Types: graphAttributes :: DotGraph -> [Attribute]
+ Data.GraphViz.Types: graphEdges :: DotGraph -> [DotEdge]
+ Data.GraphViz.Types: graphNodes :: DotGraph -> [DotNode]
+ Data.GraphViz.Types: instance Show DotEdge
+ Data.GraphViz.Types: instance Show DotGraph
+ Data.GraphViz.Types: instance Show DotNode
+ Data.GraphViz.Types: nodeAttributes :: DotNode -> [Attribute]
+ Data.GraphViz.Types: nodeID :: DotNode -> Int
+ Data.GraphViz.Types: readDotGraph :: Parser Char DotGraph
- Data.GraphViz: C :: c -> NodeCluster c a -> NodeCluster c a
+ Data.GraphViz: C :: c -> (NodeCluster c a) -> NodeCluster c a
- Data.GraphViz: N :: LNode a -> NodeCluster c a
+ Data.GraphViz: N :: (LNode a) -> NodeCluster c a
- Data.GraphViz.Attributes: Splines :: Maybe Bool -> Attribute
+ Data.GraphViz.Attributes: Splines :: (Maybe Bool) -> Attribute

Files

Data/GraphViz.hs view
@@ -1,176 +1,62 @@-{-# LANGUAGE RecordPuns+{-# LANGUAGE NamedFieldPuns            , ScopedTypeVariables            #-} - {--GraphViz ------------------------------------------------------\- |                                                                 |- | Copyright (c) 2008, Matthew Sackman (matthew@wellquite.org),    |- |              Ivan Lazar Miljenovic (ivan.miljenovic@gmail.com)  |- |                                                                 |- | GraphViz is freely distributable under the terms of a 3-Clause  |- | BSD-style license.                                              |- |                                                                 |- \-----------------------------------------------------------------}+{- |+   Module      : Data.GraphViz+   Description : GraphViz bindings for Haskell.+   Copyright   : (c) Matthew Sackman, Ivan Lazar Miljenovic+   License     : 3-Clause BSD-style+   Maintainer  : Ivan.Miljenovic@gmail.com +   This is the top-level module for the graphviz library.  It provides+   functions to convert 'Data.Graph.Inductive.Graph.Graph's into+   the /Dot/ language used by the /GraphViz/ program (as well as a+   limited ability to perform the reverse operation).++   Information about GraphViz and the Dot language can be found at:+   <http://graphviz.org/>++   Note that this module re-exports the "Data.GraphViz.Attributes"+   module, which exports a constructor that clashes with+   'Prelude.LT'.  As such, you may need to import either this module+   or the @Prelude@ qualified or hiding @LT@.++   -}+ module Data.GraphViz     ( graphToDot     , clusterGraphToDot     , graphToGraph-    , readDotGraph-    , commandFor-    , DotGraph (..)-    , DotNode (..)-    , DotEdge (..)+    , dotizeGraph     , NodeCluster(..)     , AttributeNode     , AttributeEdge+    , module Data.GraphViz.Types     , module Data.GraphViz.Attributes+    , module Data.GraphViz.Commands     )     where --- LT is defined in Attributes-import Prelude hiding (LT)-import qualified Prelude as P- import Data.Graph.Inductive.Graph import Data.List import Data.Function import qualified Data.Set as Set-import Text.ParserCombinators.PolyLazy-import System.IO-import System.Process-import Control.Concurrent+import Text.ParserCombinators.Poly.Lazy import Control.Monad import Data.Maybe import qualified Data.Map as Map+import System.IO+import System.IO.Unsafe(unsafePerformIO) +import Data.GraphViz.Types+import Data.GraphViz.Types.Clustering import Data.GraphViz.Attributes-import Data.GraphViz.ParserCombinators--data DotGraph = DotGraph { graphAttributes :: [Attribute]-                         , graphNodes :: [DotNode]-                         , graphEdges :: [DotEdge]-                         , directedGraph :: Bool-                         }--data DotNode-    = DotNode { nodeID :: Int-              , nodeAttributes :: [Attribute]-              }-    | DotCluster { clusterID         :: String-                 , clusterAttributes :: [Attribute]-                 , clusterElems      :: [DotNode]-                 }--data DotEdge = DotEdge { edgeHeadNodeID :: Int-                       , edgeTailNodeID :: Int-                       , edgeAttributes :: [Attribute]-                       , directedEdge   :: Bool-                       }--instance Show DotNode where-    show n = init . unlines . addTabs $ nodesToString n--nodesToString :: DotNode -> [String]-nodesToString (DotNode { nodeID, nodeAttributes })-    | null nodeAttributes = [nID ++ ";"]-    | otherwise           = [nID ++ (' ':((show nodeAttributes) ++ ";"))]-    where-      nID = show nodeID-nodesToString (DotCluster { clusterID, clusterAttributes, clusterElems })-    = ["subgraph cluster_" ++ clusterID ++ " {"] ++ (addTabs inner) ++ ["}"]-    where-      inner = case clusterAttributes of-                [] -> nodes-                a  -> ("graph " ++ (show a) ++ ";") : nodes-      nodes = concatMap nodesToString clusterElems--addTabs :: [String] -> [String]-addTabs = map ('\t':)--instance Show DotEdge where-    show (DotEdge { edgeHeadNodeID, edgeTailNodeID, edgeAttributes, directedEdge })-        = '\t' : ((show edgeTailNodeID) ++ edge ++ (show edgeHeadNodeID) ++ attributes)-          where-            edge = " " ++ (if directedEdge then dirEdge else undirEdge) ++ " "-            attributes = case edgeAttributes of-                           [] -> ";"-                           a  -> ' ':((show a) ++ ";")--instance Show DotGraph where-    show (DotGraph { graphAttributes, graphNodes, graphEdges, directedGraph })-        = unlines $ gType : " {" : (rest ++ ["}"])-        where-          gType = if directedGraph then dirGraph else undirGraph-          rest = case graphAttributes of-                   [] -> nodesEdges-                   a -> ("\tgraph " ++ (show a) ++ ";") : nodesEdges-          nodesEdges = (map show graphNodes) ++ (map show graphEdges)---- | Define into which cluster a particular node belongs.---   Nodes can be nested to arbitrary depth.-data NodeCluster c a = N (LNode a) | C c (NodeCluster c a)-                        deriving (Show)---- | A tree representation of a cluster.-data ClusterTree c a = NT (LNode a) | CT c [ClusterTree c a]-                       deriving (Show)---- Convert a single node cluster into its tree representation.-clustToTree          :: NodeCluster c a -> ClusterTree c a-clustToTree (N ln)   = NT ln-clustToTree (C c nc) = CT c [clustToTree nc]---- Two nodes are in the same "default" cluster; otherwise check if they--- are in the same cluster.-sameClust :: (Eq c) => ClusterTree c a -> ClusterTree c a -> Bool-sameClust (NT _)    (NT _)    = True-sameClust (CT c1 _) (CT c2 _) = c1 == c2-sameClust _         _         = False---- Singleton nodes come first, and then ordering based upon the cluster.-clustOrder :: (Ord c) => ClusterTree c a -> ClusterTree c a -> Ordering-clustOrder (NT _)    (NT _)    = EQ-clustOrder (NT _)    (CT _ _)  = P.LT -- don't use the attribute LT-clustOrder (CT _ _)  (NT _)    = GT-clustOrder (CT c1 _) (CT c2 _) = compare c1 c2---- Extract the sub-trees.-getNodes           :: ClusterTree c a -> [ClusterTree c a]-getNodes n@(NT _)  = [n]-getNodes (CT _ ns) = ns---- Combine clusters.-collapseNClusts :: (Ord c) => [ClusterTree c a] -> [ClusterTree c a]-collapseNClusts = concatMap grpCls-                  . groupBy sameClust-                  . sortBy clustOrder-    where-      grpCls []              = []-      grpCls ns@((NT _):_)   = ns-      grpCls cs@((CT c _):_) = [CT c (collapseNClusts $ concatMap getNodes cs)]---- Differences between directed and undirected graphs.--dirEdge, undirEdge :: String-dirEdge = "->"-undirEdge = "--"--dirGraph, undirGraph :: String-dirGraph = "digraph"-undirGraph = "graph"--dirCommand, undirCommand :: String-dirCommand = "dot"-undirCommand = "neato"+import Data.GraphViz.Commands --- | The appropriate GraphViz command for the given graph.-commandFor    :: DotGraph -> String-commandFor dg = if (directedGraph dg)-                then dirCommand-                else undirCommand+-- ----------------------------------------------------------------------------- --- Determine ifi the given graph is undirected or directed.+-- | Determine if the given graph is undirected or directed. isUndir   :: (Ord b, Graph g) => g a b -> Bool isUndir g = all hasFlip edges     where@@ -179,8 +65,9 @@       hasFlip e = Set.member (flippedEdge e) eSet       flippedEdge (f,t,l) = (t,f,l) --- | Convert a graph to dot format. You can then write this to a file---   and run the appropriate command on it (found using 'commandFor').+-- -----------------------------------------------------------------------------++-- | Convert a graph to GraphViz's /Dot/ format. graphToDot :: (Ord b, Graph gr) => gr a b -> [Attribute]            -> (LNode a -> [Attribute]) -> (LEdge b -> [Attribute]) -> DotGraph graphToDot graph graphAttributes fmtNode fmtEdge@@ -190,9 +77,8 @@         clusterBy = N         fmtCluster _ = [] --- | Convert a graph to dot format, using the specified clustering function---   to group nodes into clusters.  You can then write this to a file and---   run the appropriate command on it (found using 'commandFor').+-- | Convert a graph to /Dot/ format, using the specified clustering function+--   to group nodes into clusters. --   Clusters can be nested to arbitrary depth. clusterGraphToDot :: (Ord c, Ord b, Graph gr) => gr a b                   -> [Attribute] -> (LNode a -> NodeCluster c a)@@ -201,8 +87,7 @@ clusterGraphToDot graph graphAttributes clusterBy fmtCluster fmtNode fmtEdge     = DotGraph { graphAttributes, graphNodes, graphEdges, directedGraph }       where-        clusters = collapseNClusts . map (clustToTree . clusterBy) $ labNodes graph-        graphNodes = treesToNodes fmtCluster fmtNode clusters+        graphNodes = clustersToNodes clusterBy fmtCluster fmtNode graph         directedGraph = not $ isUndir graph         graphEdges = catMaybes . map mkDotEdge . labEdges $ graph         mkDotEdge e@(f,t,_) = if (directedGraph || f <= t)@@ -212,34 +97,7 @@                                                   ,directedEdge = directedGraph}                               else Nothing --- Convert the cluster representation of the trees into DotNodes.--- Clusters will be labelled with integers.-treesToNodes :: (c -> [Attribute]) -> (LNode a -> [Attribute])-             -> [ClusterTree c a] -> [DotNode]-treesToNodes fmtCluster fmtNode = snd . treesToNodesFrom fmtCluster fmtNode 0---- Start labelling the clusters with this integer.-treesToNodesFrom :: (c -> [Attribute]) -> (LNode a -> [Attribute])-                 -> Int -> [ClusterTree c a] -> (Int,[DotNode])-treesToNodesFrom fmtCluster fmtNode n = mapAccumL mkNodes n-    where-      mkNodes = treeToNode fmtCluster fmtNode---- Convert this ClusterTree into its DotNode representation.-treeToNode :: (c -> [Attribute]) -> (LNode a -> [Attribute])-           -> Int -> ClusterTree c a -> (Int, DotNode)-treeToNode _ fmtNode n (NT ln) = ( n-                                 , DotNode { nodeID = fst ln-                                           , nodeAttributes = fmtNode ln-                                           }-                                 )-treeToNode fmtCluster fmtNode n (CT c nts) = (n',clust)-    where-      (n', nts') = treesToNodesFrom fmtCluster fmtNode (n+1) nts-      clust = DotCluster { clusterID = show n-                         , clusterAttributes = fmtCluster c-                         , clusterElems = nts'-                         }+-- -----------------------------------------------------------------------------  type AttributeNode a = ([Attribute], a) type AttributeEdge b = ([Attribute], b)@@ -250,15 +108,9 @@ graphToGraph :: forall gr a b . (Ord b, Graph gr) =>                 gr a b -> [Attribute] -> (LNode a -> [Attribute]) -> (LEdge b -> [Attribute]) -> IO (gr (AttributeNode a) (AttributeEdge b)) graphToGraph gr graphAttributes fmtNode fmtEdge-    = do { (inp, outp, errp, proc) <- runInteractiveCommand (command++" -Tdot")-         ; hPutStr inp (show dot)-         ; hClose inp-         ; forkIO $ (hGetContents errp >>= hPutStr stderr)-         ; res <- hGetContents outp+    = do { out <- graphvizWithHandle command dot DotOutput hGetContents+         ; let res = fromJust out          ; (length res) `seq` return ()-         ; hClose outp-         ; hClose errp-         ; waitForProcess proc          ; return $ rebuildGraphWithAttributes res          }     where@@ -279,49 +131,13 @@                 where                   getLabel c = (fromJust $ Map.lookup c edgeMap,l) -readDotNode :: Parser Char DotNode-readDotNode = do { optional whitespace-                 ; nodeID <- number-                 ; as <- optional (whitespace >> readAttributesList)-                 ; char ';'-                 ; skipToNewline-                 ; return (DotNode { nodeID, nodeAttributes = fromMaybe [] as })-                 }--readDotEdge :: Parser Char DotEdge-readDotEdge = do { optional whitespace-                 ; edgeTailNodeID <- number-                 ; whitespace-                 ; edge <- strings [dirEdge,undirEdge]-                 ; whitespace-                 ; edgeHeadNodeID <- number-                 ; as <- optional (whitespace >> readAttributesList)-                 ; char ';'-                 ; skipToNewline-                 ; return (DotEdge { edgeHeadNodeID-                                   , edgeTailNodeID-                                   , edgeAttributes = fromMaybe [] as-                                   , directedEdge = edge == dirEdge })-                 }+-- | Pass the plain graph through 'graphToGraph'.  This is an @IO@ action,+--   however since the state doesn't change it's safe to use 'unsafePerformIO'+--   to convert this to a normal function.+dotizeGraph   :: (DynGraph gr, Ord b) => gr a b+              -> gr (AttributeNode a) (AttributeEdge b)+dotizeGraph g = unsafePerformIO+                $ graphToGraph g gAttrs noAttrs noAttrs     where---readDotGraph :: Parser Char DotGraph-readDotGraph = do { d <- strings [dirGraph,undirGraph]-                  ; let directedGraph = d == dirGraph-                  ; whitespace-                  ; char '{'-                  ; skipToNewline-                  ; graphAttributes-                      <- liftM concat $-                         many (optional whitespace >>-                               oneOf [ (string "edge" >> skipToNewline >> return [])-                                     , (string "node" >> skipToNewline >> return [])-                                     , (string "graph" >> whitespace >> readAttributesList >>= \as -> skipToNewline >> return as)-                                     ]-                              )-                  ; graphNodes <- many readDotNode-                  ; graphEdges <- many readDotEdge-                  ; char '}'-                  ; return $ DotGraph { graphAttributes, graphNodes, graphEdges, directedGraph }-                  }+      gAttrs = []+      noAttrs = const []
Data/GraphViz/Attributes.hs view
@@ -1,24 +1,27 @@-{-# LANGUAGE RecordPuns-           , PatternSignatures+{-# LANGUAGE NamedFieldPuns+           , ScopedTypeVariables            #-} - {- GraphViz ------------------------------------------------------\- |                                                                 |- | Copyright (c) 2008, Matthew Sackman (matthew@wellquite.org),    |- |              Ivan Lazar Miljenovic (ivan.miljenovic@gmail.com)  |- |                                                                 |- | GraphViz is freely distributable under the terms of a 3-Clause  |- | BSD-style license.                                              |- |                                                                 |- \-----------------------------------------------------------------}+{- |+   Module      : Data.GraphViz.Attributes+   Description : Definition of the GraphViz attributes.+   Copyright   : (c) Matthew Sackman, Ivan Lazar Miljenovic+   License     : 3-Clause BSD-style+   Maintainer  : Ivan.Miljenovic@gmail.com +   This module defines the various attributes that different parts of+   a GraphViz graph can have.  Note that when using this module, you+   may need to either import it or @Prelude@ qualified or hiding+   @LT@, as this module exports a constructor with the same name.+  -}+ module Data.GraphViz.Attributes where  import Prelude hiding (LT)  import Data.Word import Numeric-import Text.ParserCombinators.PolyLazy+import Text.ParserCombinators.Poly.Lazy import Control.Monad import Data.Maybe @@ -37,25 +40,25 @@                  deriving (Eq)  instance Show ArrowType where-    show Normal = "normal"-    show Inv = "inv"-    show Dot = "dot"-    show InvDot = "invdot"-    show ODot = "odot"-    show InvODot = "invodot"-    show NoArrow = "none"-    show Tee = "tee"-    show Empty = "empty"+    show Normal   = "normal"+    show Inv      = "inv"+    show Dot      = "dot"+    show InvDot   = "invdot"+    show ODot     = "odot"+    show InvODot  = "invodot"+    show NoArrow  = "none"+    show Tee      = "tee"+    show Empty    = "empty"     show InvEmpty = "invempty"-    show Diamond = "diamond"+    show Diamond  = "diamond"     show ODiamond = "odiamond"     show EDiamond = "ediamond"-    show Crow = "crow"-    show Box = "box"-    show OBox = "obox"-    show Open = "open"+    show Crow     = "crow"+    show Box      = "box"+    show OBox     = "obox"+    show Open     = "open"     show HalfOpen = "halfopen"-    show Vee = "vee"+    show Vee      = "vee"  readArrowType :: Parser Char ArrowType readArrowType
+ Data/GraphViz/Commands.hs view
@@ -0,0 +1,215 @@+{- |+   Module      : Data.GraphViz.Commands+   Description : Functions to run GraphViz commands.+   Copyright   : (c) Matthew Sackman, Ivan Lazar Miljenovic+   License     : 3-Clause BSD-style+   Maintainer  : Ivan.Miljenovic@gmail.com++   This module defines functions to call the various GraphViz+   commands.++   Most of these functions were from version 0.5 of /Graphalyze/:++   <http://hackage.haskell.org/cgi-bin/hackage-scripts/package/Graphalyze-0.5>+-}++module Data.GraphViz.Commands+    ( GraphvizCommand(..)+    , dirCommand+    , undirCommand+    , commandFor+    , GraphvizOutput(..)+    , runGraphviz+    , runGraphvizCommand+    , graphvizWithHandle+    )+    where++import System.IO+import System.Exit+import System.Process+import Data.Array.IO+import Control.Concurrent+import Control.Exception++import Data.GraphViz.Types++-- | The available Graphviz commands.+data GraphvizCommand = DotCmd | Neato | TwoPi | Circo | Fdp++instance Show GraphvizCommand where+    show DotCmd = "dot"+    show Neato  = "neato"+    show TwoPi  = "twopi"+    show Circo  = "circo"+    show Fdp    = "fdp"++-- | The default command for directed graphs.+dirCommand :: GraphvizCommand+dirCommand = DotCmd++-- | The default command for undirected graphs.+undirCommand :: GraphvizCommand+undirCommand = Neato++-- | The appropriate (default) GraphViz command for the given graph.+commandFor    :: DotGraph -> GraphvizCommand+commandFor dg = if (directedGraph dg)+                then dirCommand+                else undirCommand++-- | The possible Graphviz outputs, obtained by running /dot -Txxx/.+--   Note that it is not possible to choose between output variants,+--   and that not all of these may be available on your system.+--+--   This will probably be improved in future.+data GraphvizOutput = Canon+                    | Cmap+                    | Cmapx+                    | Cmapx_np+                    | Dia+                    | DotOutput+                    | Eps+                    | Fig+                    | Gd+                    | Gd2+                    | Gif+                    | Gtk+                    | Hpgl+                    | Imap+                    | Imap_np+                    | Ismap+                    | Jpe+                    | Jpeg+                    | Jpg+                    | Mif+                    | Mp+                    | Pcl+                    | Pdf+                    | Pic+                    | Plain+                    | PlainExt+                    | Png+                    | Ps+                    | Ps2+                    | Svg+                    | Svgz+                    | Tk+                    | Vml+                    | Vmlz+                    | Vrml+                    | Vtx+                    | Wbmp+                    | Xdot+                    | Xlib++instance Show GraphvizOutput where+    show Canon     = "canon"+    show Cmap      = "cmap"+    show Cmapx     = "cmapx"+    show Cmapx_np  = "cmapx_np"+    show Dia       = "dia"+    show DotOutput = "dot"+    show Eps       = "eps"+    show Fig       = "fig"+    show Gd        = "gd"+    show Gd2       = "gd2"+    show Gif       = "gif"+    show Gtk       = "gtk"+    show Hpgl      = "hpgl"+    show Imap      = "imap"+    show Imap_np   = "imap_np"+    show Ismap     = "ismap"+    show Jpe       = "jpe"+    show Jpeg      = "jpeg"+    show Jpg       = "jpg"+    show Mif       = "mif"+    show Mp        = "mp"+    show Pcl       = "pcl"+    show Pdf       = "pdf"+    show Pic       = "pic"+    show Plain     = "plain"+    show PlainExt  = "plain-ext"+    show Png       = "png"+    show Ps        = "ps"+    show Ps2       = "ps2"+    show Svg       = "svg"+    show Svgz      = "svgz"+    show Tk        = "tk"+    show Vml       = "vml"+    show Vmlz      = "vmlz"+    show Vrml      = "vrml"+    show Vtx       = "vtx"+    show Wbmp      = "wbmp"+    show Xdot      = "xdot"+    show Xlib      = "xlib"++-- | Run the recommended Graphviz command on this graph, saving the result+--   to the file provided (note: file extensions are /not/ checked).+--   Returns @True@ if successful, @False@ otherwise.+runGraphviz         :: DotGraph -> GraphvizOutput -> FilePath -> IO Bool+runGraphviz gr t fp = runGraphvizCommand (commandFor gr) gr t fp++-- | Run the chosen Graphviz command on this graph, saving the result+--   to the file provided (note: file extensions are /not/ checked).+--   Returns @True@ if successful, @False@ otherwise.+runGraphvizCommand :: GraphvizCommand -> DotGraph -> GraphvizOutput+                   -> FilePath -> IO Bool+runGraphvizCommand  cmd gr t fp+    = do pipe <- tryJust catcher $ openFile fp WriteMode+         case pipe of+           (Left _)  -> return False+           (Right f) -> do file <- graphvizWithHandle cmd gr t (flip squirt f)+                           hClose f+                           case file of+                             (Just _) -> return True+                             _        -> return False+    where+      catcher   :: IOError -> Maybe ()+      catcher _ = Just ()++-- graphvizWithHandle sometimes throws an error about handles not+-- being closed properly: investigate.++-- | Run the chosen Graphviz command on this graph, but send the result to the+--   given handle rather than to a file.+--   The result is wrapped in 'Maybe' rather than throwing an error.+graphvizWithHandle :: (Show a) => GraphvizCommand -> DotGraph -> GraphvizOutput+                   -> (Handle -> IO a) -> IO (Maybe a)+graphvizWithHandle cmd gr t f+    = do (inp, outp, errp, proc) <- runInteractiveCommand command+         forkIO $ hPrint inp gr >> hClose inp+         forkIO $ (hGetContents errp >>= hPutStr stderr >> hClose errp)+         a <- f outp+         -- Don't close outp until f finishes.+         a `seq` hClose outp+         exitCode <- waitForProcess proc+         case exitCode of+           ExitSuccess -> return (Just a)+           _           -> return Nothing+    where+      command = (show cmd) ++ " -T" ++ (show t)++{- |+   This function is taken from the /mohws/ project, available under a+   3-Clause BSD license.  The actual function is taken from:+   <http://code.haskell.org/mohws/src/Util.hs>+   It provides an efficient way of transferring data from one 'Handle'+   to another.+ -}+squirt :: Handle -> Handle -> IO ()+squirt rd wr = do+  arr <- newArray_ (0, bufsize-1)+  let loop = do+        r <- hGetArray rd arr bufsize+        if (r == 0)+          then return ()+          else if (r < bufsize)+                then hPutArray wr arr r+                else hPutArray wr arr bufsize >> loop+  loop+    where+      -- This was originally separate+      bufsize :: Int+      bufsize = 4 * 1024+
Data/GraphViz/ParserCombinators.hs view
@@ -1,19 +1,19 @@-{-# LANGUAGE PatternSignatures #-}+{-# LANGUAGE ScopedTypeVariables #-} +{- |+   Module      : Data.GraphViz.ParserCombinators+   Description : Helper functions for Parsing.+   Copyright   : (c) Matthew Sackman, Ivan Lazar Miljenovic+   License     : 3-Clause BSD-style+   Maintainer  : Ivan.Miljenovic@gmail.com - {--GraphViz ------------------------------------------------------\- |                                                                 |- | Copyright (c) 2008, Matthew Sackman (matthew@wellquite.org),    |- |              Ivan Lazar Miljenovic (ivan.miljenovic@gmail.com)  |- |                                                                 |- | GraphViz is freely distributable under the terms of a 3-Clause  |- | BSD-style license.                                              |- |                                                                 |- \-----------------------------------------------------------------}+   This module defines simple helper functions for use with+   @Text.ParserCombinators.Poly.Lazy@.+-}  module Data.GraphViz.ParserCombinators where -import Text.ParserCombinators.PolyLazy+import Text.ParserCombinators.Poly.Lazy import Control.Monad  string :: String -> Parser Char String
+ Data/GraphViz/Types.hs view
@@ -0,0 +1,161 @@+{-# LANGUAGE NamedFieldPuns+           , ScopedTypeVariables+           #-}++{- |+   Module      : Data.GraphViz.Types+   Description : Definition of the GraphViz types.+   Copyright   : (c) Matthew Sackman, Ivan Lazar Miljenovic+   License     : 3-Clause BSD-style+   Maintainer  : Ivan.Miljenovic@gmail.com++   This module defines the overall types and methods that interact+   with them for the GraphViz library.+-}++module Data.GraphViz.Types+    ( DotGraph(..)+    , DotNode(..)+    , DotEdge(..)+    , readDotGraph+    ) where++import Data.Maybe+import Control.Monad+import Text.ParserCombinators.Poly.Lazy++import Data.GraphViz.Attributes+import Data.GraphViz.ParserCombinators++-- -----------------------------------------------------------------------------++-- | The internal representation of a graph in Dot form.+data DotGraph = DotGraph { graphAttributes :: [Attribute]+                         , graphNodes :: [DotNode]+                         , graphEdges :: [DotEdge]+                         , directedGraph :: Bool+                         }++instance Show DotGraph where+    show (DotGraph { graphAttributes, graphNodes, graphEdges, directedGraph })+        = unlines $ gType : " {" : (rest ++ ["}"])+        where+          gType = if directedGraph then dirGraph else undirGraph+          rest = case graphAttributes of+                   [] -> nodesEdges+                   a -> ("\tgraph " ++ (show a) ++ ";") : nodesEdges+          nodesEdges = (map show graphNodes) ++ (map show graphEdges)++dirGraph :: String+dirGraph = "digraph"++undirGraph :: String+undirGraph = "graph"++-- -----------------------------------------------------------------------------++-- | A node in 'DotGraph' is either a singular node, or a cluster+--   containing nodes (or more clusters) within it.+data DotNode+    = DotNode { nodeID :: Int+              , nodeAttributes :: [Attribute]+              }+    | DotCluster { clusterID         :: String+                 , clusterAttributes :: [Attribute]+                 , clusterElems      :: [DotNode]+                 }++instance Show DotNode where+    show n = init . unlines . addTabs $ nodesToString n++nodesToString :: DotNode -> [String]+nodesToString (DotNode { nodeID, nodeAttributes })+    | null nodeAttributes = [nID ++ ";"]+    | otherwise           = [nID ++ (' ':((show nodeAttributes) ++ ";"))]+    where+      nID = show nodeID+nodesToString (DotCluster { clusterID, clusterAttributes, clusterElems })+    = ["subgraph cluster_" ++ clusterID ++ " {"] ++ (addTabs inner) ++ ["}"]+    where+      inner = case clusterAttributes of+                [] -> nodes+                a  -> ("graph " ++ (show a) ++ ";") : nodes+      nodes = concatMap nodesToString clusterElems++-- | Prefix each 'String' with a tab character.+addTabs :: [String] -> [String]+addTabs = map ('\t':)++-- -----------------------------------------------------------------------------++-- | An edge in 'DotGraph'.+data DotEdge = DotEdge { edgeHeadNodeID :: Int+                       , edgeTailNodeID :: Int+                       , edgeAttributes :: [Attribute]+                       , directedEdge   :: Bool+                       }++instance Show DotEdge where+    show (DotEdge { edgeHeadNodeID, edgeTailNodeID, edgeAttributes, directedEdge })+        = '\t' : ((show edgeTailNodeID) ++ edge ++ (show edgeHeadNodeID) ++ attributes)+          where+            edge = " " ++ (if directedEdge then dirEdge else undirEdge) ++ " "+            attributes = case edgeAttributes of+                           [] -> ";"+                           a  -> ' ':((show a) ++ ";")++dirEdge :: String+dirEdge = "->"++undirEdge :: String+undirEdge = "--"++-- -----------------------------------------------------------------------------++-- | Parse a 'DotNode'+readDotNode :: Parser Char DotNode+readDotNode = do { optional whitespace+                 ; nodeID <- number+                 ; as <- optional (whitespace >> readAttributesList)+                 ; char ';'+                 ; skipToNewline+                 ; return (DotNode { nodeID, nodeAttributes = fromMaybe [] as })+                 }++-- | Parse a 'DotEdge'+readDotEdge :: Parser Char DotEdge+readDotEdge = do { optional whitespace+                 ; edgeTailNodeID <- number+                 ; whitespace+                 ; edge <- strings [dirEdge,undirEdge]+                 ; whitespace+                 ; edgeHeadNodeID <- number+                 ; as <- optional (whitespace >> readAttributesList)+                 ; char ';'+                 ; skipToNewline+                 ; return (DotEdge { edgeHeadNodeID+                                   , edgeTailNodeID+                                   , edgeAttributes = fromMaybe [] as+                                   , directedEdge = edge == dirEdge })+                 }++-- | Parse a 'DotGraph'+readDotGraph :: Parser Char DotGraph+readDotGraph = do { d <- strings [dirGraph,undirGraph]+                  ; let directedGraph = d == dirGraph+                  ; whitespace+                  ; char '{'+                  ; skipToNewline+                  ; graphAttributes+                      <- liftM concat $+                         many (optional whitespace >>+                               oneOf [ (string "edge" >> skipToNewline >> return [])+                                     , (string "node" >> skipToNewline >> return [])+                                     , (string "graph" >> whitespace >> readAttributesList >>= \as -> skipToNewline >> return as)+                                     ]+                              )+                  ; graphNodes <- many readDotNode+                  ; graphEdges <- many readDotEdge+                  ; char '}'+                  ; return $ DotGraph { graphAttributes, graphNodes, graphEdges, directedGraph }+                  }
+ Data/GraphViz/Types/Clustering.hs view
@@ -0,0 +1,114 @@+{-# OPTIONS_HADDOCK hide #-}++{- |+   Module      : Data.GraphViz.Types.Clustering+   Description : Definition of the clustering types for GraphViz.+   Copyright   : (c) Matthew Sackman, Ivan Lazar Miljenovic+   License     : 3-Clause BSD-style+   Maintainer  : Ivan.Miljenovic@gmail.com++   This module defines types for creating clusters.+-}++module Data.GraphViz.Types.Clustering+    ( NodeCluster(..)+    , clustersToNodes+    ) where++-- LT is defined in Attributes+import Prelude hiding (LT)+import qualified Prelude as P++import Data.GraphViz.Types+import Data.GraphViz.Attributes++import Data.List(groupBy, sortBy, mapAccumL)+import Data.Graph.Inductive.Graph(Graph, LNode, labNodes)++-- -----------------------------------------------------------------------------++-- | Define into which cluster a particular node belongs.+--   Nodes can be nested to arbitrary depth.+data NodeCluster c a = N (LNode a) | C c (NodeCluster c a)+                        deriving (Show)++-- | Create the @'DotNode'@s for the given graph.+clustersToNodes :: (Ord c, Graph gr) => (LNode a -> NodeCluster c a)+                   -> (c -> [Attribute]) -> (LNode a -> [Attribute])+                   -> gr a b -> [DotNode]+clustersToNodes clusterBy fmtCluster fmtNode+    = treesToNodes fmtCluster fmtNode+      . collapseNClusts+      . map (clustToTree . clusterBy)+      . labNodes++-- -----------------------------------------------------------------------------++-- | A tree representation of a cluster.+data ClusterTree c a = NT (LNode a) | CT c [ClusterTree c a]+                       deriving (Show)++-- | Convert a single node cluster into its tree representation.+clustToTree          :: NodeCluster c a -> ClusterTree c a+clustToTree (N ln)   = NT ln+clustToTree (C c nc) = CT c [clustToTree nc]++-- | Two nodes are in the same "default" cluster; otherwise check if they+--   are in the same cluster.+sameClust :: (Eq c) => ClusterTree c a -> ClusterTree c a -> Bool+sameClust (NT _)    (NT _)    = True+sameClust (CT c1 _) (CT c2 _) = c1 == c2+sameClust _         _         = False++-- | Singleton nodes come first, and then ordering based upon the cluster.+clustOrder :: (Ord c) => ClusterTree c a -> ClusterTree c a -> Ordering+clustOrder (NT _)    (NT _)    = EQ+clustOrder (NT _)    (CT _ _)  = P.LT -- don't use the attribute LT+clustOrder (CT _ _)  (NT _)    = GT+clustOrder (CT c1 _) (CT c2 _) = compare c1 c2++-- | Extract the sub-trees.+getNodes           :: ClusterTree c a -> [ClusterTree c a]+getNodes n@(NT _)  = [n]+getNodes (CT _ ns) = ns++-- | Combine clusters.+collapseNClusts :: (Ord c) => [ClusterTree c a] -> [ClusterTree c a]+collapseNClusts = concatMap grpCls+                  . groupBy sameClust+                  . sortBy clustOrder+    where+      grpCls []              = []+      grpCls ns@((NT _):_)   = ns+      grpCls cs@((CT c _):_) = [CT c (collapseNClusts $ concatMap getNodes cs)]+++-- | Convert the cluster representation of the trees into @'DotNode'@s.+--   Clusters will be labelled with @'Int'@s.+treesToNodes :: (c -> [Attribute]) -> (LNode a -> [Attribute])+             -> [ClusterTree c a] -> [DotNode]+treesToNodes fmtCluster fmtNode = snd . treesToNodesFrom fmtCluster fmtNode 0++-- | Start labelling the clusters with this @'Int'@.+treesToNodesFrom :: (c -> [Attribute]) -> (LNode a -> [Attribute])+                 -> Int -> [ClusterTree c a] -> (Int,[DotNode])+treesToNodesFrom fmtCluster fmtNode n = mapAccumL mkNodes n+    where+      mkNodes = treeToNode fmtCluster fmtNode++-- | Convert this 'ClusterTree' into its 'DotNode' representation.+treeToNode :: (c -> [Attribute]) -> (LNode a -> [Attribute])+           -> Int -> ClusterTree c a -> (Int, DotNode)+treeToNode _ fmtNode n (NT ln) = ( n+                                 , DotNode { nodeID = fst ln+                                           , nodeAttributes = fmtNode ln+                                           }+                                 )++treeToNode fmtCluster fmtNode n (CT c nts) = (n',clust)+    where+      (n', nts') = treesToNodesFrom fmtCluster fmtNode (n+1) nts+      clust = DotCluster { clusterID = show n+                         , clusterAttributes = fmtCluster c+                         , clusterElems = nts'+                         }
+ TODO view
@@ -0,0 +1,10 @@+Areas to work on:++* Add support for Data.Graph-style graphs.++* Allow user to choose whether or not the graph is meant to be+  directed or undirected.++* Ensure Data.GraphViz.Attributes contains all supported attributes for Dot.++* Remove (or at least minimise) usage of extensions.
graphviz.cabal view
@@ -1,27 +1,37 @@ Name:               graphviz-Version:            2008.9.20+Version:            2009.5.1 Stability:          Beta Copyright:          Matthew Sackman, Ivan Lazar Miljenovic Category:           Graphics-Maintainer:         matthew@wellquite.org+Maintainer:         Ivan.Miljenovic@gmail.com Author:             Matthew Sackman, Ivan Lazar Miljenovic License:            BSD3 License-File:       LICENSE-Cabal-Version:      >= 1.2+Extra-Source-Files: TODO+Cabal-Version:      >= 1.6 Build-Type:         Simple-Synopsis:           GraphViz wrapper for Haskell+Synopsis:           GraphViz wrapper for Haskell. Description:-  Allows you to convert Data.Graph... graphs into dot format,-  and parse them back in, as a Dot structure.+  Provides convenient functions to convert FGL graphs into the Dot+  language used by the GraphViz (http://graphviz.org/) programs with a+  large degree of customisation for layout, etc. -  Or, you can run your Data.Graph...graph via dot, get the positional-  information out from dot and build a new graph, combining the-  positional information with the original graph.+  Also allows a limited amount of parsing of Dot, and usage of+  GraphViz to attach positional data to each node and edge in the+  graph.  Library {-        Build-depends:     base, containers, process, fgl, polyparse-        Exposed-modules:   Data.GraphViz+        Build-Depends:     base == 4.*, containers, process, array,+                           fgl, polyparse >= 1.1++        Exposed-Modules:   Data.GraphViz+                           Data.GraphViz.Types+                           Data.GraphViz.Commands                            Data.GraphViz.Attributes++        Other-Modules:     Data.GraphViz.Types.Clustering                            Data.GraphViz.ParserCombinators-        ghc-options:       -Wall -fno-warn-name-shadowing++        Ghc-Options:       -Wall -fno-warn-name-shadowing+        Ghc-Prof-Options:  -prof -auto-all }