packages feed

plot-light 0.2.6 → 0.2.7

raw patch · 10 files changed

+313/−56 lines, 10 filesdep ~basenew-component:exe:scatterbinary-addedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Graphics.Rendering.Plot.Light: Circle :: GlyphShape_
+ Graphics.Rendering.Plot.Light: Cross :: GlyphShape_
+ Graphics.Rendering.Plot.Light: Plus :: GlyphShape_
+ Graphics.Rendering.Plot.Light: ScatterPointData :: GlyphShape_ -> a -> a -> Colour Double -> ScatterPointData a
+ Graphics.Rendering.Plot.Light: Square :: GlyphShape_
+ Graphics.Rendering.Plot.Light: [spColour] :: ScatterPointData a -> Colour Double
+ Graphics.Rendering.Plot.Light: [spGlyphShape] :: ScatterPointData a -> GlyphShape_
+ Graphics.Rendering.Plot.Light: [spSize] :: ScatterPointData a -> a
+ Graphics.Rendering.Plot.Light: [spStrokeWidth] :: ScatterPointData a -> a
+ Graphics.Rendering.Plot.Light: data GlyphShape_
+ Graphics.Rendering.Plot.Light: data ScatterPointData a
+ Graphics.Rendering.Plot.Light: scatter :: (Foldable t, Show a, RealFrac a) => ScatterPointData a -> t (Point a) -> Svg
+ Graphics.Rendering.Plot.Light: scatterLP :: (Foldable t, RealFrac a, Show a) => (l -> b -> a) -> (l -> b -> a) -> (l -> Colour Double -> Colour Double) -> ScatterPointData b -> t (LabeledPoint l a) -> Svg
+ Graphics.Rendering.Plot.Light: scatterLPBar :: (RealFrac t, Enum t, RealFrac b, Show b) => FigureData b -> b -> t -> t -> Int -> LegendPosition_ -> b -> (t -> b -> b) -> (t -> b -> b) -> (t -> Colour Double -> Colour Double) -> ScatterPointData b -> Svg
- Graphics.Rendering.Plot.Light: axes :: (Show a, RealFrac a) => FigureData a -> Frame Rational -> a -> Colour Double -> Int -> Int -> MarkupM ()
+ Graphics.Rendering.Plot.Light: axes :: (Show a, RealFrac a) => FigureData a -> Frame a -> a -> Colour Double -> Int -> Int -> Svg

Files

+ app/scatter/Main.hs view
@@ -0,0 +1,47 @@+module Main where++import Control.Monad (forM_)++import qualified Data.Colour as C+import qualified Data.Colour.Names as C+import Data.Scientific (Scientific, toRealFloat)+import qualified Data.Text as T +import qualified Data.Text.IO as T (readFile, writeFile)+import Text.Blaze.Svg.Renderer.String (renderSvg)++import Graphics.Rendering.Plot.Light+import Graphics.Rendering.Plot.Light.PlotTypes++xPlot = 400+yPlot = 300+fnameOut = "data/scatter-1.svg"++fdat = FigureData xPlot yPlot 0.1 0.8 0.1 0.9 10++dats = zipWith LabeledPoint p_ l_ where+  l_ = [-5, -4 .. ]+  p_ = zipWith Point [4,7,12,23,90,34,24,5,6,12,3] [43,23,1,23,8,11,17,25,4,5]++spdata = ScatterPointData Circle 3 3 C.red+++main = do+  let+    frameTo = frameFromFigData fdat+    frameFrom = frameFromPoints $ _lp <$> dats+    vmin = minimum $ _lplabel <$> dats+    vmax = maximum $ _lplabel <$> dats     +    f l sz = 10/(1 + exp(-(0.3 * x)))+      where x = l + sz+    g _ w = w+    h l col = C.blend l' C.blue col+      where+        l' = (l - vmin)/(vmax - vmin)+    dats' = moveLabeledPointBwFrames frameFrom frameTo False True <$> dats+    svg_t = svgHeader xPlot yPlot $ do+      axes fdat frameFrom 2 C.black 10 10+      scatterLP f g h spdata dats'+      scatterLPBar fdat 50 vmin vmax 3 TopRight 100 f g h spdata+  -- putStrLn $ renderSvg svg_t+  T.writeFile fnameOut $ T.pack $ renderSvg svg_t+    
doc/fig/heatmap.png view

binary file changed (41701 → 39966 bytes)

+ doc/fig/scatter.png view

binary file changed (absent → 13903 bytes)

plot-light.cabal view
@@ -1,5 +1,5 @@ name:                plot-light-version:             0.2.6+version:             0.2.7 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@@ -47,6 +47,19 @@                      -- , QuickCheck                                            +executable scatter+  default-language:    Haskell2010+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  hs-source-dirs:      app/scatter+  main-is:             Main.hs+  build-depends:       base+                     , plot-light+                     , attoparsec+                     , text+                     , colour+                     , blaze-svg+                     , scientific+                                           executable timeseries   default-language:    Haskell2010   ghc-options:         -threaded -rtsopts -with-rtsopts=-N
src/Graphics/Rendering/Plot/Light.hs view
@@ -16,11 +16,12 @@ -- -- == Examples -- --- If you wish to try out the examples in this page, you will need to have these additional statements :+-- If you wish to try out the examples in this page, you will need to have these import statements as well : ----- @import Text.Blaze.Svg.Renderer.String (renderSvg)@--- --- @import qualified Data.Colour.Names as C@+-- > import Text.Blaze.Svg.Renderer.String (renderSvg) +-- > import qualified Data.Colour.Names as C+-- > import qualified Data.Text.IO as T (readFile, writeFile)+-- > import qualified Data.Text as T --  -- === 1. Heatmap plot of a 2D function --@@ -34,9 +35,7 @@ -- where -- \( \rho^2 = x^2 + y^2 \) and \( \theta = \arctan(y/x) \). ----- > import qualified Data.Text.IO as T (readFile, writeFile)--- > import qualified Data.Text as T--- >+-- -- > xPlot = 400 -- > yPlot = 300 -- >@@ -80,11 +79,57 @@ -- 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.+--+--+-- === 2. Scatter plot of 3D data+--+-- <<doc/fig/scatter.png>>+--+-- This example shows how to plot a collection of labelled points in the plane. Each sample row is represented by a `LabeledPoint`, in which the label is a scalar quantity.+--+-- The `scatterLP` function renders each data row as a glyph, by modifying a `ScatterPointData` record of default values via three functions that control the glyph size, contour line thickness and colour. This functionality can be exploited in creative ways to achieve effective infographics.+--+--+-- > xPlot = 400+-- > yPlot = 300+-- > fnameOut = "data/scatter-1.svg"+-- >+-- > fdat = FigureData xPlot yPlot 0.1 0.8 0.1 0.9 10+-- >+-- > dats = zipWith LabeledPoint p_ l_ where+-- >   l_ = [-5, -4 .. ]+-- >   p_ = zipWith Point [4,7,12,23,90,34,24,5,6,12,3] [43,23,1,23,8,11,17,25,4,5]+-- >+-- > spdata = ScatterPointData Circle 3 3 C.red+-- >+-- >+-- > main = do+-- >  let+-- >    frameTo = frameFromFigData fdat+-- >    frameFrom = frameFromPoints $ _lp <$> dats+-- >    vmin = minimum $ _lplabel <$> dats+-- >    vmax = maximum $ _lplabel <$> dats     +-- >    f l sz = 10/(1 + exp(-(0.3 * x)))+-- >      where x = l + sz+-- >    g _ w = w+-- >    h l col = C.blend l' C.blue col+-- >      where+-- >        l' = (l - vmin)/(vmax - vmin)+-- >    dats' = moveLabeledPointBwFrames frameFrom frameTo False True <$> dats+-- >    svg_t = svgHeader xPlot yPlot $ do+-- >      axes fdat frameFrom 2 C.black 10 10+-- >      scatterLP f g h spdata dats'+-- >      scatterLPBar fdat 50 vmin vmax 3 TopRight 100 f g h spdata+-- >   T.writeFile fnameOut $ T.pack $ renderSvg svg_t+--+--  module Graphics.Rendering.Plot.Light (   -- * Plot types   -- ** Heatmap-  heatmap, heatmap', plotFun2, colourBar, +  heatmap, heatmap', plotFun2, colourBar,+  -- ** Scatter+  scatter, scatterLP, scatterLPBar, ScatterPointData(..), GlyphShape_(..),   -- * Plot elements   -- ** Geometrical primitives   rect, rectCentered, squareCentered, circle, line, text, polyline, filledPolyline, pixel, pixel', 
src/Graphics/Rendering/Plot/Light/Internal.hs view
@@ -1,5 +1,5 @@ {-# 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+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, legendBar, 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)@@ -56,7 +56,6 @@   - -- | Create the SVG header svgHeader :: Real a =>      a   -- ^ Image width (X axis)@@ -247,14 +246,15 @@             | otherwise = setPointX ox  --- -- | A pair of Cartesian axes--- axes :: (Show a, RealFrac a) =>---      FigureData a---      -> a---      -> C.Colour Double---      -> Int---      -> Int---      -> Svg+-- | A pair of Cartesian axes+axes :: (Show a, RealFrac a) =>+     FigureData a+     -> Frame 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_@@ -267,7 +267,7 @@     fontsize = figLabelFontSize fdat     lenx = figFWidth fdat     leny = figFHeight fdat-    showlabf = T.pack . show . fromRational+    showlabf x = T.pack $ show (fromRational x :: Fixed E2)       @@ -583,36 +583,58 @@      -> 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+colourBar fdat pal w vmin vmax n legpos legh =+  legendBar (fromRational <$> fdat) w vmin vmax n legpos legh (colBarPx pal)  +legendBar+  :: (Monad m, Enum t, Fractional t, Fractional a) =>+     FigureData a+     -> a+     -> t+     -> t+     -> Int+     -> LegendPosition_+     -> a+     -> (FigureData a -> a -> a -> t -> t -> LabeledPoint t a -> m b)+     -> m ()+legendBar fdat w vmin vmax n legpos legh fun = do+  -- rect wrect hrect 1 (Just C.black) (Just C.white) prect+  forM_ lps (fun fdat w h vmin vmax) where+    wrect = 0.95 * (1 - figRightMFrac fdat) * figWidth fdat+    hrect = 1.5 * legh+    prect = movePoint (V2 (-0.5 * w) (-0.5 * w)) p2+    (legx, legy) = posCoeff legpos+    legendX = figWidth fdat * legx +    legendY = figHeight fdat * legy+    p1 = Point legendX (legendY + legh)+    p2 = Point legendX legendY+    lps = zipWith LabeledPoint (pointRange n p1 p2) v_+    h = legh / 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++colBarPx+  :: (Show a, RealFrac a, RealFrac t) =>+     [C.Colour Double]       +     -> FigureData a1+     -> a+     -> a+     -> t+     -> t+     -> LabeledPoint t a+     -> Svg+colBarPx pal fdat w h vmin vmax (LabeledPoint p val) = do+  text 0 (figLabelFontSize fdat) C.black TAStart (T.pack $ show (rr val :: Fixed E3)) (V2 (1.1*w) (0.5*h)) p   rectCentered w h 0 Nothing (Just $ pickColour pal vmin vmax val) p      ++    +    -- * Helpers @@ -623,8 +645,7 @@  -- ** -rr :: (Real a, Fractional c) => a -> c-rr = fromRational . toRational+   
src/Graphics/Rendering/Plot/Light/Internal/Geometry.hs view
@@ -274,7 +274,7 @@ moveLabeledPointV2 = moveLabeledPoint . movePoint  --- | `pointRange n p q` returns a list of `n+1` equi-spaced `Point`s between `p` and `q` (i.e. the input points are included as the first and last points in the list)+-- | `pointRange n p q` returns a list of equi-spaced `Point`s between `p` and `q`. pointRange :: (Fractional a, Integral n) =>      n -> Point a -> Point a -> [Point a] pointRange n p q = [ movePoint (fromIntegral x .* vnth) p | x <- [0 .. n]]@@ -298,14 +298,20 @@       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+-- 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++subdivSegment :: (Real a, Enum b, RealFrac b) => a -> a -> Int -> [b]+subdivSegment x1 x2 n = f <$> [0, 1 ..] where   xmin = min x1 x2   xmax = max x1 x2-  l = xmax - xmin-+  l = fromRational $ toRational (xmax - xmin)+  f x  = x * l/fromIntegral n + fromRational (toRational xmin)   fromFrame :: Fractional a => Frame a -> V2 a -> V2 a
src/Graphics/Rendering/Plot/Light/Internal/Utils.hs view
@@ -22,6 +22,9 @@  -- ** Numeric formats +rr :: (Real a, Fractional c) => a -> c+rr = fromRational . toRational+ toFloat :: Scientific -> Float toFloat x = toRealFloat x :: Float 
src/Graphics/Rendering/Plot/Light/PlotTypes/Heatmap.hs view
@@ -1,5 +1,7 @@+{-# language RecordWildCards #-} module Graphics.Rendering.Plot.Light.PlotTypes.Heatmap (heatmap, heatmap', plotFun2) where +import Control.Monad.State import Data.Scientific (Scientific, toRealFloat) import Graphics.Rendering.Plot.Light.Internal @@ -8,10 +10,37 @@ import Control.Arrow ((***), (&&&)) import Control.Monad (forM_) --- import qualified Data.Colour.Names as C+ import qualified Data.Colour as C+import qualified Data.Colour.Names as C import Data.Colour.Palette.BrewerSet -- import Text.Blaze.Svg++++data MeshGrid2d a = MeshGrid2d {+    mgFrame :: Frame a+  , mgNx :: Int+  , mgNy :: Int+                           } deriving (Eq, Show)++data Heatmap a = Heatmap {+    hmMesh :: MeshGrid2d a+  , hmPalette :: [C.Colour Double]+  , hmValMin :: a+  , hmValMax :: a+                         } deriving (Eq, Show)++heatmapDefaults :: Num a => Heatmap a+heatmapDefaults = Heatmap mesh pal 0 1+  where+    pal = palette [C.red, C.white, C.blue] 20+    pmin = Point 0 0+    pmax = Point 1 1+    mesh = MeshGrid2d (Frame pmin pmax) 20 20++  +   
src/Graphics/Rendering/Plot/Light/PlotTypes/Scatter.hs view
@@ -1,24 +1,118 @@ module Graphics.Rendering.Plot.Light.PlotTypes.Scatter where  import Graphics.Rendering.Plot.Light.Internal+import Graphics.Rendering.Plot.Light.Internal.Utils +import Data.Fixed 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+import qualified Data.Text as T  +-- | Scatter plot+--+-- Every point in the plot has the same parameters, as declared in the `ScatterPointData` record+scatter+  :: (Foldable t, Show a, RealFrac a) =>+     ScatterPointData a+     -> t (Point a)+     -> Svg+scatter (ScatterPointData glshape w sw fcol) ps = +  forM_ ps $ glyph w sw glshape Nothing (Just fcol) -data GlyphShape = Square | Circle | Cross | Plus deriving (Eq, Show, Enum) ++++-- | Parametric scatter plot+--+-- The parameters of every point in the scatter plot are modulated according to the label, using the three functions.+--+-- This can be used to produce rich infographics, in which e.g. the colour and size of the glyphs carry additional information.+scatterLP+  :: (Foldable t, RealFrac a, Show a) =>+     (l -> b -> a)+     -> (l -> b -> a)+     -> (l -> C.Colour Double -> C.Colour Double)+     -> ScatterPointData b+     -> t (LabeledPoint l a)+     -> Svg+scatterLP f g h spdat lps = forM_ lps (scatterLP1 f g h spdat)+++scatterLP1+  :: (Show a, RealFrac a) =>+     (l -> b -> a)+     -> (l -> b -> a)+     -> (l -> C.Colour Double -> C.Colour Double)+     -> ScatterPointData b+     -> LabeledPoint l a+     -> Svg+scatterLP1 f g h spdat lp = glyph w' sw' sh Nothing (Just col') (_lp lp)+ where+    ScatterPointData sh w' sw' col' = modifyScatterPoint f g h spdat lp++++scatterLPBar+  :: (RealFrac t, Enum t, RealFrac b, Show b) =>+     FigureData b+     -> b+     -> t+     -> t+     -> Int+     -> LegendPosition_+     -> b+     -> (t -> b -> b)+     -> (t -> b -> b)+     -> (t -> C.Colour Double -> C.Colour Double)+     -> ScatterPointData b+     -> Svg+scatterLPBar fdat w vmin vmax n legpos legh f g h spdat = legendBar fdat w vmin vmax n legpos legh fun where+  wglyph = spSize spdat+  fun _ _ _ _ _ lp@(LabeledPoint p val) = do+    scatterLP1 f g h spdat lp+    text 0 (figLabelFontSize fdat) C.black TAStart (T.pack $ show (rr val :: Fixed E3))   (V2 (2*wglyph) (0.5*wglyph)) p+++  +      +-- | Parameters for a scatterplot glyph+data ScatterPointData a = ScatterPointData+  {+    spGlyphShape :: GlyphShape_+  , spSize :: a+  , spStrokeWidth :: a+  , spColour :: C.Colour Double+  } deriving (Eq, Show)+++modifyScatterPoint+  :: (a -> b -> c)+     -> (a -> b -> c)+     -> (a -> C.Colour Double -> C.Colour Double)+     -> ScatterPointData b+     -> LabeledPoint a d+     -> ScatterPointData c+modifyScatterPoint f g h (ScatterPointData glsh sz w col) lp =+  ScatterPointData glsh (f lab sz) (g lab w) (h lab col)+  where+    lab = _lplabel lp+++-- | Glyph shape+data GlyphShape_ = Square | Circle | Cross | Plus deriving (Eq, Show, Enum)+ -- | Scatterplot glyph shapes glyph   :: (Show a, RealFrac a) =>      a      -> a-     -> GlyphShape+     -> GlyphShape_      -> Maybe (C.Colour Double)      -> Maybe (C.Colour Double)      -> Point a@@ -30,11 +124,10 @@   -- | 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 :: (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_