math-grads-0.1.5.1: src/Math/Grads/Drawing/Coords.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
-- | Module providing functions for obtaining coordinates of 'GenericGraph's on
-- a 2D plane.
--
module Math.Grads.Drawing.Coords
( Coord
, CoordList
, CoordMap
, Drawable (..)
, EdgeFixator
, bondLength
, getCoordsForGraph
) where
import Control.Monad (join)
import Data.Map.Strict (keys,
singleton)
import Math.Grads.Algo.Cycles (findCycles)
import Math.Grads.Algo.Interaction (getIndices)
import Math.Grads.Drawing.Internal.Coords (Coord,
CoordList,
CoordMap,
bondLength,
coordListForDrawing)
import Math.Grads.Drawing.Internal.Cycles (getCoordsOfGlobalCycle)
import Math.Grads.Drawing.Internal.CyclesPathsAlignment (alignCyclesAndPaths)
import Math.Grads.Drawing.Internal.Paths (findPaths, getCoordsOfPath)
import Math.Grads.Drawing.Internal.Sampling (EdgeFixator,
bestSample)
import Math.Grads.GenericGraph (GenericGraph)
import Math.Grads.Graph (EdgeList,
Graph,
toList)
import System.Random (StdGen)
-- | Type class that defines whether graph can be drawn or not.
--
class Graph g => Drawable g v e where
-- | Change coordinates and fixate edges that shouldn't take part in sampling.
--
edgeFixator :: g v e -> EdgeFixator e
edgeFixator = const $ (,) []
-- | Given 'StdGen' returns 'CoordMap', which keys correspond to indices of
-- vertices of given 'GenericGraph'. Works only for simple planar graphs. If graph
-- is neither simple nor planar, returns Nothing. This function is best used for
-- graphs that can be represented as systems of conjugated cycles and paths between
-- them. If graph contains too complex conjugated cycles, function will return Nothing.
--
getCoordsForGraph :: (Ord v, Ord e, Eq e, Drawable GenericGraph v e) => StdGen -> GenericGraph v e -> Maybe CoordMap
getCoordsForGraph stdGen graph = if length vertices == 1 then Just (singleton 0 (0, 0))
else res
where
(vertices, edges) = toList graph
(globalCycles, paths) = splitIntoCyclesAndPaths edges
globalCyclesWithCoords = sequence (fmap (getCoordsOfGlobalCycle pathsWithCoords) globalCycles)
pathsWithCoords = fmap getCoordsOfPath paths
finalCoords = join (fmap (alignCyclesAndPaths pathsWithCoords) globalCyclesWithCoords)
resCoords = join (fmap (bestSample stdGen (edgeFixator graph) (concat paths)) finalCoords)
resMap = fmap coordListForDrawing resCoords
res = if fmap (length . keys) resMap == pure (length vertices) then resMap else Nothing
splitIntoCyclesAndPaths :: (Ord e, Eq e) => EdgeList e -> ([EdgeList e], [EdgeList e])
splitIntoCyclesAndPaths edges = (globalCycles, paths)
where
globalCycles = findCycles edges
forPaths = filter (`notElem` concat globalCycles) edges
paths = findPaths forPaths $ concatMap getIndices globalCycles