packages feed

plot-light 0.2.4 → 0.2.5

raw patch · 13 files changed

+504/−115 lines, 13 filesdep +mtlbinary-addedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: mtl

API changes (from Hackage documentation)

+ Graphics.Rendering.Plot.Light: BottomLeft :: LegendPosition_
+ Graphics.Rendering.Plot.Light: BottomRight :: LegendPosition_
+ Graphics.Rendering.Plot.Light: TopLeft :: LegendPosition_
+ Graphics.Rendering.Plot.Light: TopRight :: LegendPosition_
+ Graphics.Rendering.Plot.Light: axes :: (Show a, RealFrac a) => FigureData a -> Frame Rational -> a -> Colour Double -> Int -> Int -> MarkupM ()
+ Graphics.Rendering.Plot.Light: blendTwo :: Colour Double -> Colour Double -> Int -> [Colour Double]
+ Graphics.Rendering.Plot.Light: colourBar :: (RealFrac t, RealFrac a, Show a, Enum t, Floating a) => FigureData (Ratio Integer) -> [Colour Double] -> a -> t -> t -> Int -> LegendPosition_ -> a -> Svg
+ Graphics.Rendering.Plot.Light: data LegendPosition_
+ Graphics.Rendering.Plot.Light: figFHeight :: Num a => FigureData a -> a
+ Graphics.Rendering.Plot.Light: figFWidth :: Num a => FigureData a -> a
+ Graphics.Rendering.Plot.Light: heatmap' :: (Foldable f, Functor f, Show a, RealFrac a, RealFrac t) => FigureData a -> [Colour Double] -> Frame a -> a -> a -> f (LabeledPoint t a) -> Svg
+ Graphics.Rendering.Plot.Light: meshGrid :: (Enum a, RealFrac a) => Frame a -> Int -> Int -> [Point a]
+ Graphics.Rendering.Plot.Light: palette :: [Colour Double] -> Int -> [Colour Double]
+ Graphics.Rendering.Plot.Light: pickColour :: RealFrac t => [Colour Double] -> t -> t -> t -> Colour Double
+ Graphics.Rendering.Plot.Light: pixel :: (Show a, RealFrac a) => [Colour Double] -> a -> a -> Scientific -> Scientific -> LabeledPoint Scientific a -> Svg
+ Graphics.Rendering.Plot.Light: pixel' :: (Show a, RealFrac a, RealFrac t) => [Colour Double] -> a -> a -> t -> t -> LabeledPoint t a -> Svg
+ Graphics.Rendering.Plot.Light: plotFun2 :: Functor f => (t -> t -> l) -> f (Point t) -> f (LabeledPoint l t)
+ Graphics.Rendering.Plot.Light: squareCentered :: (Show a, RealFrac a) => a -> a -> Maybe (Colour Double) -> Maybe (Colour Double) -> Point a -> Svg
- Graphics.Rendering.Plot.Light: circle :: (Real a1, Real a) => Point a1 -> a -> a -> Maybe (Colour Double) -> Maybe (Colour Double) -> Svg
+ Graphics.Rendering.Plot.Light: circle :: (Real a1, Real a) => a -> a -> Maybe (Colour Double) -> Maybe (Colour Double) -> Point a1 -> Svg
- Graphics.Rendering.Plot.Light: filledBand :: (Foldable t, Real o, Show a) => Colour Double -> o -> (LabeledPoint l a -> a) -> (LabeledPoint l a -> a) -> t (LabeledPoint l a) -> Svg
+ Graphics.Rendering.Plot.Light: filledBand :: (Foldable t, Real o, Show a) => Colour Double -> o -> (l -> a) -> (l -> a) -> t (LabeledPoint l a) -> Svg
- Graphics.Rendering.Plot.Light: svgHeader :: Real a => Frame a -> Svg -> Svg
+ Graphics.Rendering.Plot.Light: svgHeader :: Real a => a -> a -> Svg -> Svg

Files

app/heatmap/Main.hs view
@@ -16,32 +16,69 @@ import qualified Data.Colour.Names as C import qualified Data.Colour as C import qualified Data.Colour.Palette.BrewerSet as CP-+import qualified Data.Colour.Palette.Harmony as CH   fname = "data/heatmap-bw" -xPlot = 300+xPlot = 400 yPlot = 300 fnameOut = "data/heatmap-1.svg" -fdat = FigureData xPlot yPlot 0.1 0.9 0.1 0.85 10+-- fdat :: FigureData Double+fdat = FigureData xPlot yPlot 0.1 0.8 0.1 0.9 10 +palette0 = palette [C.blue, C.white, C.red] 15 -nColors = 9  -- 3 - 9-palette :: [C.Colour Double]-palette = CP.brewerSet CP.GnBu nColors -main :: IO ()+-- main :: IO ()+-- main = do+--   dat <- T.readFile fname+--   let pd = A.parseOnly (gridNum space) dat+--   case pd of Left e -> error e+--              Right d -> do+--                   let pixels = heatmap fdat palette0 d+--                       svg_t = svgHeader xPlot yPlot pixels+--                   -- putStrLn $ renderSvg svg_t+--                   T.writeFile fnameOut $ T.pack $ renderSvg svg_t +    +  ++ main = do-  dat <- T.readFile fname-  let pd = A.parseOnly (gridNum space) dat-  case pd of Left e -> error e-             Right d -> do-                  let pixels = heatmap fdat palette d-                      svg_t = svgHeader (mkFrameOrigin xPlot yPlot) pixels-                  -- putStrLn $ renderSvg svg_t-                  T.writeFile fnameOut $ T.pack $ renderSvg svg_t      +  let +    p1 = Point (-2) (-2)+    p2 = Point 2 2+    frame = mkFrame p1 p2+    nx = 50+    ny = 50+    f x y = cos ( pi * theta ) * sin r +      where+      r = x'**2 + y'**2+      theta = atan2 y' x'+      (x', y') = (fromRational x, fromRational y)+    lps = plotFun2 f $ meshGrid frame nx ny+    vmin = minimum $ _lplabel <$> lps+    vmax = maximum $ _lplabel <$> lps       +    pixels = heatmap' fdat palette0 frame (fromIntegral nx) (fromIntegral ny) lps+    cbar = colourBar fdat palette0 10 vmin vmax 10 TopRight 100+    svg_t = svgHeader xPlot yPlot $ do+      axes fdat frame 2 C.black 10 10+      pixels+      cbar+  T.writeFile "data/heatmap-3.svg" $ T.pack $ renderSvg svg_t+  +        ++-- withMeshGrid :: (RealFrac a, Enum a) =>+--     Point a -> Point a -> a -> a -> (Frame a -> [Point a] -> t) -> t+-- withMeshGrid p1 p2 nx ny f =+--   withFrame p1 p2 $ \fr -> let+--   grid = meshGrid fr nx ny in f fr grid ++-- withFrame p1 p2 f = f frame where+--   frame = mkFrame p1 p2+   
app/timeseries/Main.hs view
@@ -17,14 +17,11 @@ import Control.Applicative ((<|>)) import Data.Time (Day, TimeOfDay) ----fname = "data/forex_small"+fname = "data/forex" -xPlot = 800-yPlot = 600-fnameOut = "data/forex_plot_4.svg"+xPlot = 400+yPlot = 300+fnameOut = "data/forex_plot_5.svg"  fdat = FigureData xPlot yPlot 0.1 0.9 0.1 0.85 10 @@ -36,12 +33,12 @@              Right d ->                 do                let-                 figure = tsAxis fdat fop fcl fhi flo 1 C.black (-45) Nothing Nothing ( tspToLP fhi (\_ x -> x) <$> d)-                 -- figure = tsAxis fdat 2 C.black C.red (-45) Nothing Nothing ( tspToLP fplot (\ti _ -> show ti) <$> d)-                 svg_t = svgHeader (mkFrameOrigin xPlot yPlot) figure-                 -- svg_t = svgHeader (mkFrameOrigin xPlot yPlot) $ tsAxis fdat 2 C.black C.red (-45) Nothing Nothing ( tspToLP fplot (\ti _ -> show ti) <$> d)-               putStrLn $ renderSvg svg_t-               -- T.writeFile fnameOut $ T.pack $ renderSvg svg_t+                 lps =  tspToLP fhi (\_ x -> x) <$> d+                 -- figure = tsAxis fdat fop fcl fhi flo 1 C.black (-45) Nothing Nothing lps       +                 figure = tsAxis' fdat flo fhi C.magenta lps+                 svg_t = svgHeader xPlot yPlot figure+               -- putStrLn $ renderSvg svg_t+               T.writeFile fnameOut $ T.pack $ renderSvg svg_t                  where                    fhi = toFloat . rateHigh                    flo = toFloat . rateLow
+ doc/fig/heatmap.png view

binary file changed (absent → 61305 bytes)

plot-light.cabal view
@@ -1,5 +1,5 @@ name:                plot-light-version:             0.2.4+version:             0.2.5 synopsis:            A lightweight plotting library, exporting to SVG description:         This library provides drawing and plotting datastructures and functions; it is aimed in particular at scientific visualization, but it also exposes its plotting primitives and a general purpose 2D geometry library. homepage:            https://github.com/ocramz/plot-light@@ -11,7 +11,8 @@ category:            Graphics build-type:          Simple extra-source-files:  README.md-cabal-version:       >=1.10+extra-doc-files:     doc/fig/*.png+cabal-version:       >=1.18 tested-with:         GHC == 8.0.2 data-dir:            data data-files:          forex@@ -27,12 +28,14 @@   other-modules:       Graphics.Rendering.Plot.Light.Internal                        Graphics.Rendering.Plot.Light.PlotTypes.Heatmap                        Graphics.Rendering.Plot.Light.PlotTypes.Scatter+                       Graphics.Rendering.Plot.Light.PlotTypes.Histogram                        Graphics.Rendering.Plot.Light.PlotTypes.TimeSeries                        Graphics.Rendering.Plot.Light.PlotTypes.TimeSeries.Candlestick                        Graphics.Rendering.Plot.Light.Internal.Geometry                        Graphics.Rendering.Plot.Light.Internal.Utils                        Data.Parsers   build-depends:       base >= 4.7 && < 5+                     , mtl                      , time                      , scientific                      , text
src/Graphics/Rendering/Plot/Light.hs view
@@ -6,31 +6,88 @@ -- License     :  BSD3 -- Maintainer  :  Marco Zocca <zocca marco gmail> ----- `plot-light` provides functionality for rendering vector+-- @plot-light@ provides functionality for rendering vector -- graphics in SVG format. It is geared in particular towards scientific plotting, and it is termed "light" because it only requires a few common Haskell dependencies and no external libraries. -- -- == Usage ----- To use this project you just need @import Graphics.Rendering.Plot.Light@. If GHC complains of name clashes you can import the module in "qualified" form.+-- To incorporate this library in your projects you just need @import Graphics.Rendering.Plot.Light@. If GHC complains of name collisions you must import the module in "qualified" form. ----- If you wish to try out the examples in this page, you will need to have these additional import statements: --+-- == Examples+-- +-- If you wish to try out the examples in this page, you will need to have these additional statements :+-- -- @import Text.Blaze.Svg.Renderer.String (renderSvg)@ --  -- @import qualified Data.Colour.Names as C@+-- +-- === 1. Heatmap plot of a 2D function+--+-- <<doc/fig/heatmap.png#diagram=heatmap&height=200&width=266>>+--+-- > import qualified Data.Text.IO as T (readFile, writeFile)+-- > import qualified Data.Text as T+-- >+-- > xPlot = 400+-- > yPlot = 300+-- >+-- > fdat = FigureData xPlot yPlot 0.1 0.8 0.1 0.9 10+-- >+-- > palette0 = palette [C.red, C.white, C.blue] 15+-- > +-- > plotFun2ex1 = do+-- >  let +-- >    p1 = Point (-2) (-2)+-- >    p2 = Point 2 2+-- >    frame = mkFrame p1 p2+-- >    nx = 50 +-- >    ny = 50+-- >    f x y = cos ( pi * theta ) * sin r +-- >      where+-- >        r = x'**2 + y'**2+-- >        theta = atan2 y' x'+-- >        (x', y') = (fromRational x, fromRational y)+-- >    lps = plotFun2 f $ meshGrid frame nx ny+-- >    vmin = minimum $ _lplabel <$> lps+-- >    vmax = maximum $ _lplabel <$> lps   +-- >    pixels = heatmap' fdat palette0 frame nx ny lps+-- >    cbar = colourBar fdat palette0 10 vmin vmax 10 TopRight 100+-- >    svg_t = svgHeader xPlot yPlot $ do+-- >       axes fdat frame 2 C.black 10 10+-- >       pixels+-- >       cbar+-- >  T.writeFile "heatmap.svg" $ T.pack $ renderSvg svg_t+--+-- This example demonstrates how to plot a 2D scalar function and write the output to SVG file.+--+-- First, we define a `FigureData` object (which holds the SVG figure dimensions and parameters for the white margin around the rendering canvas) and a `palette`.+--+--Afterwards we declare a `Frame` that bounds the rendering canvas using `mkFrame`. This is discretized in `nx` by `ny` pixels with `meshGrid`, and the function `f` is computed at the /intersections/ of the mesh with `plotFun2`.+--+-- The `axes` function adds labeled axes to the figure; the user just needs to specify stroke width and color and how many ticks to display.+--+-- The data to be plotted (represented in this case as a list of `LabeledPoint`s, in which the "label" carries the function value) are then mapped onto the given colour palette and drawn to the SVG canvas as a `heatmap'`, i.e. a mesh of filled rectangles (Caution: do not exceed resolutions of ~ hundred pixels per side).+--+-- Next, we create the legend; in this case this is a `colourBar` element that requires the data bounds `vmin`, `vmax`.+--+-- As a last step, the SVG content is wrapped in the appropriate markdown by `svgHeader` and written to file.  module Graphics.Rendering.Plot.Light (   -- * Plot types-  heatmap,+  -- ** Heatmap+  heatmap, heatmap', plotFun2, colourBar,    -- * Plot elements   -- ** Geometrical primitives-  rect, rectCentered, circle, line, text, polyline, filledPolyline,+  rect, rectCentered, squareCentered, circle, line, text, polyline, filledPolyline, pixel, pixel',    -- ** Composite plot elements   filledBand, candlestick,   -- ** Plot utilities-  toPlot, FigureData(..),+  axes, toPlot, FigureData(..),   -- ** Element attributes-  LineStroke_(..), StrokeLineJoin_(..), TextAnchor_(..),+  LineStroke_(..), StrokeLineJoin_(..), TextAnchor_(..), LegendPosition_(..),+  -- ** Colour utilities+  blendTwo, palette, pickColour,    -- ** SVG utilities   svgHeader, translateSvg,   -- * Types@@ -51,11 +108,11 @@   -- ** Operations on vectors   frameToFrame,    -- ** Operations on frames-  frameFromPoints, frameFromFigData, mkFrame, mkFrameOrigin, width, height,+  frameFromPoints, frameFromFigData, mkFrame, mkFrameOrigin, width, height, figFWidth, figFHeight,   -- ** Typeclasses   AdditiveGroup(..), VectorSpace(..), Hermitian(..), LinearMap(..), MultiplicativeSemigroup(..), MatrixGroup(..), Eps(..),   -- ** Helpers-  toFloat, wholeDecimal+  meshGrid, toFloat, wholeDecimal   ) where  -- import qualified Data.Text as T
src/Graphics/Rendering/Plot/Light/Internal.hs view
@@ -1,11 +1,12 @@-{-# LANGUAGE OverloadedStrings #-}-module Graphics.Rendering.Plot.Light.Internal (FigureData(..), Frame(..), mkFrame, mkFrameOrigin, frameToFrame, frameToFrameValue, frameFromPoints, frameFromFigData, xmin,xmax,ymin,ymax, width, height, Point(..), mkPoint, LabeledPoint(..), mkLabeledPoint, labelPoint, mapLabel, Axis(..), svgHeader, rect, rectCentered, circle, line, tick, ticks, axis, toPlot, text, polyline, filledPolyline, filledBand, candlestick, strokeLineJoin, LineStroke_(..), StrokeLineJoin_(..), TextAnchor_(..), V2(..), Mat2(..), DiagMat2(..), diagMat2, AdditiveGroup(..), VectorSpace(..), Hermitian(..), LinearMap(..), MultiplicativeSemigroup(..), MatrixGroup(..), Eps(..), norm2, normalize2, v2fromEndpoints, v2fromPoint, origin, (-.), pointRange, movePoint, moveLabeledPointV2, moveLabeledPointBwFrames, translateSvg, toSvgFrame, toSvgFrameLP, e1, e2, toFloat, wholeDecimal) where+{-# LANGUAGE OverloadedStrings, DeriveFunctor #-}+module Graphics.Rendering.Plot.Light.Internal (FigureData(..), Frame(..), mkFrame, mkFrameOrigin, frameToFrame, frameToFrameValue, frameFromPoints, frameFromFigData, xmin,xmax,ymin,ymax, width, height, figFWidth, figFHeight, Point(..), mkPoint, LabeledPoint(..), mkLabeledPoint, labelPoint, mapLabel, Axis(..), axes, meshGrid, subdivSegment, svgHeader, rect, rectCentered, squareCentered, circle, line, tick, ticks, axis, toPlot, text, pixel, pixel', pickColour, colourBar, plusGlyph, crossGlyph, polyline, filledPolyline, filledBand, candlestick, strokeLineJoin, LineStroke_(..), StrokeLineJoin_(..), TextAnchor_(..), LegendPosition_(..), V2(..), Mat2(..), DiagMat2(..), diagMat2, AdditiveGroup(..), VectorSpace(..), Hermitian(..), LinearMap(..), MultiplicativeSemigroup(..), MatrixGroup(..), Eps(..), norm2, normalize2, v2fromEndpoints, v2fromPoint, origin, (-.), pointRange, movePoint, moveLabeledPointV2, moveLabeledPointBwFrames, translateSvg, toSvgFrame, toSvgFrameLP, e1, e2, toFloat, wholeDecimal, blendTwo, palette) where  import Data.Monoid ((<>)) import qualified Data.Foldable as F (toList) import Data.List -- import Control.Arrow ((&&&), (***)) import Control.Monad (forM, forM_)+import Control.Monad.State -- import Data.Semigroup (Min(..), Max(..)) import Data.Scientific (Scientific, toRealFloat) @@ -24,11 +25,13 @@ import qualified Data.Colour.SRGB as C  import GHC.Real+import Data.Fixed  import Graphics.Rendering.Plot.Light.Internal.Geometry import Graphics.Rendering.Plot.Light.Internal.Utils  + -- | Figure data data FigureData a = FigureData {   -- | Figure width@@ -43,20 +46,30 @@   , figTopMFrac :: a   -- | Bottom margin fraction (w.r.t figure height)     , figBottomMFrac :: a+  -- -- | Axis stroke width+  -- , figAxisStrokeWidth :: a   -- | Tick label font size   , figLabelFontSize :: Int-                       } deriving (Eq, Show)+                       } deriving (Eq, Show, Functor)   --- | Create the SVG header from a `Frame`-svgHeader :: Real a => Frame a -> Svg -> Svg-svgHeader fd =++++-- | Create the SVG header+svgHeader :: Real a =>+     a   -- ^ Image width (X axis)+  -> a   -- ^ Image height (Y axis)+  -> Svg -- ^ Image content+  -> Svg+svgHeader w h  =   S.docTypeSvg   ! SA.version "1.1"-  ! SA.width (vd $ width fd)-  ! SA.height (vd $ height fd)-  ! SA.viewbox (vds [xmin fd, ymin fd, xmax fd, ymax fd])+  ! SA.width (vd w)+  ! SA.height (vd h)+  ! SA.viewbox (vds [xmin fd, ymin fd, xmax fd, ymax fd]) where+     fd = mkFrameOrigin w h   -- | A rectangle, defined by its anchor point coordinates and side lengths@@ -86,15 +99,27 @@   -> Maybe (C.Colour Double) -- ^ Fill colour   -> Point a                 -- ^ Center coordinates        -> Svg-rectCentered  wid hei sw scol fcol p@(Point x0 y0) =-  translateSvg (Point x0c y0c) $ rect wid hei sw scol fcol p where-   x0c = x0 - (wid / 2)-   y0c = y0 - (hei / 2)   +rectCentered  wid hei sw scol fcol (Point x0 y0) =+  rect wid hei sw scol fcol p' where+    p' = Point x0c y0c+    x0c = x0 - (wid / 2)+    y0c = y0 - (hei / 2)      +squareCentered+  :: (Show a, RealFrac a) =>+     a+     -> a+     -> Maybe (C.Colour Double)+     -> Maybe (C.Colour Double)+     -> Point a+     -> Svg+squareCentered w = rectCentered w w  ++ -- | Line segment between two `Point`s --  -- > > putStrLn $ renderSvg $ line (Point 0 0) (Point 1 1) 0.1 Continuous C.blueviolet@@ -129,6 +154,38 @@     | otherwise = (x-lh, y, x+lh, y)  +plusGlyph, crossGlyph+  :: (Show a, RealFrac a) =>+     a+     -> a+     -> C.Colour Double+     -> Point a+     -> Svg+plusGlyph w sw col (Point x y) = do+  line pl pr sw Continuous col+  line pt pb sw Continuous col+  where+    wh = w / 2+    pl = Point (x-wh) y+    pr = Point (x+wh) y+    pt = Point x (y-wh)+    pb = Point x (y+wh)++crossGlyph w sw col (Point x y) = do+  line pa pb sw Continuous col+  line pc pd sw Continuous col+  where+    wh = 1.4142 * w+    pa = Point (x+wh) (x+wh)+    pb = Point (x-wh) (x-wh)+    pc = Point (x+wh) (x-wh)+    pd = Point (x-wh) (x+wh)+    +++    ++ labeledTick   :: (Show a, RealFrac a) =>      Axis@@ -190,7 +247,31 @@             | otherwise = setPointX ox  +-- -- | A pair of Cartesian axes+-- axes :: (Show a, RealFrac a) =>+--      FigureData a+--      -> a+--      -> C.Colour Double+--      -> Int+--      -> Int+--      -> Svg+axes fdat (Frame (Point xmi ymi) (Point xma yma)) sw col nx ny = do+  axis o X lenx sw col 0.01 Continuous fontsize (-45) TAEnd showlabf (V2 (-10) 0) plabx_+  axis o Y (- leny) sw col 0.01 Continuous fontsize 0 TAEnd showlabf (V2 (-10) 0) plaby_+  where+    o = Point (figWidth fdat * figLeftMFrac fdat) (figHeight fdat * figBottomMFrac fdat)+    pxend = movePoint (V2 lenx 0) o+    pyend = movePoint (V2 0 (- leny)) o+    plabx_ = zipWith LabeledPoint (pointRange nx o pxend) (take (nx+1) $ subdivSegment xmi xma $ fromIntegral nx)+    plaby_ = zipWith LabeledPoint (pointRange ny o pyend) (take (ny+1) $ subdivSegment ymi yma $ fromIntegral ny)+    fontsize = figLabelFontSize fdat+    lenx = figFWidth fdat+    leny = figFHeight fdat+    showlabf = T.pack . show . fromRational+     ++ -- | `toPlot` performs a number of related operations: -- -- * Maps the dataset to the figure frame@@ -239,19 +320,16 @@     oTo = Point left top     p2To = Point right bot +figFWidth, figFHeight :: Num a => FigureData a -> a+figFWidth = width . frameFromFigData+figFHeight = height . frameFromFigData --- | Create Axis labels from--- * fig.data (axis ranges)--- * label font size--- * a container of `LabeledPoint`s--- mkAxisLabels figdata fontsize lps = undefined---   where---     laxis =      + -- * text  -- | `text` renders text onto the SVG canvas@@ -294,13 +372,13 @@ -- > <circle cx="20.0" cy="30.0" r="15.0" fill="#ff0000" stroke="#0000ff" /> circle   :: (Real a1, Real a) =>-     Point a1                   -- ^ Center-     -> a                       -- ^ Radius+        a                       -- ^ Radius      -> a                       -- ^ Stroke width      -> Maybe (C.Colour Double) -- ^ Stroke colour      -> Maybe (C.Colour Double) -- ^ Fill colour+     -> Point a1                   -- ^ Center        -> Svg-circle (Point x y) r sw scol fcol =+circle  r sw scol fcol (Point x y) =   S.circle ! SA.cx (vd x) ! SA.cy (vd y) ! SA.r (vd r) ! colourFillOpt fcol ! colourStrokeOpt scol ! SA.strokeWidth (vd sw)   @@ -351,14 +429,14 @@ filledBand :: (Foldable t, Real o, Show a) =>     C.Colour Double          -- ^ Fill colour            -> o              -- ^ Fill opacity-           -> (LabeledPoint l a -> a) -- ^ Band maximum value-           -> (LabeledPoint l a -> a) -- ^ Band minimum value+           -> (l -> a) -- ^ Band maximum value+           -> (l -> a) -- ^ Band minimum value            -> t (LabeledPoint l a)    -- ^ Centerline points            -> Svg filledBand col opac ftop fbot lis0 = filledPolyline col opac (lis1 <> lis2) where   lis = F.toList lis0-  f1 lp = setPointY (ftop lp) $ _lp lp-  f2 lp = setPointY (fbot lp) $ _lp lp+  f1 lp = setPointY (ftop $ _lplabel lp) $ _lp lp+  f2 lp = setPointY (fbot $ _lplabel lp) $ _lp lp   lis1 = f1  <$> lis   lis2 = f2  <$> reverse lis @@ -390,7 +468,7 @@     lab = _lplabel lp     pmin = setPointY (fmin lab) p     pmax = setPointY (fmax lab) p-    hei = fboxmax lab - fboxmin lab+    hei = abs $ fboxmax lab - fboxmin lab     col | fdec (fboxmax lab) (fboxmin lab) = col1         | otherwise = col2 @@ -441,12 +519,101 @@ toSvgFrameLP from to fliplr (LabeledPoint p lab) = LabeledPoint (toSvgFrame from to fliplr p) lab  +-- withToSvgFrame figdata dat = datf+--   where+--     from = frameFromPoints $ _lp <$> dat+--     to = frameFromFigData figdata+--     datf = toSvgFrameLP from to False -- data mapping function        +++pixel+  :: (Show a, RealFrac a) =>+     [C.Colour Double]+     -> a+     -> a+     -> Scientific+     -> Scientific+     -> LabeledPoint Scientific a+     -> Svg+pixel pal w h vmin vmax (LabeledPoint p l) = rect w h 0 Nothing (Just col) p where+  col = pickColour pal (toFloat vmin) (toFloat vmax) (toFloat l)++pixel'+  :: (Show a, RealFrac a, RealFrac t) =>+     [C.Colour Double] -> a -> a -> t -> t -> LabeledPoint t a -> Svg+pixel' pal w h vmin vmax (LabeledPoint p l) = rect w h 0 Nothing (Just col) p where+  col = pickColour pal vmin vmax l   ++pickColour :: RealFrac t => [C.Colour Double] -> t -> t -> t -> C.Colour Double+pickColour pal xmin xmax x = pal !! i+  where+    i = floor (x01 * fromIntegral (nColors - 1))+    x01 = (x-xmin)/(xmax - xmin)+    nColors = length pal++++data LegendPosition_ =+  TopLeft | TopRight | BottomLeft | BottomRight deriving (Eq, Show)++posCoeff :: Fractional a => LegendPosition_ -> (a, a)+posCoeff pos =+  case pos of+    TopLeft -> (0.1, 0.1)+    TopRight -> (0.83, 0.15)+    BottomLeft -> (0.1, 0.9)+    BottomRight -> (0.9, 0.9)+++-- | A colour bar legend, to be used within `heatmap`-style plots.+colourBar+  :: (RealFrac t, RealFrac a, Show a, Enum t, Floating a) =>+     FigureData (Ratio Integer)  -- ^ Figure data+     -> [C.Colour Double]        -- ^ Palette+     -> a                        -- ^ Width+     -> t                        -- ^ Value range minimum+     -> t                        -- ^ Value range maximum+     -> Int                      -- ^ Number of distinct values+     -> LegendPosition_          -- ^ Legend position in the figure+     -> a                        -- ^ Colour bar length+     -> Svg+colourBar fdat pal w vmin vmax n legpos legh = forM_ lps (colBarPx fdat pal w h vmin vmax) where+  (legx, legy) = posCoeff legpos+  legendX = fromRational $ figWidth fdat * legx +  legendY = fromRational $ figHeight fdat * legy+  p1 = Point legendX (legendY + legh)+  p2 = Point legendX legendY+  lps = zipWith LabeledPoint (pointRange n p1 p2) v_+  h = norm2 (p1 -. p2) / fromIntegral n+  v_ = take (n+1) [vmin, vmin + dv ..]+  dv = (vmax - vmin)/fromIntegral n++++-- colBarPx+--   :: (Show a, RealFrac a, RealFrac t) =>+--      FigureData a1+--      -> [C.Colour Double]+--      -> a+--      -> a+--      -> t+--      -> t+--      -> LabeledPoint t a+--      -> Svg+colBarPx fdat pal w h vmin vmax (LabeledPoint p val) = do+  text 0 (figLabelFontSize fdat) C.black TAStart (T.pack $ show (rr val :: Fixed E6)) (V2 (1.1*w) (0.5*h)) p+  rectCentered w h 0 Nothing (Just $ pickColour pal vmin vmax val) p+  ++++   -- * Helpers  -- | Render a Colour from `colour` into a `blaze` Attribute@@ -454,6 +621,13 @@ colourAttr = S.toValue . C.sRGB24show   +-- **++rr :: (Real a, Fractional c) => a -> c+rr = fromRational . toRational+++ -- ** Conversion from primitive numerical types to AttributeValue  -- String@@ -482,16 +656,3 @@   ------       S.rect ! A.width "1" ! A.height "2" ! A.fill "#d2232c"---       -- S.path ! A.d makePath---- makePath :: S.AttributeValue--- makePath = mkPath $ do---   l 2 3---   m 4 5---- makeTransform :: S.AttributeValue--- makeTransform = translate 1 1 -- rotate 50
src/Graphics/Rendering/Plot/Light/Internal/Geometry.hs view
@@ -5,11 +5,11 @@ -} module Graphics.Rendering.Plot.Light.Internal.Geometry where --- import Data.Monoid ((<>))+import Data.Monoid ((<>))   --- | A `Point` defines a point in R2+-- | A `Point` object defines a point in the plane data Point a = Point { _px :: a,                        _py :: a } deriving (Eq) @@ -19,6 +19,14 @@ mkPoint :: a -> a -> Point a mkPoint = Point +-- | A binary operation on the components of two `Point`s+lift2Point :: (a -> b -> c) -> Point a -> Point b -> Point c+lift2Point f (Point a b) (Point c d) = Point (f a c) (f b d)++pointMin, pointMax :: (Ord a) => Point a -> Point a -> Point a+pointMin = lift2Point min+pointMax = lift2Point max+ -- | Overwrite either coordinate of a Point, to e.g. project on an axis setPointCoord :: Axis -> a -> Point a -> Point a setPointCoord axis c (Point x y)@@ -32,7 +40,9 @@ -- | A `LabeledPoint` carries a "label" (i.e. any additional information such as a text tag, or any other data structure), in addition to position information. Data points on a plot are `LabeledPoint`s. data LabeledPoint l a =   LabeledPoint {+  -- | The coordinates of the `LabeledPoint` (i.e. where in the figure it will be rendered)    _lp :: Point a,+   -- | Data associated with the `LabeledPoint`    _lplabel :: l    } deriving (Eq, Show) @@ -57,6 +67,11 @@    _fpmax :: Point a    } deriving (Eq, Show) +-- | The semigroup operation (`mappend`) applied on two `Frames` results in a new `Frame` that bounds both.+instance (Ord a, Num a) => Monoid (Frame a) where+  mempty = Frame (Point 0 0) (Point 0 0)+  mappend (Frame p1min p1max) (Frame p2min p2max) = Frame (pointMin p1min p2min) (pointMax p1max p2max)+ mkFrame :: Point a -> Point a -> Frame a mkFrame = Frame @@ -68,6 +83,7 @@ -- | Create a `Frame` from a container of `Point`s `P`, i.e. construct two points `p1` and `p2` such that : -- -- p1 := inf(x,y) P+-- -- p2 := sup(x,y) P frameFromPoints :: (Ord a, Foldable t, Functor t) =>                          t (Point a) -> Frame a@@ -267,6 +283,28 @@     vnth = (1/fromIntegral n) .* v  ++-- | A list of `nx` by `ny` points in the plane arranged on the vertices of a rectangular mesh.+--+-- NB: Only the minimum x, y coordinate point is included in the output mesh. This is intentional, since the output from this can be used as an input to functions that use a corner rather than the center point as refernce (e.g. `rect`)+meshGrid+  :: (Enum a, RealFrac a) =>+     Frame a  +  -> Int      -- ^ Number of points along x axis+  -> Int      -- ^ " y axis+  -> [Point a]+meshGrid (Frame (Point xmi ymi) (Point xma yma)) nx ny =+  [Point x y |+      x <- take nx $ subdivSegment xmi xma nx,+      y <- take ny $ subdivSegment ymi yma ny]++subdivSegment+  :: (Enum a, RealFrac a) => a -> a -> Int -> [a]+subdivSegment x1 x2 n = [xmin, xmin + dx ..] where+  dx = fromRational . toRational $ l / fromIntegral n+  xmin = min x1 x2+  xmax = max x1 x2+  l = xmax - xmin   
src/Graphics/Rendering/Plot/Light/Internal/Utils.hs view
@@ -1,10 +1,22 @@ module Graphics.Rendering.Plot.Light.Internal.Utils where -+import qualified Data.Colour as C import Data.Scientific (Scientific, toRealFloat)  -- * Misc helpers ++-- | `blendTwo c1 c2 n` creates a palette of `n` intermediate colours, interpolated linearly between `c1` and `c2`.+blendTwo :: C.Colour Double -> C.Colour Double -> Int -> [C.Colour Double]+blendTwo c1 c2 n = blf c1 c2 <$> [0, dh .. 1] where+  dh = 1/fromIntegral n+  blf cola colb x = C.blend x cola colb++-- | `palette cs n` blends linearly a list of colours `cs`, by generating `n` intermediate colours between each consecutive pair.+palette :: [C.Colour Double] -> Int -> [C.Colour Double]+palette ll n = concatMap f l1 where+  l1 = zip ll (tail ll)+  f (c1, c2) = blendTwo c2 c1 n   
src/Graphics/Rendering/Plot/Light/PlotTypes.hs view
@@ -2,7 +2,12 @@  import Graphics.Rendering.Plot.Light.PlotTypes.TimeSeries as X import Graphics.Rendering.Plot.Light.PlotTypes.Heatmap as X+import Graphics.Rendering.Plot.Light.PlotTypes.Scatter as X+import Graphics.Rendering.Plot.Light.PlotTypes.Histogram as X  import Data.Parsers as X  import Data.TimeSeries as X+++
src/Graphics/Rendering/Plot/Light/PlotTypes/Heatmap.hs view
@@ -1,10 +1,11 @@-module Graphics.Rendering.Plot.Light.PlotTypes.Heatmap (heatmap) where+module Graphics.Rendering.Plot.Light.PlotTypes.Heatmap (heatmap, heatmap', plotFun2) where  import Data.Scientific (Scientific, toRealFloat) import Graphics.Rendering.Plot.Light.Internal  import Text.Blaze.Svg +import Control.Arrow ((***), (&&&)) import Control.Monad (forM_)  -- import qualified Data.Colour.Names as C@@ -13,9 +14,8 @@ -- import Text.Blaze.Svg  --- | A 2D heatmap plot------ `heatmap` assumes the input data corresponds to evenly sampled values of a scalar-valued field, and it maps the data values onto the provided `palette` (which can be created e.g. with `brewerSet`).++-- | `heatmap` assumes the input data corresponds to evenly sampled values of a scalar-valued field, and it maps the data values onto the provided `palette` (which can be created e.g. with `brewerSet`). heatmap   :: FigureData Rational   -- ^ Figure data      -> [C.Colour Double]  -- ^ Colour palette@@ -23,13 +23,33 @@      -> Svg heatmap fdat palette d = do   let (nh, nw, vmin, vmax, d') = prepData d-      w = figWidth fdat / nw-      h = figHeight fdat / nh+      w = figFWidth fdat / nw+      h = figFHeight fdat / nh       from = Frame (Point 0 0) (Point 1 1)       to = frameFromFigData fdat-  forM_ d' (mkPixel palette w h vmin vmax . toFigFrame from to) +  forM_ d' (pixel palette w h vmin vmax . toFigFrame from to)  +-- | `heatmap'` renders one SVG pixel for every `LabeledPoint` supplied as input. The `LabeledPoint`s must be bounded by the `Frame`.+heatmap'+  :: (Foldable f, Functor f, Show a, RealFrac a, RealFrac t) =>+     FigureData a         -- ^ Figure data+     -> [C.Colour Double] -- ^ Colour palette+     -> Frame a           -- ^ Frame containing the data+     -> a                 -- ^ Number of points along x axis+     -> a                 -- ^ " y axis+     -> f (LabeledPoint t a) -- ^ Data+     -> Svg+heatmap' fdat palette from nw nh lp = do+  let+    w = figFWidth fdat / nw+    h = figFHeight fdat / nh+    to = frameFromFigData fdat+    (vmin, vmax) = (minimum &&& maximum) (_lplabel <$> lp)+  forM_ lp (pixel' palette w h vmin vmax . moveLabeledPointBwFrames from to False False) +  ++ toFigFrame   :: Fractional a =>      Frame a -> Frame a -> LabeledPoint l Rational -> LabeledPoint l a@@ -39,28 +59,9 @@ fromRationalLP (LabeledPoint (Point x y) l) = LabeledPoint (Point (fromRational x) (fromRational y)) l  -mkPixel-  :: (Show a, RealFrac a) =>-     [C.Colour Double]-     -> a-     -> a-     -> Scientific-     -> Scientific-     -> LabeledPoint Scientific a-     -> Svg-mkPixel palette w h vmin vmax (LabeledPoint p l) = rect w h 0 Nothing (Just col) p where-  col = pickColor palette (toFloat vmin) (toFloat vmax) (toFloat l)-   -pickColor :: RealFrac t => [C.Colour Double] -> t -> t -> t -> C.Colour Double-pickColor palette xmin xmax x = palette !! i-  where-    i = floor (x01 * fromIntegral (nColors - 1))-    x01 = (x-xmin)/(xmax - xmin)-    nColors = length palette  - -- | `prepData d` assumes the input lists correspond to evenly sampled values of a scalar-valued field. -- -- The function extracts the pixel mesh size, the data ranges and places the data points within the unit square [0,1] x [0,1]@@ -71,9 +72,7 @@ prepData ll = (nh, nw, valMin, valMax, d')   where     nh = fromIntegral $ length ll-    nw = fromIntegral $ length (head ll)-    -- nh = toRational $ length ll-    -- nw = toRational $ length (head ll)    +    nw = fromIntegral $ length (head ll)      d' = toUnitFramedLP nw nh <$> toCoord ll     valMin = minimum $ _lplabel <$> d'     valMax = maximum $ _lplabel <$> d'@@ -82,13 +81,22 @@  toCoord :: (Num i, Enum i) => [[c]] -> [(i, i, c)] toCoord ll = concat $ reverse $ go 0 ll [] where-  go i (x:xs) acc = go (i + 1) xs $ zip3 (repeat i) [0 ..] x : acc+  go i (x:xs) acc = go (i + 1) xs $ zip3 [0 ..] (repeat i) x : acc   go _ [] acc = acc  toUnitFramedLP :: (Fractional t) =>       t -> t -> (t, t, l) -> LabeledPoint l t toUnitFramedLP w h (i, j, x) = LabeledPoint p x   where p = Point (i/h) (j/w)++++-- | Plot a scalar function `f` of points in the plane (i.e. \(f : \mathbf{R}^2 \rightarrow \mathbf{R}\))+plotFun2+  :: Functor f =>+     (t -> t -> l) -> f (Point t) -> f (LabeledPoint l t)+plotFun2 f = fmap f' where+  f' p@(Point x y) = LabeledPoint p (f x y)   
+ src/Graphics/Rendering/Plot/Light/PlotTypes/Histogram.hs view
@@ -0,0 +1,12 @@+module Graphics.Rendering.Plot.Light.PlotTypes.Histogram where++import Graphics.Rendering.Plot.Light.Internal++import Control.Monad (forM_)+import Text.Blaze.Svg+import qualified Data.Colour as C+import qualified Data.Colour.Palette.BrewerSet as CP+import qualified Data.Colour.Names as C+++histogram figdata = undefined 
src/Graphics/Rendering/Plot/Light/PlotTypes/Scatter.hs view
@@ -1,3 +1,40 @@ module Graphics.Rendering.Plot.Light.PlotTypes.Scatter where  import Graphics.Rendering.Plot.Light.Internal++import Data.Maybe (fromMaybe)+import Control.Monad (forM_)+import Text.Blaze.Svg+import qualified Data.Colour as C+import qualified Data.Colour.Palette.BrewerSet as CP+import qualified Data.Colour.Names as C++++data GlyphShape = Square | Circle | Cross | Plus deriving (Eq, Show, Enum)++-- | Scatterplot glyph shapes+glyph+  :: (Show a, RealFrac a) =>+     a+     -> a+     -> GlyphShape+     -> Maybe (C.Colour Double)+     -> Maybe (C.Colour Double)+     -> Point a+     -> Svg+glyph w sw Square scol fcol p = squareCentered w sw scol fcol p+glyph w sw Circle scol fcol p = circle w sw scol fcol p+glyph w sw Cross _ fcol p = crossGlyph w sw (fromMaybe C.black fcol) p+glyph w sw Plus _ fcol p = plusGlyph w sw (fromMaybe C.black fcol) p+++-- | Utility function for cycling glyph colours and shapes (i.e. unique combinations of these make it easy to tell different datasets apart)+cycleGlyphCols :: (CP.ColorCat, Int) -> Int -> [(CP.Kolor, GlyphShape)]+cycleGlyphCols (pal, n) nsets = take nsets $ zip (cycle $ CP.brewerSet pal n ) (cycle [Square, Circle ..])+++scatter figdata = forM_+++
src/Graphics/Rendering/Plot/Light/PlotTypes/TimeSeries.hs view
@@ -22,6 +22,7 @@ import Text.Blaze.Svg.Renderer.String (renderSvg)  + -- | Compute the plotting coordinates of a timeseries point  -- | Preprocess the dataset for plotting @@ -75,11 +76,32 @@      -> Maybe (t (LabeledPoint l a))      -> t (LabeledPoint l a)      -> Svg-tsAxis fd fsela fselb fselc fseld sw colAxis rot plabx plaby ps =+tsAxis fd fboxmin fboxmax fmin fmax sw colAxis rot plabx plaby ps =   toPlot fd baz baz rot 0 sw colAxis plabx plaby fplot ps where+    from = frameFromPoints $ _lp <$> ps+    to = frameFromFigData fd+    fdat = frameToFrameValue from to     baz = const (T.pack "")     fplot lps =-      forM_ lps (candlestick (>) fsela fselb fselc fseld 5 1 C.green C.red colAxis)+      forM_ lps (candlestick (>) fboxmin' fboxmax' fmin' fmax' 5 1 C.green C.red colAxis)+    fboxmin' = fdat . fboxmin+    fboxmax' = fdat . fboxmax+    fmin' = fdat . fmin+    fmax' = fdat . fmax++++tsAxis' figdata fmin fmax col lps =+  toPlot figdata l0 l0 (-45) 0 5 C.black Nothing Nothing fplot lps where+  l0 = const (T.pack "")+  from = frameFromPoints $ _lp <$> lps+  to = frameFromFigData figdata+  fdat = frameToFrameValue from to+  fplot = filledBand col 0.4 fmax' fmin'+  fmax' = fdat . fmax+  fmin' = fdat . fmin++