combinat-0.2.10.1: src/Math/Combinat/Trees/Graphviz.hs
-- | Creates graphviz @.dot@ files from trees.
module Math.Combinat.Trees.Graphviz
( Dot
, graphvizDotBinTree
, graphvizDotBinTree'
, graphvizDotForest
, graphvizDotTree
)
where
--------------------------------------------------------------------------------
import Data.Tree
import Control.Applicative
import {-# SOURCE #-} Math.Combinat.Trees.Binary ( BinTree(..) , BinTree'(..) )
import {-# SOURCE #-} Math.Combinat.Trees.Nary ( addUniqueLabelsTree , addUniqueLabelsForest )
--------------------------------------------------------------------------------
type Dot = String
digraphBracket :: String -> [String] -> String
digraphBracket name lines =
"digraph " ++ name ++ " {\n" ++
concatMap (\xs -> " "++xs++"\n") lines
++ "}\n"
--------------------------------------------------------------------------------
graphvizDotBinTree :: Show a => String -> BinTree a -> Dot
graphvizDotBinTree graphname tree =
digraphBracket graphname $ binTreeDot' tree
graphvizDotBinTree' :: (Show a, Show b) => String -> BinTree' a b -> Dot
graphvizDotBinTree' graphname tree =
digraphBracket graphname $ binTree'Dot' tree
binTreeDot' :: Show a => BinTree a -> [String]
binTreeDot' tree = lines where
lines = worker (0::Int) "r" tree
name path = "node_"++path
worker depth path (Leaf x) =
[ name path ++ "[shape=box,label=\"" ++ show x ++ "\"" ++ "];" ]
worker depth path (Branch left right)
= [vertex,leftedge,rightedge] ++
worker (depth+1) ('l':path) left ++
worker (depth+1) ('r':path) right
where
vertex = name path ++ "[shape=circle,style=filled,height=0.25,label=\"\"];"
leftedge = name path ++ " -> " ++ name ('l':path) ++ "[tailport=sw];"
rightedge = name path ++ " -> " ++ name ('r':path) ++ "[tailport=se];"
binTree'Dot' :: (Show a, Show b) => BinTree' a b -> [String]
binTree'Dot' tree = lines where
lines = worker (0::Int) "r" tree
name path = "node_"++path
worker depth path (Leaf' x) =
[ name path ++ "[shape=box,label=\"" ++ show x ++ "\"" ++ "];" ]
worker depth path (Branch' left y right)
= [vertex,leftedge,rightedge] ++
worker (depth+1) ('l':path) left ++
worker (depth+1) ('r':path) right
where
vertex = name path ++ "[shape=ellipse,label=\"" ++ show y ++ "\"];"
leftedge = name path ++ " -> " ++ name ('l':path) ++ "[tailport=sw];"
rightedge = name path ++ " -> " ++ name ('r':path) ++ "[tailport=se];"
--------------------------------------------------------------------------------
-- | Generates graphviz @.dot@ file from a forest. The first argument tells whether
-- to make the individual trees clustered subgraphs; the second is the name of the
-- graph.
graphvizDotForest
:: Show a
=> Bool -- ^ make the individual trees clustered subgraphs
-> Bool -- ^ reverse the direction of the arrows
-> String -- ^ name of the graph
-> Forest a
-> Dot
graphvizDotForest clustered revarrows graphname forest = digraphBracket graphname lines where
lines = concat $ zipWith cluster [(1::Int)..] (addUniqueLabelsForest forest)
name unique = "node_"++show unique
cluster j tree = let treelines = worker (0::Int) tree in case clustered of
False -> treelines
True -> ("subgraph cluster_"++show j++" {") : map (" "++) treelines ++ ["}"]
worker depth (Node (label,unique) subtrees) = vertex : edges ++ concatMap (worker (depth+1)) subtrees where
vertex = name unique ++ "[label=\"" ++ show label ++ "\"" ++ "];"
edges = map edge subtrees
edge (Node (_,unique') _) = if not revarrows
then name unique ++ " -> " ++ name unique'
else name unique' ++ " -> " ++ name unique
-- | Generates graphviz @.dot@ file from a tree. The first argument is
-- the name of the graph.
graphvizDotTree
:: Show a
=> Bool -- ^ reverse the direction of the arrow
-> String -- ^ name of the graph
-> Tree a
-> Dot
graphvizDotTree revarrows graphname tree = digraphBracket graphname lines where
lines = worker (0::Int) (addUniqueLabelsTree tree)
name unique = "node_"++show unique
worker depth (Node (label,unique) subtrees) = vertex : edges ++ concatMap (worker (depth+1)) subtrees where
vertex = name unique ++ "[label=\"" ++ show label ++ "\"" ++ "];"
edges = map edge subtrees
edge (Node (_,unique') _) = if not revarrows
then name unique ++ " -> " ++ name unique'
else name unique' ++ " -> " ++ name unique
--------------------------------------------------------------------------------