Vivian McPhail 45a55dbc5e initial repository
darcs-hash:20100901145450-af16d-11f5ac7750d938d81276e005e7b4a63d27620fb0.gz
2010-09-02 02:54:50 +12:00

216 lines
9.7 KiB
Haskell

-----------------------------------------------------------------------------
-- |
-- Module : Graphics.Rendering.Plot.Figure.Plot
-- Copyright : (c) A. V. H. McPhail 2010
-- License : BSD3
--
-- Maintainer : haskell.vivian.mcphail <at> gmail <dot> com
-- Stability : provisional
-- Portability : portable
--
-- Creation and manipulation of 'Plot's
--
-----------------------------------------------------------------------------
module Graphics.Rendering.Plot.Figure.Plot (
Plot
, PlotType(..)
-- * Plot elements
, Border
, setBorder
, setPlotPadding
, withHeading
-- * Series data
, D.Abscissa(), D.Ordinate(), D.Dataset()
, D.FormattedSeries()
, D.line, D.point, D.linepoint
, setDataset
-- ** Plot type
, setSeriesType
, setAllSeriesTypes
-- ** Formatting
, D.PlotFormats(..)
, withSeriesFormat
, withAllSeriesFormats
-- * Range
, setRange
, setRangeFromData
-- * Axes
, AX.Axis
, AxisType(..),AxisSide(..),AxisPosn(..)
, clearAxes
, addAxis
-- , withAxis
-- ** Formatting
, Tick(..), TickValues, GridLines
, AX.setTicks
, AX.setTickLabelFormat
, AX.withAxisLabel
, AX.withAxisLine
) where
-----------------------------------------------------------------------------
import Data.Packed.Vector
import Numeric.LinearAlgebra.Linear
import qualified Data.Array.IArray as A
import Control.Monad.State
import Control.Monad.Reader
--import Control.Monad.Supply
import Prelude hiding(min,max)
import Graphics.Rendering.Plot.Types
import Graphics.Rendering.Plot.Defaults
import qualified Graphics.Rendering.Plot.Figure.Plot.Data as D
import qualified Graphics.Rendering.Plot.Figure.Plot.Axis as AX
-----------------------------------------------------------------------------
-- | whether to draw a boundary around the plot area
setBorder :: Border -> Plot ()
setBorder b = modify $ \s -> s { _border = b }
-- | set the padding of the subplot
setPlotPadding :: Double -> Double -> Double -> Double -> Plot ()
setPlotPadding l r b t = modify $ \s -> s { _plot_pads = Padding l r b t }
-- | set the heading of the subplot
withHeading :: Text () -> Plot ()
withHeading m = do
o <- asks _textoptions
modify $ \s -> s { _heading = execText m o (_heading s) }
-----------------------------------------------------------------------------
-- | set the axis range
setRange :: AxisType -> AxisSide -> Double -> Double -> Plot ()
setRange XAxis sd min max = modify $ \s -> s { _ranges = setXRanges sd min max (_ranges s) }
where setXRanges Lower min' max' (Ranges (Left _) yr) = Ranges (Left (Range min' max')) yr
setXRanges Lower min' max' (Ranges (Right (_,xr)) yr) = Ranges (Right ((Range min' max',xr))) yr
setXRanges Upper min' max' (Ranges (Left xr) yr) = Ranges (Right (xr,Range min' max')) yr
setXRanges Upper min' max' (Ranges (Right (_,xr)) yr) = Ranges (Right (Range min' max',xr)) yr
setRange YAxis sd min max = modify $ \s -> s { _ranges = setYRanges sd min max (_ranges s) }
where setYRanges Lower min' max' (Ranges xr (Left _)) = Ranges xr (Left (Range min' max'))
setYRanges Lower min' max' (Ranges xr (Right (_,yr))) = Ranges xr (Right ((Range min' max',yr)))
setYRanges Upper min' max' (Ranges xr (Left yr)) = Ranges xr (Right (yr,Range min' max'))
setYRanges Upper min' max' (Ranges xr (Right (_,yr))) = Ranges xr (Right ((Range min' max',yr)))
-- | set the axis ranges to values based on dataset
setRangeFromData :: AxisType -> AxisSide -> Plot ()
setRangeFromData ax sd = do
ds <- gets _data
let ((xmin,xmax),(ymin,ymax)) = calculateRanges ds
case ax of
XAxis -> setRange ax sd xmin xmax
YAxis -> setRange ax sd ymin ymax
-----------------------------------------------------------------------------
-- | clear the axes of a subplot
clearAxes :: Plot ()
clearAxes = modify $ \s -> s { _axes = [] }
-- | add an axis to the subplot
addAxis :: AxisType -> AxisPosn -> AX.Axis () -> Plot ()
addAxis at axp m = do
ax' <- gets _axes
o <- ask
let ax = execAxis m o (defaultAxis at axp)
modify $ \s -> s { _axes = ax : ax' }
-- | operate on the given axis
withAxis :: AxisType -> AxisPosn -> AX.Axis () -> Plot ()
withAxis at axp m = do
axes' <- gets _axes
o <- ask
modify $ \s -> s { _axes = map (\a@(Axis at' ap' _ _ _ _ _)
-> if at == at' && axp == ap' then execAxis m o a else a) axes' }
-----------------------------------------------------------------------------
-- | set the type of the subplot
setPlotType :: PlotType -> Plot ()
setPlotType pt = modify $ \s -> s { _type = pt }
-----------------------------------------------------------------------------
-- | operate on the data
withData :: D.Data () -> Plot ()
withData = dataInPlot
-- | set the data series of the subplot
setDataset :: D.Dataset a => a -> Plot ()
setDataset d = withData $ D.setDataSeries d
-- | set the plot type of a given data series
setSeriesType :: Int -> SeriesType -> Plot ()
setSeriesType i t = withData $ D.setSeriesType t i
-- | change the plot type of all data series
setAllSeriesTypes :: SeriesType -> Plot ()
setAllSeriesTypes t = withData $ D.setAllSeriesTypes t
-- | format the plot elements of a given series
withSeriesFormat :: D.PlotFormats m => Int -> m () -> Plot ()
withSeriesFormat i f = withData $ D.withSeriesFormat i f
{- |
format the plot elements of all series
the operation to modify the formats is passed the series index.
This allows, for example, colours to be selected from a list
that gets indexed by the argument
> setColour i = withAllSeriesFormats (\i -> do
> setLineColour $ [black,blue,red,green,yellow] !! i
> setLineWidth 1.0
-}
withAllSeriesFormats :: D.PlotFormats m => (Int -> m ()) -> Plot ()
withAllSeriesFormats f = withData $ D.withAllSeriesFormats f
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
findMinMax :: Abscissae -> Ordinates -> (Double,Double)
findMinMax AbsFunction (OrdFunction f) = let v = mapVector f (linspace 100 (-1,1))
in (vectorMin v,vectorMax v)
findMinMax (AbsPoints x) (OrdFunction f) = let v = mapVector f x
in (vectorMin v,vectorMax v)
-- what if errors go beyond plot?
findMinMax _ (OrdPoints y) = let o = getOrdData y
in (vectorMin o,vectorMax o)
abscMinMax :: Abscissae -> (Double,Double)
abscMinMax AbsFunction = (-1,1)
abscMinMax (AbsPoints x) = (vectorMin x,vectorMax x)
ordDim :: Ordinates -> Int
ordDim (OrdFunction _) = 1
ordDim (OrdPoints o) = dim $ getOrdData o
calculateRanges :: DataSeries -> ((Double,Double),(Double,Double))
calculateRanges (DS_Y ys) = let xmax = maximum $ map (\(DecSeries o _) -> fromIntegral $ ordDim o) $ A.elems ys
ym = unzip $ map (\(DecSeries o _) -> findMinMax AbsFunction o) $ A.elems ys
ymm = (minimum $ fst ym,maximum $ snd ym)
in ((0,xmax),ymm)
calculateRanges (DS_1toN x ys) = let ym = unzip $ map (\(DecSeries o _) -> findMinMax x o) $ A.elems ys
ymm = (minimum $ fst ym,maximum $ snd ym)
xmm = abscMinMax x
in (xmm,ymm)
calculateRanges (DS_1to1 ys) = let (xm',ym') = unzip $ A.elems ys
ym = unzip $ map (\(x,(DecSeries o _)) -> findMinMax x o) (zip xm' ym')
ymm = (minimum $ fst ym,maximum $ snd ym)
xm = unzip $ map abscMinMax xm'
xmm = (minimum $ fst xm,maximum $ snd ym)
in (xmm,ymm)
-----------------------------------------------------------------------------