{- |
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/ suite of programs (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/>
Commands for converting graphs to Dot format have two options: one
in which the user specifies whether the graph is directed or
undirected, and a primed version which attempts to automatically
infer if the graph is directed or not. Note that these conversion
functions assume that undirected graphs have every edge being
duplicated (or at least that if there exists an edge from /n1/ to
/n2/, then /n1 <= n2/).
-}moduleData.GraphViz(-- * Conversion from graphs to /Dot/ format.graphToDot,graphToDot'-- ** Conversion with support for clusters.,NodeCluster(..),clusterGraphToDot,clusterGraphToDot'-- * Passing the graph through GraphViz.-- ** Type aliases for @Node@ and @Edge@ labels.,AttributeNode,AttributeEdge-- ** For normal graphs.,graphToGraph,graphToGraph',dotizeGraph,dotizeGraph'-- ** For clustered graphs.,clusterGraphToGraph,clusterGraphToGraph',dotizeClusterGraph,dotizeClusterGraph'-- * Re-exporting other modules.,moduleData.GraphViz.Types,moduleData.GraphViz.Attributes,moduleData.GraphViz.Commands)whereimportData.GraphViz.TypesimportData.GraphViz.Types.ClusteringimportData.GraphViz.AttributesimportData.GraphViz.CommandsimportData.Graph.Inductive.GraphimportqualifiedData.SetasSetimportControl.Arrow((&&&))importData.Maybe(mapMaybe,fromJust)importqualifiedData.MapasMapimportControl.Parallel.Strategies(rnf)importSystem.IO(hGetContents)importSystem.IO.Unsafe(unsafePerformIO)-- ------------------------------------------------------------------------------- | Determine if the given graph is undirected.isUndirected::(Ordb,Graphg)=>gab->BoolisUndirectedg=allhasFlipeswherees=labEdgesgeSet=Set.fromListeshasFlipe=Set.member(flippedEdgee)eSetflippedEdge(f,t,l)=(t,f,l)-- | Determine if the given graph is directed.isDirected::(Ordb,Graphg)=>gab->BoolisDirected=not.isUndirected-- ------------------------------------------------------------------------------- | Convert a graph to GraphViz's /Dot/ format. The 'Bool' value is-- 'True' for directed graphs, 'False' otherwise.graphToDot::(Graphgr)=>Bool->grab->[GlobalAttributes]->(LNodea->Attributes)->(LEdgeb->Attributes)->DotGraphNodegraphToDotisDirgraphgAttributes=clusterGraphToDotisDirgraphgAttributesclustBycIDfmtClustwhereclustBy::LNodea->NodeCluster()aclustBy=NcID=constNothingfmtClust=const[]-- | Convert a graph to GraphViz's /Dot/ format with automatic-- direction detection.graphToDot'::(Ordb,Graphgr)=>grab->[GlobalAttributes]->(LNodea->Attributes)->(LEdgeb->Attributes)->DotGraphNodegraphToDot'graph=graphToDot(isDirectedgraph)graph-- | Convert a graph to /Dot/ format, using the specified clustering function-- to group nodes into clusters.-- Clusters can be nested to arbitrary depth.-- The 'Bool' argument is 'True' for directed graphs, 'False' otherwise.clusterGraphToDot::(Ordc,Graphgr)=>Bool->grab->[GlobalAttributes]->(LNodea->NodeClusterca)->(c->MaybeGraphID)->(c->[GlobalAttributes])->(LNodea->Attributes)->(LEdgeb->Attributes)->DotGraphNodeclusterGraphToDotdirGraphgraphgAttrsclusterBycIDfmtClusterfmtNodefmtEdge=DotGraph{strictGraph=False,directedGraph=dirGraph,graphID=Nothing,graphStatements=stmts}wherestmts=DotStmts{attrStmts=gAttrs,subGraphs=cs,nodeStmts=ns,edgeStmts=es}(cs,ns)=clustersToNodesclusterBycIDfmtClusterfmtNodegraphes=mapMaybemkDotEdge.labEdges$graphmkDotEdgee@(f,t,_)=ifdirGraph||f<=tthenJustDotEdge{edgeFromNodeID=f,edgeToNodeID=t,edgeAttributes=fmtEdgee,directedEdge=dirGraph}elseNothing-- | Convert a graph to /Dot/ format, using the specified clustering function-- to group nodes into clusters.-- Clusters can be nested to arbitrary depth.-- Graph direction is automatically inferred.clusterGraphToDot'::(Ordc,Ordb,Graphgr)=>grab->[GlobalAttributes]->(LNodea->NodeClusterca)->(c->MaybeGraphID)->(c->[GlobalAttributes])->(LNodea->Attributes)->(LEdgeb->Attributes)->DotGraphNodeclusterGraphToDot'gr=clusterGraphToDot(isDirectedgr)gr-- -----------------------------------------------------------------------------typeAttributeNodea=(Attributes,a)typeAttributeEdgeb=(Attributes,b)-- | Run the appropriate GraphViz command on the graph to get-- positional information and then combine that information back-- into the original graph. Note that for the edge information to-- be parsed properly when using multiple edges, each edge between-- two nodes needs to have a unique label.---- The 'Bool' argument is 'True' for directed graphs, 'False'-- otherwise. Directed graphs are passed through /dot/, and-- undirected graphs through /neato/.graphToGraph::(Graphgr)=>Bool->grab->[GlobalAttributes]->(LNodea->Attributes)->(LEdgeb->Attributes)->IO(gr(AttributeNodea)(AttributeEdgeb))graphToGraphisDirgrgAttributesfmtNodefmtEdge=dotAttributesisDirgrdotwheredot=graphToDotisDirgrgAttributesfmtNodefmtEdgedotAttributes::(Graphgr)=>Bool->grab->DotGraphNode->IO(gr(AttributeNodea)(AttributeEdgeb))dotAttributesisDirgrdot=do(Justoutput)<-graphvizWithHandlecommanddotDotOutputhToStringreturn$rebuildGraphWithAttributesoutputwherecommand=ifisDirthendirCommandelseundirCommandhToStringh=dos<-hGetContentshrnfs`seq`returnsrebuildGraphWithAttributesdotResult=mkGraphlnodesledgeswherelnodes=map(\(n,l)->(n,(fromJust$Map.lookupnnodeMap,l)))$labNodesgrledges=mapcreateEdges$labEdgesgrcreateEdges(f,t,l)=ifisDir||f<=tthen(f,t,getLabel(f,t))else(f,t,getLabel(t,f))wheregetLabelc=(fromJust$Map.lookupcedgeMap,l)g'=parseDotGraphdotResultns=graphNodesg'es=graphEdgesg'nodeMap=Map.fromList$map(nodeID&&&nodeAttributes)nsedgeMap=Map.fromList$map((edgeFromNodeID&&&edgeToNodeID)&&&edgeAttributes)es-- | Run the appropriate GraphViz command on the graph to get-- positional information and then combine that information back-- into the original graph.---- Graph direction is automatically inferred.graphToGraph'::(Ordb,Graphgr)=>grab->[GlobalAttributes]->(LNodea->Attributes)->(LEdgeb->Attributes)->IO(gr(AttributeNodea)(AttributeEdgeb))graphToGraph'gr=graphToGraph(isDirectedgr)gr-- | Run the appropriate GraphViz command on the clustered graph to-- get positional information and then combine that information back-- into the original graph. Note that for the edge information to-- be parsed properly when using multiple edges, each edge between-- two nodes needs to have a unique label.---- The 'Bool' argument is 'True' for directed graphs, 'False'-- otherwise. Directed graphs are passed through /dot/, and-- undirected graphs through /neato/.clusterGraphToGraph::(Ordc,Graphgr)=>Bool->grab->[GlobalAttributes]->(LNodea->NodeClusterca)->(c->MaybeGraphID)->(c->[GlobalAttributes])->(LNodea->Attributes)->(LEdgeb->Attributes)->IO(gr(AttributeNodea)(AttributeEdgeb))clusterGraphToGraphisDirgrgAttsclBycIDfmtClustfmtNodefmtEdge=dotAttributesisDirgrdotwheredot=clusterGraphToDotisDirgrgAttsclBycIDfmtClustfmtNodefmtEdge-- | Run the appropriate GraphViz command on the clustered graph to-- get positional information and then combine that information back-- into the original graph.---- Graph direction is automatically inferred.clusterGraphToGraph'::(Ordb,Ordc,Graphgr)=>grab->[GlobalAttributes]->(LNodea->NodeClusterca)->(c->MaybeGraphID)->(c->[GlobalAttributes])->(LNodea->Attributes)->(LEdgeb->Attributes)->IO(gr(AttributeNodea)(AttributeEdgeb))clusterGraphToGraph'gr=clusterGraphToGraph(isDirectedgr)gr-- | Pass the graph through 'graphToGraph' with no 'Attribute's. 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.---- The 'Bool' argument is 'True' for directed graphs, 'False'-- otherwise. Directed graphs are passed through /dot/, and-- undirected graphs through /neato/.dotizeGraph::(Graphgr)=>Bool->grab->gr(AttributeNodea)(AttributeEdgeb)dotizeGraphisDirg=unsafePerformIO$graphToGraphisDirggAttrsnoAttrsnoAttrswheregAttrs=[]noAttrs=const[]-- | Pass the graph through 'graphToGraph' with no 'Attribute's. 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.---- The graph direction is automatically inferred.dotizeGraph'::(Graphgr,Ordb)=>grab->gr(AttributeNodea)(AttributeEdgeb)dotizeGraph'g=dotizeGraph(isDirectedg)g-- | Pass the clustered graph through 'clusterGraphToGraph' with no-- 'Attribute's. 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.---- The 'Bool' argument is 'True' for directed graphs, 'False'-- otherwise. Directed graphs are passed through /dot/, and-- undirected graphs through /neato/.dotizeClusterGraph::(Ordc,Graphgr)=>Bool->grab->(LNodea->NodeClusterca)->gr(AttributeNodea)(AttributeEdgeb)dotizeClusterGraphisDirgclustBy=unsafePerformIO$clusterGraphToGraphisDirggAttrsclustBycIDcAttrsnoAttrsnoAttrswheregAttrs=[]cID=constNothingcAttrs=constgAttrsnoAttrs=const[]-- | Pass the clustered graph through 'graphToGraph' with no-- 'Attribute's. 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.---- The graph direction is automatically inferred.dotizeClusterGraph'::(Ordb,Ordc,Graphgr)=>grab->(LNodea->NodeClusterca)->gr(AttributeNodea)(AttributeEdgeb)dotizeClusterGraph'g=dotizeClusterGraph(isDirectedg)g