module Graphics.Gnuplot.Private.Plot where import qualified Graphics.Gnuplot.Private.Display as Display import qualified Graphics.Gnuplot.Private.FrameOptionSet as OptionSet import qualified Graphics.Gnuplot.Private.Graph as Graph import qualified Graphics.Gnuplot.Private.File as FileClass import qualified Graphics.Gnuplot.File as File import Graphics.Gnuplot.Utility (quote, commaConcat, ) import qualified Control.Monad.Trans.Reader as MR import qualified Control.Monad.Trans.State as MS import qualified Control.Monad.Trans.Class as MT import qualified Data.Accessor.Monad.Trans.State as AccState import qualified Data.Accessor.Tuple as AccTuple import qualified Data.Foldable as Fold import Control.Monad (liftM2, return, ) import Data.Monoid (Monoid, mempty, mappend, ) import Data.Semigroup (Semigroup, (<>), ) import Data.Maybe (Maybe(Just, Nothing), mapMaybe, ) import Data.List (concatMap, map, (++), ) import Data.Function (($), (.), ) import qualified System.FilePath as Path import System.FilePath (FilePath, (), ) import Prelude (Functor, fmap, String, show, Int, succ, writeFile, ) {- | Plots can be assembled using 'mappend' or 'mconcat' or several functions from "Data.Foldable". -} newtype T graph = Cons (MS.StateT Int (MR.Reader FilePath) [File graph]) pure :: [File graph] -> T graph pure = Cons . return instance Semigroup (T graph) where Cons s0 <> Cons s1 = Cons (liftM2 (<>) s0 s1) {- Could also be implemented with Control.Monad.Trans.State: mappend = liftA2 mappend -} instance Monoid (T graph) where mempty = Cons $ return mempty mappend = (<>) withUniqueFile :: String -> [graph] -> T graph withUniqueFile content graphs = Cons $ do n <- MS.get dir <- MT.lift MR.ask MS.put $ succ n return $ [File (dir Path.addExtension (tmpFileStem ++ show n) "csv") (Just content) graphs] fromGraphs :: FilePath -> [graph] -> T graph fromGraphs name gs = pure [File name Nothing gs] data File graph = File { filename_ :: FilePath, content_ :: Maybe String, graphs_ :: [graph] } instance FileClass.C (File graph) where write (File fn cont _) = Fold.mapM_ (writeFile fn) cont tmpFileStem :: FilePath tmpFileStem = "curve" {- tmpFile :: FilePath tmpFile = tmpFileStem ++ ".csv" -} instance Functor T where fmap f (Cons mp) = Cons $ fmap (map (\file -> file{graphs_ = map f $ graphs_ file})) mp {- | In contrast to the Display.toScript method instantiation this function leaves the options, and thus can be used to write the Display.toScript instance for Frame. -} toScript :: Graph.C graph => T graph -> Display.Script toScript p@(Cons mp) = Display.Script $ do blocks <- AccState.liftT AccTuple.first mp let files = mapMaybe (\blk -> fmap (File.Cons (filename_ blk)) (content_ blk)) blocks graphs = concatMap (\blk -> map (\gr -> quote (filename_ blk) ++ " " ++ Graph.toString gr) $ graphs_ blk) $ blocks return $ Display.Body files [Graph.commandString (plotCmd p) ++ " " ++ commaConcat graphs] optionsToScript :: Graph.C graph => OptionSet.T graph -> Display.Script optionsToScript opts = Display.Script $ AccState.liftT AccTuple.second $ do opts0 <- MS.get let opts1 = OptionSet.decons opts MS.put opts1 return $ Display.Body [] $ OptionSet.diffToString opts0 opts1 defltOpts :: Graph.C graph => T graph -> OptionSet.T graph defltOpts _ = Graph.defltOptions instance Graph.C graph => Display.C (T graph) where toScript plot = optionsToScript (defltOpts plot) `mappend` toScript plot plotCmd :: Graph.C graph => T graph -> Graph.Command graph plotCmd _plot = Graph.command