-----------------------------------------------------------------------------
-- |
-- Module      :  Graphics.Rendering.Chart.Plot.FillBetween
-- Copyright   :  (c) Tim Docker 2006, 2014
-- License     :  BSD-style (see chart/COPYRIGHT)
--
-- Plots that fill the area between two lines.
--
{-# LANGUAGE TemplateHaskell #-}

module Graphics.Rendering.Chart.Plot.FillBetween(
    PlotFillBetween(..),

    -- * Accessors
    -- | These accessors are generated by template haskell
    plot_fillbetween_title,
    plot_fillbetween_style,
    plot_fillbetween_line,
    plot_fillbetween_values,
) where

import Control.Lens
import Graphics.Rendering.Chart.Geometry
import Graphics.Rendering.Chart.Drawing
import Graphics.Rendering.Chart.Plot.Types
import Data.Colour (opaque)
import Data.Colour.SRGB (sRGB)
import Data.Default.Class

-- | Value specifying a plot filling the area between two sets of Y
--   coordinates, given common X coordinates.

data PlotFillBetween x y = PlotFillBetween {
    forall x y. PlotFillBetween x y -> String
_plot_fillbetween_title  :: String,
    forall x y. PlotFillBetween x y -> FillStyle
_plot_fillbetween_style  :: FillStyle,
    forall x y. PlotFillBetween x y -> Maybe LineStyle
_plot_fillbetween_line  :: Maybe LineStyle,
    forall x y. PlotFillBetween x y -> [(x, (y, y))]
_plot_fillbetween_values :: [ (x, (y,y))]
}


instance ToPlot PlotFillBetween where
    toPlot :: forall x y. PlotFillBetween x y -> Plot x y
toPlot PlotFillBetween x y
p = Plot {
        _plot_render :: PointMapFn x y -> BackendProgram ()
_plot_render     = forall x y.
PlotFillBetween x y -> PointMapFn x y -> BackendProgram ()
renderPlotFillBetween PlotFillBetween x y
p,
        _plot_legend :: [(String, Rect -> BackendProgram ())]
_plot_legend     = [(forall x y. PlotFillBetween x y -> String
_plot_fillbetween_title PlotFillBetween x y
p,forall x y. PlotFillBetween x y -> Rect -> BackendProgram ()
renderPlotLegendFill PlotFillBetween x y
p)],
        _plot_all_points :: ([x], [y])
_plot_all_points = forall x y. PlotFillBetween x y -> ([x], [y])
plotAllPointsFillBetween PlotFillBetween x y
p
    }

renderPlotFillBetween :: PlotFillBetween x y -> PointMapFn x y -> BackendProgram ()
renderPlotFillBetween :: forall x y.
PlotFillBetween x y -> PointMapFn x y -> BackendProgram ()
renderPlotFillBetween PlotFillBetween x y
p =
    forall x y a b.
PlotFillBetween x y
-> [(a, (b, b))]
-> ((Limit a, Limit b) -> Point)
-> BackendProgram ()
renderPlotFillBetween' PlotFillBetween x y
p (forall x y. PlotFillBetween x y -> [(x, (y, y))]
_plot_fillbetween_values PlotFillBetween x y
p)

renderPlotFillBetween' :: 
  PlotFillBetween x y 
  -> [(a, (b, b))]
  -> ((Limit a, Limit b) -> Point)
  -> BackendProgram ()
renderPlotFillBetween' :: forall x y a b.
PlotFillBetween x y
-> [(a, (b, b))]
-> ((Limit a, Limit b) -> Point)
-> BackendProgram ()
renderPlotFillBetween' PlotFillBetween x y
_ [] (Limit a, Limit b) -> Point
_     = forall (m :: * -> *) a. Monad m => a -> m a
return ()
renderPlotFillBetween' PlotFillBetween x y
p [(a, (b, b))]
vs (Limit a, Limit b) -> Point
pmap  = 
  forall a. FillStyle -> BackendProgram a -> BackendProgram a
withFillStyle (forall x y. PlotFillBetween x y -> FillStyle
_plot_fillbetween_style PlotFillBetween x y
p) forall a b. (a -> b) -> a -> b
$ do
    [Point]
ps <- [Point] -> BackendProgram [Point]
alignFillPoints forall a b. (a -> b) -> a -> b
$ [Point
p0] forall a. [a] -> [a] -> [a]
++ [Point]
p1s forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [a]
reverse [Point]
p2s forall a. [a] -> [a] -> [a]
++ [Point
p0]
    [Point] -> BackendProgram ()
fillPointPath [Point]
ps
    case forall x y. PlotFillBetween x y -> Maybe LineStyle
_plot_fillbetween_line PlotFillBetween x y
p of
      Maybe LineStyle
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return ()
      Just LineStyle
lineStyle -> forall a. LineStyle -> BackendProgram a -> BackendProgram a
withLineStyle LineStyle
lineStyle forall a b. (a -> b) -> a -> b
$ [Point] -> BackendProgram ()
strokePointPath [Point]
ps
  where
    pmap' :: (a, b) -> Point
pmap'    = forall x y. PointMapFn x y -> (x, y) -> Point
mapXY (Limit a, Limit b) -> Point
pmap
    (Point
p0:[Point]
p1s) = forall a b. (a -> b) -> [a] -> [b]
map (a, b) -> Point
pmap' [ (a
x,b
y1) | (a
x,(b
y1,b
_)) <- [(a, (b, b))]
vs ]
    p2s :: [Point]
p2s      = forall a b. (a -> b) -> [a] -> [b]
map (a, b) -> Point
pmap' [ (a
x,b
y2) | (a
x,(b
_,b
y2)) <- [(a, (b, b))]
vs ]

renderPlotLegendFill :: PlotFillBetween x y -> Rect -> BackendProgram ()
renderPlotLegendFill :: forall x y. PlotFillBetween x y -> Rect -> BackendProgram ()
renderPlotLegendFill PlotFillBetween x y
p Rect
r = 
  forall a. FillStyle -> BackendProgram a -> BackendProgram a
withFillStyle (forall x y. PlotFillBetween x y -> FillStyle
_plot_fillbetween_style PlotFillBetween x y
p) forall a b. (a -> b) -> a -> b
$ 
    Path -> BackendProgram ()
fillPath (Rect -> Path
rectPath Rect
r)

plotAllPointsFillBetween :: PlotFillBetween x y -> ([x],[y])
plotAllPointsFillBetween :: forall x y. PlotFillBetween x y -> ([x], [y])
plotAllPointsFillBetween PlotFillBetween x y
p = ( [ x
x | (x
x,(y
_,y
_)) <- [(x, (y, y))]
pts ]
                             , forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ [y
y1,y
y2] | (x
_,(y
y1,y
y2)) <- [(x, (y, y))]
pts ] )
  where
    pts :: [(x, (y, y))]
pts = forall x y. PlotFillBetween x y -> [(x, (y, y))]
_plot_fillbetween_values PlotFillBetween x y
p

instance Default (PlotFillBetween x y) where
  def :: PlotFillBetween x y
def = PlotFillBetween 
    { _plot_fillbetween_title :: String
_plot_fillbetween_title  = String
""
    , _plot_fillbetween_style :: FillStyle
_plot_fillbetween_style  = AlphaColour Double -> FillStyle
solidFillStyle (forall a. Num a => Colour a -> AlphaColour a
opaque forall a b. (a -> b) -> a -> b
$ forall b. (Ord b, Floating b) => b -> b -> b -> Colour b
sRGB Double
0.5 Double
0.5 Double
1.0)
    , _plot_fillbetween_line :: Maybe LineStyle
_plot_fillbetween_line   = forall a. Maybe a
Nothing
    , _plot_fillbetween_values :: [(x, (y, y))]
_plot_fillbetween_values = []
    }

$( makeLenses ''PlotFillBetween )