graphviz 2999.10.0.1 → 2999.11.0.0
raw patch · 10 files changed
+451/−226 lines, 10 filesdep ~QuickCheckdep ~bytestringPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: QuickCheck, bytestring
API changes (from Hackage documentation)
- Data.GraphViz: instance (Eq el) => Eq (EdgeID el)
- Data.GraphViz: instance (Ord el) => Ord (EdgeID el)
- Data.GraphViz: instance (Show el) => Show (EdgeID el)
- Data.GraphViz.Attributes: data LayerList
- Data.GraphViz.Parsing: instance (ParseDot a) => ParseDot [a]
- Data.GraphViz.Printing: instance (PrintDot a) => PrintDot [a]
- Data.GraphViz.Types: instance (Eq a) => Eq (DotGraph a)
- Data.GraphViz.Types: instance (Eq a) => Eq (DotStatements a)
- Data.GraphViz.Types: instance (Eq a) => Eq (DotSubGraph a)
- Data.GraphViz.Types: instance (Ord a) => Ord (DotGraph a)
- Data.GraphViz.Types: instance (Ord a) => Ord (DotStatements a)
- Data.GraphViz.Types: instance (Ord a) => Ord (DotSubGraph a)
- Data.GraphViz.Types: instance (ParseDot a) => ParseDot (DotGraph a)
- Data.GraphViz.Types: instance (ParseDot a) => ParseDot (DotStatements a)
- Data.GraphViz.Types: instance (ParseDot a) => ParseDot (DotSubGraph a)
- Data.GraphViz.Types: instance (PrintDot a) => PrintDot (DotGraph a)
- Data.GraphViz.Types: instance (PrintDot a) => PrintDot (DotStatements a)
- Data.GraphViz.Types: instance (PrintDot a) => PrintDot (DotSubGraph a)
- Data.GraphViz.Types: instance (Read a) => Read (DotGraph a)
- Data.GraphViz.Types: instance (Read a) => Read (DotStatements a)
- Data.GraphViz.Types: instance (Read a) => Read (DotSubGraph a)
- Data.GraphViz.Types: instance (Show a) => Show (DotGraph a)
- Data.GraphViz.Types: instance (Show a) => Show (DotStatements a)
- Data.GraphViz.Types: instance (Show a) => Show (DotSubGraph a)
- Data.GraphViz.Types.Generalised: instance (Eq a) => Eq (GDotGraph a)
- Data.GraphViz.Types.Generalised: instance (Eq a) => Eq (GDotStatement a)
- Data.GraphViz.Types.Generalised: instance (Eq a) => Eq (GDotSubGraph a)
- Data.GraphViz.Types.Generalised: instance (Ord a) => Ord (GDotGraph a)
- Data.GraphViz.Types.Generalised: instance (Ord a) => Ord (GDotStatement a)
- Data.GraphViz.Types.Generalised: instance (Ord a) => Ord (GDotSubGraph a)
- Data.GraphViz.Types.Generalised: instance (ParseDot a) => ParseDot (GDotGraph a)
- Data.GraphViz.Types.Generalised: instance (ParseDot a) => ParseDot (GDotStatement a)
- Data.GraphViz.Types.Generalised: instance (ParseDot a) => ParseDot (GDotSubGraph a)
- Data.GraphViz.Types.Generalised: instance (PrintDot a) => PrintDot (GDotGraph a)
- Data.GraphViz.Types.Generalised: instance (PrintDot a) => PrintDot (GDotStatement a)
- Data.GraphViz.Types.Generalised: instance (PrintDot a) => PrintDot (GDotSubGraph a)
- Data.GraphViz.Types.Generalised: instance (Read a) => Read (GDotGraph a)
- Data.GraphViz.Types.Generalised: instance (Read a) => Read (GDotStatement a)
- Data.GraphViz.Types.Generalised: instance (Read a) => Read (GDotSubGraph a)
- Data.GraphViz.Types.Generalised: instance (Show a) => Show (GDotGraph a)
- Data.GraphViz.Types.Generalised: instance (Show a) => Show (GDotStatement a)
- Data.GraphViz.Types.Generalised: instance (Show a) => Show (GDotSubGraph a)
+ Data.GraphViz: instance Eq el => Eq (EdgeID el)
+ Data.GraphViz: instance Ord el => Ord (EdgeID el)
+ Data.GraphViz: instance Show el => Show (EdgeID el)
+ Data.GraphViz.Attributes: class Labellable a
+ Data.GraphViz.Attributes: createPoint :: Double -> Double -> Point
+ Data.GraphViz.Attributes: forcePos :: Point -> Bool
+ Data.GraphViz.Attributes: instance Labellable (PortName, EscString)
+ Data.GraphViz.Attributes: instance Labellable Bool
+ Data.GraphViz.Attributes: instance Labellable Char
+ Data.GraphViz.Attributes: instance Labellable Double
+ Data.GraphViz.Attributes: instance Labellable EscString
+ Data.GraphViz.Attributes: instance Labellable HtmlLabel
+ Data.GraphViz.Attributes: instance Labellable HtmlTable
+ Data.GraphViz.Attributes: instance Labellable HtmlText
+ Data.GraphViz.Attributes: instance Labellable Int
+ Data.GraphViz.Attributes: instance Labellable PortName
+ Data.GraphViz.Attributes: instance Labellable RecordField
+ Data.GraphViz.Attributes: instance Labellable RecordFields
+ Data.GraphViz.Attributes: newtype LayerList
+ Data.GraphViz.Attributes: toLabel :: Labellable a => a -> Attribute
+ Data.GraphViz.Attributes: xCoord :: Point -> Double
+ Data.GraphViz.Attributes: yCoord :: Point -> Double
+ Data.GraphViz.Attributes: zCoord :: Point -> Maybe Double
+ Data.GraphViz.Parsing: instance ParseDot a => ParseDot [a]
+ Data.GraphViz.Printing: instance PrintDot a => PrintDot [a]
+ Data.GraphViz.Types: instance Eq a => Eq (DotGraph a)
+ Data.GraphViz.Types: instance Eq a => Eq (DotStatements a)
+ Data.GraphViz.Types: instance Eq a => Eq (DotSubGraph a)
+ Data.GraphViz.Types: instance Ord a => Ord (DotGraph a)
+ Data.GraphViz.Types: instance Ord a => Ord (DotStatements a)
+ Data.GraphViz.Types: instance Ord a => Ord (DotSubGraph a)
+ Data.GraphViz.Types: instance ParseDot a => ParseDot (DotGraph a)
+ Data.GraphViz.Types: instance ParseDot a => ParseDot (DotStatements a)
+ Data.GraphViz.Types: instance ParseDot a => ParseDot (DotSubGraph a)
+ Data.GraphViz.Types: instance PrintDot a => PrintDot (DotGraph a)
+ Data.GraphViz.Types: instance PrintDot a => PrintDot (DotStatements a)
+ Data.GraphViz.Types: instance PrintDot a => PrintDot (DotSubGraph a)
+ Data.GraphViz.Types: instance Read a => Read (DotGraph a)
+ Data.GraphViz.Types: instance Read a => Read (DotStatements a)
+ Data.GraphViz.Types: instance Read a => Read (DotSubGraph a)
+ Data.GraphViz.Types: instance Show a => Show (DotGraph a)
+ Data.GraphViz.Types: instance Show a => Show (DotStatements a)
+ Data.GraphViz.Types: instance Show a => Show (DotSubGraph a)
+ Data.GraphViz.Types.Generalised: instance Eq a => Eq (GDotGraph a)
+ Data.GraphViz.Types.Generalised: instance Eq a => Eq (GDotStatement a)
+ Data.GraphViz.Types.Generalised: instance Eq a => Eq (GDotSubGraph a)
+ Data.GraphViz.Types.Generalised: instance Ord a => Ord (GDotGraph a)
+ Data.GraphViz.Types.Generalised: instance Ord a => Ord (GDotStatement a)
+ Data.GraphViz.Types.Generalised: instance Ord a => Ord (GDotSubGraph a)
+ Data.GraphViz.Types.Generalised: instance ParseDot a => ParseDot (GDotGraph a)
+ Data.GraphViz.Types.Generalised: instance ParseDot a => ParseDot (GDotStatement a)
+ Data.GraphViz.Types.Generalised: instance ParseDot a => ParseDot (GDotSubGraph a)
+ Data.GraphViz.Types.Generalised: instance PrintDot a => PrintDot (GDotGraph a)
+ Data.GraphViz.Types.Generalised: instance PrintDot a => PrintDot (GDotStatement a)
+ Data.GraphViz.Types.Generalised: instance PrintDot a => PrintDot (GDotSubGraph a)
+ Data.GraphViz.Types.Generalised: instance Read a => Read (GDotGraph a)
+ Data.GraphViz.Types.Generalised: instance Read a => Read (GDotStatement a)
+ Data.GraphViz.Types.Generalised: instance Read a => Read (GDotSubGraph a)
+ Data.GraphViz.Types.Generalised: instance Show a => Show (GDotGraph a)
+ Data.GraphViz.Types.Generalised: instance Show a => Show (GDotStatement a)
+ Data.GraphViz.Types.Generalised: instance Show a => Show (GDotSubGraph a)
- Data.GraphViz: addEdgeIDs :: (Graph gr) => gr nl el -> gr nl (EdgeID el)
+ Data.GraphViz: addEdgeIDs :: Graph gr => gr nl el -> gr nl (EdgeID el)
- Data.GraphViz: prettyPrint :: (DotRepr dg n) => dg n -> IO String
+ Data.GraphViz: prettyPrint :: DotRepr dg n => dg n -> IO String
- Data.GraphViz: prettyPrint' :: (DotRepr dg n) => dg n -> String
+ Data.GraphViz: prettyPrint' :: DotRepr dg n => dg n -> String
- Data.GraphViz: preview :: (Ord el, Graph gr) => gr nl el -> IO ()
+ Data.GraphViz: preview :: (Ord el, Graph gr, Labellable nl, Labellable el) => gr nl el -> IO ()
- Data.GraphViz.Attributes: LL :: String -> [(String, String)] -> LayerList
+ Data.GraphViz.Attributes: LL :: [LayerID] -> LayerList
- Data.GraphViz.Attributes: LRS :: LayerID -> String -> LayerID -> LayerRange
+ Data.GraphViz.Attributes: LRS :: LayerID -> LayerID -> LayerRange
- Data.GraphViz.Attributes: Point :: Double -> Double -> Point
+ Data.GraphViz.Attributes: Point :: Double -> Double -> Maybe Double -> Bool -> Point
- Data.GraphViz.Commands: commandFor :: (DotRepr dg n) => dg n -> GraphvizCommand
+ Data.GraphViz.Commands: commandFor :: DotRepr dg n => dg n -> GraphvizCommand
- Data.GraphViz.Commands: graphvizWithHandle :: (DotRepr dg n) => GraphvizCommand -> dg n -> GraphvizOutput -> (Handle -> IO a) -> IO (Either String a)
+ Data.GraphViz.Commands: graphvizWithHandle :: DotRepr dg n => GraphvizCommand -> dg n -> GraphvizOutput -> (Handle -> IO a) -> IO (Either String a)
- Data.GraphViz.Commands: runGraphviz :: (DotRepr dg n) => dg n -> GraphvizOutput -> FilePath -> IO (Either String FilePath)
+ Data.GraphViz.Commands: runGraphviz :: DotRepr dg n => dg n -> GraphvizOutput -> FilePath -> IO (Either String FilePath)
- Data.GraphViz.Commands: runGraphvizCanvas :: (DotRepr dg n) => GraphvizCommand -> dg n -> GraphvizCanvas -> IO RunResult
+ Data.GraphViz.Commands: runGraphvizCanvas :: DotRepr dg n => GraphvizCommand -> dg n -> GraphvizCanvas -> IO RunResult
- Data.GraphViz.Commands: runGraphvizCanvas' :: (DotRepr dg n) => dg n -> GraphvizCanvas -> IO RunResult
+ Data.GraphViz.Commands: runGraphvizCanvas' :: DotRepr dg n => dg n -> GraphvizCanvas -> IO RunResult
- Data.GraphViz.Commands: runGraphvizCommand :: (DotRepr dg n) => GraphvizCommand -> dg n -> GraphvizOutput -> FilePath -> IO (Either String FilePath)
+ Data.GraphViz.Commands: runGraphvizCommand :: DotRepr dg n => GraphvizCommand -> dg n -> GraphvizOutput -> FilePath -> IO (Either String FilePath)
- Data.GraphViz.Parsing: noneOf :: (Eq a) => [a] -> Parser a a
+ Data.GraphViz.Parsing: noneOf :: Eq a => [a] -> Parser a a
- Data.GraphViz.Parsing: parse :: (ParseDot a) => Parse a
+ Data.GraphViz.Parsing: parse :: ParseDot a => Parse a
- Data.GraphViz.Parsing: parseEscaped :: Bool -> [Char] -> Parse String
+ Data.GraphViz.Parsing: parseEscaped :: Bool -> [Char] -> [Char] -> Parse String
- Data.GraphViz.Parsing: parseField :: (ParseDot a) => (a -> b) -> String -> Parse b
+ Data.GraphViz.Parsing: parseField :: ParseDot a => (a -> b) -> String -> Parse b
- Data.GraphViz.Parsing: parseFieldDef :: (ParseDot a) => (a -> b) -> a -> String -> Parse b
+ Data.GraphViz.Parsing: parseFieldDef :: ParseDot a => (a -> b) -> a -> String -> Parse b
- Data.GraphViz.Parsing: parseFields :: (ParseDot a) => (a -> b) -> [String] -> Parse b
+ Data.GraphViz.Parsing: parseFields :: ParseDot a => (a -> b) -> [String] -> Parse b
- Data.GraphViz.Parsing: parseFieldsDef :: (ParseDot a) => (a -> b) -> a -> [String] -> Parse b
+ Data.GraphViz.Parsing: parseFieldsDef :: ParseDot a => (a -> b) -> a -> [String] -> Parse b
- Data.GraphViz.Parsing: parseIt :: (ParseDot a) => String -> (a, String)
+ Data.GraphViz.Parsing: parseIt :: ParseDot a => String -> (a, String)
- Data.GraphViz.Parsing: parseIt' :: (ParseDot a) => String -> a
+ Data.GraphViz.Parsing: parseIt' :: ParseDot a => String -> a
- Data.GraphViz.Parsing: parseList :: (ParseDot a) => Parse [a]
+ Data.GraphViz.Parsing: parseList :: ParseDot a => Parse [a]
- Data.GraphViz.Parsing: parseUnqt :: (ParseDot a) => Parse a
+ Data.GraphViz.Parsing: parseUnqt :: ParseDot a => Parse a
- Data.GraphViz.Parsing: parseUnqtList :: (ParseDot a) => Parse [a]
+ Data.GraphViz.Parsing: parseUnqtList :: ParseDot a => Parse [a]
- Data.GraphViz.Parsing: tryParseList :: (ParseDot a) => Parse [a]
+ Data.GraphViz.Parsing: tryParseList :: ParseDot a => Parse [a]
- Data.GraphViz.Printing: listToDot :: (PrintDot a) => [a] -> DotCode
+ Data.GraphViz.Printing: listToDot :: PrintDot a => [a] -> DotCode
- Data.GraphViz.Printing: printField :: (PrintDot a) => String -> a -> DotCode
+ Data.GraphViz.Printing: printField :: PrintDot a => String -> a -> DotCode
- Data.GraphViz.Printing: printIt :: (PrintDot a) => a -> String
+ Data.GraphViz.Printing: printIt :: PrintDot a => a -> String
- Data.GraphViz.Printing: toDot :: (PrintDot a) => a -> DotCode
+ Data.GraphViz.Printing: toDot :: PrintDot a => a -> DotCode
- Data.GraphViz.Printing: unqtDot :: (PrintDot a) => a -> DotCode
+ Data.GraphViz.Printing: unqtDot :: PrintDot a => a -> DotCode
- Data.GraphViz.Printing: unqtListToDot :: (PrintDot a) => [a] -> DotCode
+ Data.GraphViz.Printing: unqtListToDot :: PrintDot a => [a] -> DotCode
- Data.GraphViz.Types: edgeInformation :: (DotRepr dg n) => Bool -> dg n -> [DotEdge n]
+ Data.GraphViz.Types: edgeInformation :: DotRepr dg n => Bool -> dg n -> [DotEdge n]
- Data.GraphViz.Types: getID :: (DotRepr dg n) => dg n -> Maybe GraphID
+ Data.GraphViz.Types: getID :: DotRepr dg n => dg n -> Maybe GraphID
- Data.GraphViz.Types: graphEdges :: (DotRepr dg n) => dg n -> [DotEdge n]
+ Data.GraphViz.Types: graphEdges :: DotRepr dg n => dg n -> [DotEdge n]
- Data.GraphViz.Types: graphIsDirected :: (DotRepr dg n) => dg n -> Bool
+ Data.GraphViz.Types: graphIsDirected :: DotRepr dg n => dg n -> Bool
- Data.GraphViz.Types: graphIsStrict :: (DotRepr dg n) => dg n -> Bool
+ Data.GraphViz.Types: graphIsStrict :: DotRepr dg n => dg n -> Bool
- Data.GraphViz.Types: graphStructureInformation :: (DotRepr dg n) => dg n -> (GlobalAttributes, ClusterLookup)
+ Data.GraphViz.Types: graphStructureInformation :: DotRepr dg n => dg n -> (GlobalAttributes, ClusterLookup)
- Data.GraphViz.Types: makeStrict :: (DotRepr dg n) => dg n -> dg n
+ Data.GraphViz.Types: makeStrict :: DotRepr dg n => dg n -> dg n
- Data.GraphViz.Types: nodeInformation :: (DotRepr dg n) => Bool -> dg n -> NodeLookup n
+ Data.GraphViz.Types: nodeInformation :: DotRepr dg n => Bool -> dg n -> NodeLookup n
- Data.GraphViz.Types: parseDotGraph :: (DotRepr dg n) => String -> dg n
+ Data.GraphViz.Types: parseDotGraph :: DotRepr dg n => String -> dg n
- Data.GraphViz.Types: printDotGraph :: (DotRepr dg n) => dg n -> String
+ Data.GraphViz.Types: printDotGraph :: DotRepr dg n => dg n -> String
- Data.GraphViz.Types: setID :: (DotRepr dg n) => GraphID -> dg n -> dg n
+ Data.GraphViz.Types: setID :: DotRepr dg n => GraphID -> dg n -> dg n
Files
- Changelog +25/−0
- Data/GraphViz.hs +85/−21
- Data/GraphViz/Attributes.hs +192/−105
- Data/GraphViz/Attributes/Internal.hs +2/−2
- Data/GraphViz/Commands.hs +14/−11
- Data/GraphViz/Parsing.hs +17/−10
- Data/GraphViz/Printing.hs +15/−4
- Data/GraphViz/Testing/Instances.hs +48/−41
- FAQ +49/−28
- graphviz.cabal +4/−4
Changelog view
@@ -7,6 +7,31 @@ The following is information about what major changes have gone into each release. +Changes in 2999.11.0.0+----------------------++* Addition of the `Labellable` class (and its method `toLabel`) to+ make it easier to construct labels.++* Backslashes (i.e. the `\` character) are now escaped/unescaped+ properly (bug spotted by **Han Joosten**). As part of this:++ - Dot-specific escapes such as `\N` are now also handled+ correctly, so the slash does not need to be escaped.++ - Newline (`'\n'`) characters in labels, etc. are escaped to+ centred-newlines in Dot code, but not unescaped.++* `Point` values can now have the optional third dimension and end in+ a `!` to indicate that that position should be used (as input to+ Graphviz).++* `LayerList` uses `LayerID` values, and now has a proper `shrink`+ implementation in the test suite.++* The optional `z` coordinate and `!` argument for `Point` values are+ now supported.+ Changes in 2999.10.0.1 ----------------------
Data/GraphViz.hs view
@@ -1,6 +1,4 @@-{-# LANGUAGE MultiParamTypeClasses- , FlexibleContexts- #-}+{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-} {- | Module : Data.GraphViz@@ -21,16 +19,17 @@ module Data.GraphViz ( -- * Conversion from graphs to /Dot/ format. -- ** Specifying parameters.+ -- $params GraphvizParams(..) , defaultParams , nonClusteredParams , blankParams , setDirectedness- -- ** Converting graphs.- , graphToDot- -- ** Conversion with support for clusters.+ -- *** Specifying clusters. , LNodeCluster , NodeCluster(..)+ -- ** Converting graphs.+ , graphToDot -- ** Pseudo-inverse conversion. , dotToGraph -- * Graph augmentation.@@ -88,6 +87,62 @@ -- ----------------------------------------------------------------------------- +{- $params++ A 'GraphvizParams' value contains all the information necessary to+ manipulate 'Graph's with this library. As such, its components deal+ with:++ * Whether to treat graphs as being directed or not;++ * Which top-level 'GlobalAttributes' values should be applied;++ * How to define (and name) clusters;++ * How to format clusters, nodes and edges.++ Apart from not having to pass multiple values around, another+ advantage of using 'GraphvizParams' over the previous approach is that+ there is no distinction between clustering and non-clustering variants+ of the same functions.++ Example usages of 'GraphvizParams' follow:++ * Quickly visualise a graph using the default parameters. Note the+ usage of @'nonClusteredParams'@ over @'defaultParams'@ to avoid+ type-checking problems with the cluster type.++ > defaultVis :: (Graph gr) => gr nl el -> DotGraph Node+ > defaultVis = graphToDot nonClusteredParams++ * As with @defaultVis@, but determine whether or not the graph is+ directed or undirected.++ > checkDirectednessVis :: (Graph gr, Ord el) => gr nl el -> DotGraph Node+ > checkDirectednessVis = setDirectedness graphToDot nonClusteredParams++ * Clustering nodes based upon whether they are even or odd. We have+ the option of either constructing a @GraphvizParams@ directly, or+ using @'blankParams'@. Going with the latter to avoid setting+ @'isDirected'@.++ > evenOdd :: (Graph gr, Ord el) => gr Int el -> DotGraph Node+ > evenOdd = setDirectedness graphToDot params+ > where+ > params = blankParams { globalAttributes = []+ > , clusterBy = clustBy+ > , clusterID = Just . Int+ > , fmtCluster = clFmt+ > , fmtNode = const []+ > , fmtEdge = const []+ > }+ > clustBy (n,l) = C (n `mod` 2) $ N (n,l)+ > clFmt m = [GraphAttrs [toLabel $ "n == " ++ show m ++ " (mod 2)"]]++ For more examples, see the source of 'dotizeGraph' and 'preview'.++-}+ -- | Defines the parameters used to convert a 'Graph' into a 'DotRepr'. -- -- A value of type @'GraphvizParams' nl el cl l@ indicates that the@@ -98,32 +153,33 @@ -- -- The clustering in 'clusterBy' can be to arbitrary depth. data GraphvizParams nl el cl l- = Params { -- | 'True' if the 'Graph' is directed; 'False'+ = Params { -- | @True@ if the graph is directed; @False@ -- otherwise. isDirected :: Bool -- | The top-level global 'Attributes' for the entire- -- 'Graph'.+ -- graph. , globalAttributes :: [GlobalAttributes] -- | A function to specify which cluster a particular -- 'LNode' is in. , clusterBy :: (LNode nl -> LNodeCluster cl l)- -- | The 'GraphID' for a cluster.+ -- | The name/identifier for a cluster. , clusterID :: (cl -> Maybe GraphID) -- | Specify which global attributes are applied in -- the given cluster. , fmtCluster :: (cl -> [GlobalAttributes])- -- | The specific 'Attributes' for that 'LNode'.+ -- | The specific @Attributes@ for a node. , fmtNode :: (LNode l -> Attributes)- -- | The specific 'Attributes' for that 'LEdge'.+ -- | The specific @Attributes@ for an edge. , fmtEdge :: (LEdge el -> Attributes) } -- | A default 'GraphvizParams' value which assumes the graph is -- directed, contains no clusters and has no 'Attribute's set. ----- If you wish to have the labels of the nodes after applying--- 'clusterBy' to have a different from before clustering, then you--- will have to specify your own 'GraphvizParams' value from scratch.+-- If you wish to have the labels of the nodes to have a different+-- type after applying 'clusterBy' from before clustering, then you+-- will have to specify your own 'GraphvizParams' value from+-- scratch (or use 'blankParams'). defaultParams :: GraphvizParams nl el cl nl defaultParams = Params { isDirected = True , globalAttributes = []@@ -135,9 +191,9 @@ } -- | A variant of 'defaultParams' that enforces that the clustering--- type is @'()'@; this avoids problems when using 'defaultParams'--- internally within a function without any constraint on what the--- clustering type is.+-- type is @'()'@ (i.e.: no clustering); this avoids problems when+-- using 'defaultParams' internally within a function without any+-- constraint on what the clustering type is. nonClusteredParams :: GraphvizParams nl el () nl nonClusteredParams = defaultParams @@ -145,7 +201,9 @@ -- @'undefined'@. This is useful when you have a function that will -- set some of the values for you (e.g. 'setDirectedness') but you -- don't want to bother thinking of default values to set in the--- meantime.+-- meantime. This is especially useful when you are+-- programmatically setting the clustering function (and as such do+-- not know what the types might be). blankParams :: GraphvizParams nl el cl l blankParams = Params { isDirected = undefined , globalAttributes = undefined@@ -415,11 +473,17 @@ canonicalise :: (DotRepr dg n, DotRepr DotGraph n) => dg n -> IO (DotGraph n) canonicalise = liftM parseDotGraph . prettyPrint --- | Quickly visualise a graph using the 'Xlib' 'GraphvizCanvas'.-preview :: (Ord el, Graph gr) => gr nl el -> IO ()+-- | Quickly visualise a graph using the 'Xlib' 'GraphvizCanvas'. If+-- your label types are not (and cannot) be instances of 'Labellable',+-- you may wish to use 'gmap', 'nmap' or 'emap' to set them to a value+-- such as @\"\"@.+preview :: (Ord el, Graph gr, Labellable nl, Labellable el) => gr nl el -> IO () preview g = ign $ forkIO (ign $ runGraphvizCanvas' dg Xlib) where- dg = setDirectedness graphToDot nonClusteredParams g+ dg = setDirectedness graphToDot params g+ params = nonClusteredParams { fmtNode = \ (_,l) -> [toLabel l]+ , fmtEdge = \ (_, _, l) -> [toLabel l]+ } ign = (>> return ()) -- | Used for obtaining results from 'graphvizWithHandle', etc. when
Data/GraphViz/Attributes.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}+ {- | Module : Data.GraphViz.Attributes Description : Definition of the Graphviz attributes.@@ -27,11 +29,12 @@ (since /A/ is at the tail end of the arrow). * @ColorList@, @DoubleList@ and @PointfList@ are defined as actual- lists (but 'LayerList' is not). All of these are assumed to be- non-empty lists. Note that for the @Color@ 'Attribute' for node- values, only a single Color is valid; edges are allowed multiple- colors with one spline/arrow per color in the list (but you must have- at least one 'Color' in the list). This might be changed in future.+ lists (@'LayerList'@ needs a newtype for other reasons). All of these+ are assumed to be non-empty lists. Note that for the @Color@+ 'Attribute' for node values, only a single Color is valid; edges are+ allowed multiple colors with one spline/arrow per color in the list+ (but you must have at least one 'Color' in the list). This might be+ changed in future. * Style is implemented as a list of 'StyleItem' values; note that empty lists are not allowed.@@ -41,10 +44,8 @@ expanded upon to give an idea of what they represent rather than using generic terms. - * @PointF@ and 'Point' have been combined, and feature support for pure- 'Int'-based co-ordinates as well as 'Double' ones (i.e. no floating- point-only points for Point). The optional '!' and third value- for Point are not available.+ * @PointF@ and 'Point' have been combined. The optional '!' and+ third value for Point are also available. * 'Rect' uses two 'Point' values to denote the lower-left and top-right corners.@@ -79,63 +80,41 @@ , usedByClusters , usedByNodes , usedByEdges+ -- * Value types for @Attribute@s.+ , module Data.GraphViz.Attributes.Colors++ -- ** Labels , EscString- , ArrowType(..)- , AspectType(..)- , Rect(..)- , ClusterMode(..)- , DirType(..)- , DEConstraints(..)- , DPoint(..)- , ModeType(..)- , Model(..) , Label(..)- , Point(..)- , Overlap(..)- , LayerRange(..)- , LayerID(..)- , LayerList(..)- , OutputMode(..)- , Pack(..)- , PackMode(..)- , Pos(..)- , EdgeType(..)- , PageDir(..)- , Spline(..)- , QuadType(..)- , Root(..)- , RankType(..)- , RankDir(..)- , Shape(..)- , SmoothType(..)- , StartType(..)- , STStyle(..)- , StyleItem(..)- , StyleName(..)- , ViewPort(..)- , FocusType(..)+ , Labellable(..) , VerticalPlacement(..)- , ScaleType(..)+ , module Data.GraphViz.Attributes.HTML+ -- *** Types representing the Dot grammar for records.+ , RecordFields+ , RecordField(..)+ , Rect(..) , Justification(..)- , Ratios(..)- -- Re-exported from Data.GraphViz.Attributes.Internal++ -- ** Nodes+ , Shape(..)+ , ScaleType(..)++ -- ** Edges+ , DirType(..)+ , EdgeType(..)+ -- *** Modifying where edges point , PortName(..) , PortPos(..) , CompassPoint(..)- -- Re-exporting modules- , module Data.GraphViz.Attributes.Colors- , module Data.GraphViz.Attributes.HTML- -- * Types representing the Dot grammar for records.- , RecordFields- , RecordField(..)- -- * Types representing the Dot grammar for @ArrowType@.+ -- *** Arrows+ , ArrowType(..) , ArrowShape(..) , ArrowModifier(..) , ArrowFill(..) , ArrowSide(..)- -- ** Default @ArrowType@ aliases.- -- *** The 9 primitive @ArrowShape@s.+ -- **** Default @ArrowType@ aliases.+ -- ***** The 9 primitive @ArrowShape@s. , box , crow , diamond@@ -145,24 +124,63 @@ , normal , tee , vee- -- *** 5 derived Arrows.+ -- ***** 5 derived Arrows. , oDot , invDot , invODot , oBox , oDiamond- -- *** 5 supported cases for backwards compatibility+ -- ***** 5 supported cases for backwards compatibility , eDiamond , openArr , halfOpen , emptyArr , invEmpty- -- ** @ArrowModifier@ instances+ -- **** @ArrowModifier@ instances , noMods , openMod- -- * Other exported functions\/values++ -- ** Positioning+ , Point(..)+ , createPoint+ , Pos(..)+ , Spline(..)+ , DPoint(..)++ -- ** Layout+ , AspectType(..)+ , ClusterMode(..)+ , Model(..)+ , Overlap(..)+ , Root(..)+ , OutputMode(..)+ , Pack(..)+ , PackMode(..)+ , PageDir(..)+ , QuadType(..)+ , RankType(..)+ , RankDir(..)+ , StartType(..)+ , ViewPort(..)+ , FocusType(..)+ , Ratios(..)++ -- ** Modes+ , ModeType(..)+ , DEConstraints(..)++ -- ** Layers+ , LayerRange(..)+ , LayerID(..)+ , LayerList(..) , defLayerSep , notLayerSep++ -- ** Stylistic+ , SmoothType(..)+ , STStyle(..)+ , StyleItem(..)+ , StyleName(..) ) where import Data.GraphViz.Attributes.Colors@@ -1257,6 +1275,7 @@ -- ----------------------------------------------------------------------------- +-- | Should only have 2D points (i.e. created with 'createPoint'). data Rect = Rect Point Point deriving (Eq, Ord, Show, Read) @@ -1266,7 +1285,7 @@ toDot = doubleQuotes . unqtDot instance ParseDot Rect where- parseUnqt = liftM (uncurry Rect) commaSepUnqt+ parseUnqt = liftM (uncurry Rect) $ commaSep' parsePoint2D parsePoint2D parse = quotedParse parseUnqt @@ -1328,7 +1347,8 @@ -- ----------------------------------------------------------------------------- --- | Either a 'Double' or a 'Point'.+-- | Either a 'Double' or a (2D) 'Point' (i.e. created with+-- 'createPoint'). data DPoint = DVal Double | PVal Point deriving (Eq, Ord, Show, Read)@@ -1341,13 +1361,13 @@ toDot (PVal p) = toDot p instance ParseDot DPoint where- parseUnqt = liftM PVal parseUnqt+ parseUnqt = liftM PVal parsePoint2D `onFail` liftM DVal parseUnqt - parse = liftM PVal parse+ parse = quotedParse parseUnqt `onFail`- liftM DVal parse+ liftM DVal parseUnqt -- ----------------------------------------------------------------------------- @@ -1426,6 +1446,51 @@ , liftM StrLabel parse ] +-- | A convenience class to make it easier to create labels. It is+-- highly recommended that you make any other types that you wish to+-- create labels from an instance of this class, preferably via the+-- @String@ instance.+class Labellable a where+ toLabel :: a -> Attribute++instance Labellable EscString where+ toLabel = Label . StrLabel++instance Labellable Char where+ toLabel = toLabel . (:[])++instance Labellable Int where+ toLabel = toLabel . show++instance Labellable Double where+ toLabel = toLabel . show++instance Labellable Bool where+ toLabel = toLabel . show++instance Labellable HtmlLabel where+ toLabel = Label . HtmlLabel++instance Labellable HtmlText where+ toLabel = toLabel . HtmlText++instance Labellable HtmlTable where+ toLabel = toLabel . HtmlTable++instance Labellable RecordFields where+ toLabel = Label . RecordLabel++instance Labellable RecordField where+ toLabel = toLabel . (:[])++-- | A shorter variant than using @PortName@ from 'RecordField'.+instance Labellable PortName where+ toLabel = toLabel . PortName++-- | A shorter variant than using 'LabelledTarget'.+instance Labellable (PortName, EscString) where+ toLabel = toLabel . uncurry LabelledTarget+ -- ----------------------------------------------------------------------------- -- | A RecordFields value should never be empty.@@ -1489,7 +1554,7 @@ printPortName = angled . unqtRecordString . portName parseRecord :: Parse String-parseRecord = parseEscaped False recordEscChars+parseRecord = parseEscaped False recordEscChars [] unqtRecordString :: String -> DotCode unqtRecordString = unqtEscaped recordEscChars@@ -1499,11 +1564,27 @@ -- ----------------------------------------------------------------------------- -data Point = Point Double Double- deriving (Eq, Ord, Show, Read)+data Point = Point { xCoord :: Double+ , yCoord :: Double+ -- | Can only be 'Just' for @'Dim' 3@ or greater.+ , zCoord :: Maybe Double+ -- | Input to Graphviz only: specify that the+ -- node position should not change.+ , forcePos :: Bool+ }+ deriving (Eq, Ord, Show, Read) +-- | Create a point with only @x@ and @y@ values.+createPoint :: Double -> Double -> Point+createPoint x y = Point x y Nothing False++parsePoint2D :: Parse Point+parsePoint2D = liftM (uncurry createPoint) commaSepUnqt+ instance PrintDot Point where- unqtDot (Point x y) = commaDel x y+ unqtDot (Point x y mz frs) = bool id (<> char '!') frs+ . maybe id (\ z -> (<> unqtDot z) . (<> comma)) mz+ $ commaDel x y toDot = doubleQuotes . unqtDot @@ -1512,11 +1593,10 @@ listToDot = doubleQuotes . unqtListToDot instance ParseDot Point where- -- Need to take into account the situation where first value is an- -- integer, second a double: if Point parsing first, then it won't- -- parse the second number properly; but if PointD first then it- -- will treat Int/Int as Double/Double.- parseUnqt = liftM (uncurry Point) commaSepUnqt+ parseUnqt = do (x,y) <- commaSepUnqt+ mz <- optional $ parseComma >> parseUnqt+ bng <- liftM isJust . optional $ character '!'+ return $ Point x y mz bng parse = quotedParse parseUnqt @@ -1561,29 +1641,31 @@ -- ----------------------------------------------------------------------------- data LayerRange = LRID LayerID- | LRS LayerID String LayerID+ | LRS LayerID LayerID deriving (Eq, Ord, Show, Read) instance PrintDot LayerRange where- unqtDot (LRID lid) = unqtDot lid- unqtDot (LRS id1 s id2) = unqtDot id1 <> unqtDot s <> unqtDot id2+ unqtDot (LRID lid) = unqtDot lid+ unqtDot (LRS id1 id2) = unqtDot id1 <> s <> unqtDot id2+ where+ s = unqtDot $ head defLayerSep toDot (LRID lid) = toDot lid toDot lrs = doubleQuotes $ unqtDot lrs instance ParseDot LayerRange where parseUnqt = do id1 <- parseUnqt- s <- parseLayerSep+ _ <- parseLayerSep id2 <- parseUnqt- return $ LRS id1 s id2+ return $ LRS id1 id2 `onFail` liftM LRID parseUnqt parse = quotedParse ( do id1 <- parseUnqt- s <- parseLayerSep+ _ <- parseLayerSep id2 <- parseUnqt- return $ LRS id1 s id2+ return $ LRS id1 id2 ) `onFail` liftM LRID parse@@ -1592,12 +1674,12 @@ parseLayerSep = many1 . oneOf $ map character defLayerSep +-- | The default separators for 'LayerSep'. defLayerSep :: [Char] defLayerSep = [' ', ':', '\t'] parseLayerName :: Parse String-parseLayerName = many1 . orQuote- $ satisfy (liftM2 (&&) notLayerSep (quoteChar /=))+parseLayerName = parseEscaped False [] defLayerSep parseLayerName' :: Parse String parseLayerName' = stringBlock@@ -1607,11 +1689,11 @@ notLayerSep :: Char -> Bool notLayerSep = flip notElem defLayerSep --- | You should not have any quote characters for the 'LRName' option,--- as it won't be parseable.+-- | You should not have any layer separator characters for the+-- 'LRName' option, as they won't be parseable. data LayerID = AllLayers | LRInt Int- | LRName String+ | LRName String -- ^ Should not be a number of @"all"@. deriving (Eq, Ord, Show, Read) instance PrintDot LayerID where@@ -1623,6 +1705,15 @@ -- Other two don't need quotes toDot li = unqtDot li + unqtListToDot = hcat . punctuate s . map unqtDot+ where+ s = unqtDot $ head defLayerSep++ listToDot [l] = toDot l+ -- Might not need quotes, but probably will. Can't tell either+ -- way since we don't know what the separator character will be.+ listToDot ll = doubleQuotes $ unqtDot ll+ instance ParseDot LayerID where parseUnqt = liftM checkLayerName parseLayerName -- tests for Int and All @@ -1637,31 +1728,23 @@ then AllLayers else LRName str --- | The list represent (Separator, Name). You should not have any--- quote characters for any of the 'String's, since there are--- parsing problems with them.-data LayerList = LL String [(String, String)]- deriving (Eq, Ord, Show, Read)+-- | A non-empty list of layer names. The names should all be+-- 'LRName' values, and when printed will use an arbitrary character+-- from 'defLayerSep'.+newtype LayerList = LL [LayerID]+ deriving (Eq, Ord, Show, Read) instance PrintDot LayerList where- unqtDot (LL l1 ols) = unqtDot l1 <> hcat (map subLL ols)- where- subLL (s, l) = unqtDot s <> unqtDot l+ unqtDot (LL ll) = unqtDot ll - toDot (LL l1 []) = toDot l1- -- Might not need quotes, but probably will.- toDot ll = doubleQuotes $ unqtDot ll+ toDot (LL ll) = toDot ll instance ParseDot LayerList where- parseUnqt = do l1 <- parseLayerName- ols <- many $ do s <- parseLayerSep- lnm <- parseLayerName- return (s, lnm)- return $ LL l1 ols+ parseUnqt = liftM LL $ sepBy1 parseUnqt parseLayerSep - parse = quotedParse parseUnqt- `onFail`- liftM (flip LL []) (parseLayerName' `onFail` numString)+ parse = quotedParse parseUnqt+ `onFail`+ liftM (LL . (:[]) . LRName) stringBlock -- ----------------------------------------------------------------------------- @@ -2219,11 +2302,14 @@ _ -> DD str parseStyleName :: Parse String-parseStyleName = do f <- orQuote . noneOf $ ' ' : disallowedChars- r <- many (orQuote $ noneOf disallowedChars)+parseStyleName = do f <- orEscaped . noneOf $ ' ' : disallowedChars+ r <- parseEscaped True [] disallowedChars return $ f:r where disallowedChars = [quoteChar, '(', ')', ',']+ -- Used because the first character has slightly stricter requirements than the rest.+ orSlash p = stringRep '\\' "\\\\" `onFail` p+ orEscaped = orQuote . orSlash -- ----------------------------------------------------------------------------- @@ -2254,6 +2340,7 @@ parse = quotedParse parseUnqt +-- | For use with 'ViewPort'. data FocusType = XY Point | NodeFocus String deriving (Eq, Ord, Show, Read)
Data/GraphViz/Attributes/Internal.hs view
@@ -50,8 +50,8 @@ toDot = toDot . portName instance ParseDot PortName where- parseUnqt = liftM PN . many1- . orQuote $ satisfy (`notElem` ['"', ':'])+ parseUnqt = liftM PN+ $ parseEscaped False [] ['"', ':'] parse= quotedParse parseUnqt `onFail`
Data/GraphViz/Commands.hs view
@@ -59,7 +59,7 @@ import System.Exit(ExitCode(ExitSuccess)) import System.Process(runInteractiveProcess, waitForProcess) import Control.Concurrent(MVar, forkIO, newEmptyMVar, putMVar, takeMVar)-import Control.Exception.Extensible( SomeException(..), catch+import Control.Exception.Extensible( IOException, catch , bracket, evaluate, handle) import Control.Monad(liftM) import System.FilePath((<.>))@@ -355,22 +355,25 @@ ExitSuccess -> return output _ -> return $ Left $ othErr ++ err where- notRunnable e@SomeException{} = return . Left $ unwords- [ "Unable to call the Graphviz command "- , cmd'- , " with the arguments: "- , unwords args- , " because of: "- , show e- ]+ notRunnable :: IOException -> IO (Either String a)+ notRunnable e = return . Left $ unwords+ [ "Unable to call the Graphviz command "+ , cmd'+ , " with the arguments: "+ , unwords args+ , " because of: "+ , show e+ ] cmd' = showCmd cmd args = ["-T" ++ outputCall t] -- Augmenting the f function to let it work within the forkIO: f' h = liftM Right (f h) `catch`- (\e@SomeException{} -> return . Left $ fErr ++ show e)- fErr = "Error re-directing the output from " ++ cmd' ++ ": "+ fErr+ fErr :: IOException -> IO (Either String a)+ fErr e = return . Left $ "Error re-directing the output from "+ ++ cmd' ++ ": " ++ show e othErr = "Error messages from " ++ cmd' ++ ":\n"
Data/GraphViz/Parsing.hs view
@@ -82,7 +82,9 @@ import Data.Char( digitToInt , isDigit , isSpace+ , isLower , toLower+ , toUpper ) import Data.Maybe(fromMaybe, isNothing) import Data.Ratio((%))@@ -204,7 +206,7 @@ -- | Used when quotes are explicitly required; quotedString :: Parse String-quotedString = parseEscaped True []+quotedString = parseEscaped True [] [] parseSigned :: Real a => Parse a -> Parse a parseSigned p = (character '-' >> liftM negate p)@@ -303,7 +305,10 @@ `adjustErr` (++ "\nnot the expected char: " ++ [c]) where- parseC c' = c' == c || toLower c == c'+ parseC c' = c' == c || c == flipCase c'+ flipCase c' = if isLower c'+ then toUpper c'+ else toLower c' noneOf :: (Eq a) => [a] -> Parser a a noneOf t = satisfy (\x -> all (/= x) t)@@ -346,22 +351,24 @@ quoteChar :: Char quoteChar = '"' --- | Parse a 'String' where the provided 'Char's (as well as @\"@) are--- escaped. Note: does not parse surrounding quotes, and assumes--- that @\\@ is not an escaped character. The 'Bool' value--- indicates whether empty 'String's are allowed or not.-parseEscaped :: Bool -> [Char] -> Parse String-parseEscaped empt cs = lots $ qPrs `onFail` oth+-- | Parse a 'String' where the provided 'Char's (as well as @\"@ and+-- @\\@) are escaped and the second list of 'Char's are those that+-- are not permitted. Note: does not parse surrounding quotes. The+-- 'Bool' value indicates whether empty 'String's are allowed or+-- not.+parseEscaped :: Bool -> [Char] -> [Char] -> Parse String+parseEscaped empt cs bnd = lots $ qPrs `onFail` oth where lots = if empt then many else many1- cs' = quoteChar : cs+ cs' = quoteChar : slash : cs csSet = Set.fromList cs'+ bndSet = Set.fromList bnd `Set.union` csSet slash = '\\' -- Have to allow standard slashes qPrs = do character slash mE <- optional $ oneOf (map character cs') return $ fromMaybe slash mE- oth = satisfy (`Set.notMember` csSet)+ oth = satisfy (`Set.notMember` bndSet) newline :: Parse String newline = oneOf $ map string ["\r\n", "\n", "\r"]
Data/GraphViz/Printing.hs view
@@ -74,6 +74,7 @@ import Data.Char(toLower) import qualified Data.Set as Set import Data.Word(Word8, Word16)+import Control.Monad(ap) -- ----------------------------------------------------------------------------- @@ -216,13 +217,23 @@ -- needs to pass the result from this to 'addQuotes' to determine if -- it needs to be quoted or not. addEscapes :: [Char] -> String -> String-addEscapes cs = foldr escape ""+addEscapes cs = foldr escape "" . withNext where- cs' = Set.fromList $ quote : cs+ cs' = Set.fromList $ quote : slash : cs slash = '\\' quote = '"'- escape c str | c `Set.member` cs' = slash : c : str- | otherwise = c : str+ escape (c,c') str+ | c == slash && c' `Set.member` escLetters = c : str+ | c `Set.member` cs' = slash : c : str+ | c == '\n' = slash : 'n' : str+ | otherwise = c : str++ -- When a slash precedes one of these characters, don't escape the slash.+ escLetters = Set.fromList ['N', 'G', 'E', 'T', 'H', 'L', 'n', 'l', 'r']++ -- Need to check subsequent characters when escaping slashes, but+ -- don't want to lose the last character when zipping, so append a space.+ withNext = zip `ap` ((++" ") . tail) angled :: DotCode -> DotCode angled = wrap lang rang
Data/GraphViz/Testing/Instances.hs view
@@ -31,7 +31,6 @@ import qualified Data.Sequence as Seq import qualified Data.Map as Map import Control.Monad(liftM, liftM2, liftM3, liftM4)-import Data.Word(Word8, Word16) -- ----------------------------------------------------------------------------- -- Defining Arbitrary instances for the overall types@@ -599,14 +598,6 @@ validUnknown _ = True {- delete to here -} -instance Arbitrary Word8 where- arbitrary = arbitraryBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Word16 where- arbitrary = arbitraryBoundedIntegral- shrink = shrinkIntegral- instance Arbitrary ArrowType where arbitrary = liftM AType -- Arrow specifications have between 1 and 4 elements.@@ -637,7 +628,7 @@ return $ RatioPassCount ds is instance Arbitrary Rect where- arbitrary = liftM2 Rect arbitrary arbitrary+ arbitrary = liftM2 Rect point2D point2D shrink (Rect p1 p2) = do p1s <- shrink p1 p2s <- shrink p2@@ -645,12 +636,18 @@ instance Arbitrary Point where -- Pretty sure points have to be positive...- arbitrary = liftM2 Point posArbitrary posArbitrary+ arbitrary = liftM4 Point posArbitrary posArbitrary posZ arbitrary+ where+ posZ = frequency [(1, return Nothing), (3, liftM Just posArbitrary)] - shrink (Point v1 v2) = do v1s <- shrink v1- v2s <- shrink v2- return $ Point v1s v2s+ shrink p = do x' <- shrink $ xCoord p+ y' <- shrink $ yCoord p+ z' <- shrinkM $ zCoord p+ return $ Point x' y' z' False +point2D :: Gen Point+point2D = liftM2 createPoint posArbitrary posArbitrary+ instance Arbitrary ClusterMode where arbitrary = arbBounded @@ -662,7 +659,7 @@ instance Arbitrary DPoint where arbitrary = oneof [ liftM DVal arbitrary- , liftM PVal arbitrary+ , liftM PVal point2D ] shrink (DVal d) = map DVal $ shrink d@@ -729,21 +726,22 @@ shrink _ = [] instance Arbitrary LayerList where- arbitrary = do fs <- arbLayerName- oths <- listOf $ liftM2 (,) arbLayerSep arbLayerName- return $ LL fs oths+ arbitrary = liftM LL $ listOf1 arbName+ where+ arbName = suchThat arbitrary isLayerName - -- This should be improved to try actually shrinking the seps and names.- -- shrink (LL _ [(_,fs')]) = [LL fs' []]- -- shrink (LL fs oths) = map (LL fs) $ shrink oths+ isLayerName LRName{} = True+ isLayerName _ = False + shrink (LL ll) = map LL $ nonEmptyShrinks ll+ instance Arbitrary LayerRange where- arbitrary = oneof [ liftM LRID arbitrary- , liftM3 LRS arbitrary arbLayerSep arbitrary+ arbitrary = oneof [ liftM LRID arbitrary+ , liftM2 LRS arbitrary arbitrary ] - shrink (LRID nm) = map LRID $ shrink nm- shrink (LRS l1 _ l2) = [LRID l1, LRID l2]+ shrink (LRID nm) = map LRID $ shrink nm+ shrink (LRS l1 l2) = [LRID l1, LRID l2] instance Arbitrary LayerID where arbitrary = oneof [ return AllLayers@@ -1132,8 +1130,8 @@ arbitrary = arbBounded instance Arbitrary PortName where- arbitrary = liftM PN . flip suchThat (liftM2 (&&) (not . null) notCP)- $ liftM (filter (/= ':')) arbString+ arbitrary = liftM PN+ $ suchThat arbString (liftM2 (&&) (notElem ':') notCP) shrink = map PN . filter notCP . shrinkString . portName @@ -1150,26 +1148,31 @@ posArbitrary = liftM fromPositive arbitrary arbString :: Gen String-arbString = suchThat genStr validString+arbString = suchThat genStr notBool where- genStr = listOf1 $ elements strChr- strChr = ['a'..'z'] ++ ['0'..'9'] ++ ['\'', '"', ' ', '(', ')'- , ',', ':', '.']+ genStr = liftM2 (:) (elements notDigits) (listOf $ elements strChr)+ notDigits = ['a'..'z'] ++ ['\'', '"', ' ', '(', ')', ',', ':', '\\']+ strChr = notDigits ++ '.' : ['0'..'9'] +{-# INLINE arbString #-}+ arbIDString :: Gen String-arbIDString = suchThat genStr validString+arbIDString = suchThat genStr notBool where genStr = liftM2 (:) (elements frst) $ listOf (elements rest) frst = ['a'..'z'] ++ ['_'] rest = frst ++ ['0'.. '9'] -validString :: String -> Bool-validString "true" = False-validString "false" = False-validString str = notNumStr str+validString :: String -> Bool+validString = liftM2 (&&) notBool notNumStr +notBool :: String -> Bool+notBool "true" = False+notBool "false" = False+notBool _ = True+ shrinkString :: String -> [String]-shrinkString = filter validString . nonEmptyShrinks+shrinkString = filter validString . nonEmptyShrinks' notNumStr :: String -> Bool notNumStr = not . isNumString@@ -1177,9 +1180,6 @@ arbBounded :: (Bounded a, Enum a) => Gen a arbBounded = elements [minBound .. maxBound] -arbLayerSep :: Gen [Char]-arbLayerSep = listOf1 (elements defLayerSep)- arbLayerName :: Gen String arbLayerName = suchThat arbString (all notLayerSep) @@ -1194,11 +1194,18 @@ nonEmptyShrinks :: (Arbitrary a) => [a] -> [[a]] nonEmptyShrinks = filter (not . null) . shrinkList +nonEmptyShrinks' :: [a] -> [[a]]+nonEmptyShrinks' = filter (not . null) . shrinkList'+ -- Shrink lists with more than one value only by removing values, not -- by shrinking individual items. shrinkList :: (Arbitrary a) => [a] -> [[a]] shrinkList [a] = map return $ shrink a-shrinkList as = rm (length as) as+shrinkList as = shrinkList' as++-- Just shrink the size.+shrinkList' :: [a] -> [[a]]+shrinkList' as = rm (length as) as where rm 0 _ = [] rm 1 _ = [[]]
FAQ view
@@ -55,13 +55,18 @@ [output formats]: http://www.graphviz.org/doc/info/output.html * Functions to convert [FGL] graphs to and from the internal Dot- representations.+ representations. In future, this will be expanded to a much larger+ range of graph-like values once a suitable abstraction is available. [FGL]: http://web.engr.oregonstate.edu/~erwig/fgl/haskell/ * The ability to augment Dot and FGL graphs with positioning information by round-trip passing through Graphviz. +* _graphviz_ is continually being worked upon and expanded to better+ suit/match the requirements of Graphviz, to improve performance and+ to make it easier for the programmer to use.+ ### Is the API of _graphviz_ stable? ### For the most part, yes: the only items that are likely to change in@@ -110,9 +115,9 @@ * The deprecated `overlap` algorithms have not been defined. -* `pointf` and `point` values have been combined into one datatype;- however the optional `!` and third value for `point` values is not- accepted.+* `pointf` and `point` values have been combined into one datatype; as+ such, when constructing values such as `Rect` care should be taken+ about which parts of a `Point` are allowed. * Only polygon-based `shape`s are available. @@ -151,6 +156,11 @@ * HTML-like and record labels are available, and feature proper escaping/unescaping when printing/parsing. +* Other syntactic issues are taken care of for you automatically (such+ as escaping/unescaping quotation marks). Even newlines are+ automatically escaped (but not unescaped) for you, defaulting to+ centered lines.+ Getting _graphviz_ and more documentation ----------------------------------------- @@ -201,7 +211,10 @@ ### Are there any tutorials on how to use _graphviz_? ### -There will be soon.+There will be soon. The current stumbling block is that each time I+sit down to write a tutorial, I find that it would be even better if+_graphviz_ had some extra features in it first, so the tutorial has to+wait until I have finished that feature... ### What other packages use _graphviz_? ### @@ -232,11 +245,11 @@ ### Can I start using _graphviz_ without knowing anything about Graphviz? ### -Unfortunately, no: the layout and design of _graphviz_ is heavily-based upon the Dot language and the various [attributes] that Graphviz-supports. As such, you can't just suffice on the documentation-available in _graphviz_ (unless you're doing something _very_-simplistic).+You can, but if you want to start doing anything more advanced then+you should be reading Graphviz's documentation as well as+_graphviz_'s. This is because the layout and design of _graphviz_ is+heavily based upon the Dot language and the various [attributes] that+Graphviz supports. ### Can I just use _graphviz_ without reading its documentation? ### @@ -314,27 +327,31 @@ - `Color` to set the colour of an edge; if you supply more than one value then the edge is drawn using parallel splines/lines- (one per colour in the list).+ (one per colour in the list). This can also be used to set the+ border colour for a node, but if a filled style is used and+ `FillColor` isn't set then this will also set the background+ colour. - `PenColor` to set the colour of the bounding box for a cluster. - When choosing a `Color` value for one of the above, it is better to- use one of the `X11Color` values (note: these are **not** the same- as the standard X11 colours) or - if you have to - one of the manual- colours over a `BrewerColor`, as they come under a different license- and have no real standard on what the values are.+ When choosing a `Color` value for one of the above, it is+ better to use one of the `X11Color` values (note: these+ colours slightly differ from the standard X11 colours) or - if+ you have to - one of the manual colours over a `BrewerColor`,+ as they come under a different license and have no real+ standard on what the values are. -* `Label`: `StrLabel` can be used for both nodes and edges; the other- two only for nodes. `RecordLabel` and `HtmlLabel` provide ways of- having more fine-grained control over a node's layout with different- sub-components, etc.; in most cases these won't be needed.+* For labels, use the `Labellable` class to more easily construct+ labels. In most cases, a `String`-based label will suffice; for+ more fine-grained control over layout, colours, etc. use either a+ `RecordFields` or an `HtmlLabel`. * `Rank`: this lets you control relative placement of sub-graphs and clusters. -* `Shape`: Use `Record` and `MRecord` for `RecordLabel` labels; feel- free to use any other ones at any time (though you probably want to- use `PlainText` for `HtmlLabel` labels).+* `Shape`: Use `Record` and `MRecord` for `RecordFields`-based labels;+ feel free to use any other ones at any time (though you probably+ want to use `PlainText` for `HtmlLabel` labels). * `Style`: use this to set line types, etc. for nodes and edges. You should **not** use a `DD` (device-dependent) value.@@ -345,7 +362,8 @@ but in future _graphviz_ will not contain this attribute and will only allow UTF-8 usage. -* `Color` for anything except edge colours.+* `Color` for anything except edge colours (and if you must, the+ border colour for a node). * `ColorScheme`: just stick with X11 colours. @@ -357,7 +375,7 @@ No: attributes are all defined in one big datatype for the sake of simplicity, but not all attributes are valid in all places. Read the-documentation (either in Graphviz or _graphviz_) to determine which is+documentation (either for Graphviz or _graphviz_) to determine which is suitable where. ### How can I use _graphviz_ to visualise non-FGL graphs? ###@@ -383,7 +401,7 @@ way of doing this is to have functions that convert between your type and `String` and let graphviz determine how to print and parse those. Here is an example of a more difficult type that should be printed-like "1: Foo":+like `"1: Foo"`: ~~~~~~~~~~~~~~~~~~~~ {.haskell} data MyType = MyType String Int@@ -418,7 +436,7 @@ conversion functions (`int`, `text`, etc.) to ensure that quotations, etc. are dealt with correctly. -* Be as liberal as you can when printing, especially with whitespace:+* Be as liberal as you can when parsing, especially with whitespace: when printing only one space is used, yet when parsing we use the `whitespace` parsing combinator that will parse all whitespace characters (but it must consume _at least_ one; there is a variant@@ -449,6 +467,9 @@ ] ~~~~~~~~~~~~~~~~~~~~ +I realise that doing this manually isn't as convenient, but I am open+to suggestions on how this can be improved.+ Note where `TailPort` and `HeadPort` are used; the next question explains this. @@ -609,7 +630,7 @@ copy of the darcs repository: ~~~~~~~~~~~~~~~~~~~~ {.bash}-darcs get --lazy http://code.haskell.org/+darcs get --lazy http://code.haskell.org/graphviz ~~~~~~~~~~~~~~~~~~~~ Once you've made your changes, make sure you build and run the
graphviz.cabal view
@@ -1,5 +1,5 @@ Name: graphviz-Version: 2999.10.0.1+Version: 2999.11.0.0 Stability: Beta Synopsis: Graphviz bindings for Haskell. Description: {@@ -63,8 +63,8 @@ filepath, polyparse == 1.4.*, pretty,- bytestring < 0.10,- colour >= 2.3 && < 2.4,+ bytestring == 0.9.*,+ colour == 2.3.*, transformers == 0.2.* Exposed-Modules: Data.GraphViz@@ -84,7 +84,7 @@ Data.GraphViz.Types.State Data.GraphViz.Util if flag(test)- Build-Depends: QuickCheck >= 2.1 && < 2.2+ Build-Depends: QuickCheck >= 2.3 && < 2.5 Exposed-Modules: Data.GraphViz.Testing Data.GraphViz.Testing.Properties