----------------------------------------------------------------------------- -- | -- Module : Graphics.Rendering.Chart.Plot.ErrBars -- Copyright : (c) Tim Docker 2006 -- License : BSD-style (see chart/COPYRIGHT) -- -- Plot series of points with associated error bars. -- {-# OPTIONS_GHC -XTemplateHaskell #-} module Graphics.Rendering.Chart.Plot.ErrBars( PlotErrBars(..), defaultPlotErrBars, ErrPoint(..), ErrValue(..), symErrPoint, -- * Accessors -- | These accessors are generated by template haskell plot_errbars_title, plot_errbars_line_style, plot_errbars_tick_length, plot_errbars_overhang, plot_errbars_values, ) where import Data.Accessor.Template import qualified Graphics.Rendering.Cairo as C import Graphics.Rendering.Chart.Types import Graphics.Rendering.Chart.Renderable import Graphics.Rendering.Chart.Plot.Types import Data.Colour (opaque) import Data.Colour.Names (black, blue) import Data.Colour.SRGB (sRGB) -- | Value for holding a point with associated error bounds for each axis. data ErrValue x = ErrValue { ev_low :: x, ev_best :: x, ev_high :: x } deriving Show data ErrPoint x y = ErrPoint { ep_x :: ErrValue x, ep_y :: ErrValue y } deriving Show -- | When the error is symmetric, we can simply pass in dx for the error. symErrPoint :: (Num a, Num b) => a -> b -> a -> b -> ErrPoint a b symErrPoint x y dx dy = ErrPoint (ErrValue (x-dx) x (x+dx)) (ErrValue (y-dy) y (y+dy)) -- | Value defining a series of error intervals, and a style in -- which to render them. data PlotErrBars x y = PlotErrBars { plot_errbars_title_ :: String, plot_errbars_line_style_ :: CairoLineStyle, plot_errbars_tick_length_ :: Double, plot_errbars_overhang_ :: Double, plot_errbars_values_ :: [ErrPoint x y] } instance ToPlot PlotErrBars where toPlot p = Plot { plot_render_ = renderPlotErrBars p, plot_legend_ = [(plot_errbars_title_ p, renderPlotLegendErrBars p)], plot_all_points_ = ( concat [ [ev_low x,ev_high x] | ErrPoint x _ <- pts ] , concat [ [ev_low y,ev_high y] | ErrPoint _ y <- pts ] ) } where pts = plot_errbars_values_ p renderPlotErrBars :: PlotErrBars x y -> PointMapFn x y -> CRender () renderPlotErrBars p pmap = preserveCState $ do mapM_ (drawErrBar.epmap) (plot_errbars_values_ p) where epmap (ErrPoint (ErrValue xl x xh) (ErrValue yl y yh)) = ErrPoint (ErrValue xl' x' xh') (ErrValue yl' y' yh') where (Point x' y') = pmap' (x,y) (Point xl' yl') = pmap' (xl,yl) (Point xh' yh') = pmap' (xh,yh) drawErrBar = drawErrBar0 p pmap' = mapXY pmap drawErrBar0 ps (ErrPoint (ErrValue xl x xh) (ErrValue yl y yh)) = do let tl = plot_errbars_tick_length_ ps let oh = plot_errbars_overhang_ ps setLineStyle (plot_errbars_line_style_ ps) c $ C.newPath c $ C.moveTo (xl-oh) y c $ C.lineTo (xh+oh) y c $ C.moveTo x (yl-oh) c $ C.lineTo x (yh+oh) c $ C.moveTo xl (y-tl) c $ C.lineTo xl (y+tl) c $ C.moveTo (x-tl) yl c $ C.lineTo (x+tl) yl c $ C.moveTo xh (y-tl) c $ C.lineTo xh (y+tl) c $ C.moveTo (x-tl) yh c $ C.lineTo (x+tl) yh c $ C.stroke renderPlotLegendErrBars :: PlotErrBars x y -> Rect -> CRender () renderPlotLegendErrBars p r@(Rect p1 p2) = preserveCState $ do drawErrBar (symErrPoint (p_x p1) ((p_y p1 + p_y p2)/2) dx dx) drawErrBar (symErrPoint ((p_x p1 + p_x p2)/2) ((p_y p1 + p_y p2)/2) dx dx) drawErrBar (symErrPoint (p_x p2) ((p_y p1 + p_y p2)/2) dx dx) where drawErrBar = drawErrBar0 p dx = min ((p_x p2 - p_x p1)/6) ((p_y p2 - p_y p1)/2) defaultPlotErrBars :: PlotErrBars x y defaultPlotErrBars = PlotErrBars { plot_errbars_title_ = "", plot_errbars_line_style_ = solidLine 1 $ opaque blue, plot_errbars_tick_length_ = 3, plot_errbars_overhang_ = 0, plot_errbars_values_ = [] } ---------------------------------------------------------------------- -- Template haskell to derive an instance of Data.Accessor.Accessor -- for each field. $( deriveAccessors ''PlotErrBars )