{-# LANGUAGE OverloadedStrings #-} -- | -- Module : Graphics.Rendering.Plot.Light -- Copyright : Marco Zocca 2017 -- License : BSD3 -- Maintainer : Marco Zocca -- -- @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 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. -- -- -- == Examples -- -- 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 qualified Data.Text.IO as T (readFile, writeFile) -- > import qualified Data.Text as T -- -- === 1. Heatmap plot of a 2D function -- -- <> -- -- This example renders the function -- -- \[ -- {f(x, y) = \cos(\pi \theta) \sin( \rho^2) } -- \] -- where -- \( \rho^2 = x^2 + y^2 \) and \( \theta = \arctan(y/x) \). -- -- -- > xPlot = 400 -- > yPlot = 300 -- > fnameOut = "heatmap.svg" -- -- > 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 fnameOut $ 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. -- -- -- === 2. Scatter plot of 3D data -- -- <> -- -- 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 four functions that control the glyph size, contour line thickness, colour and opacity. This functionality can be exploited in creative ways to achieve effective infographics. -- -- > xPlot = 400 -- > yPlot = 300 -- > fnameOut = "scatter.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 [46,30,4,7,73,12,23,90,34,24,5,6,12,3,55,61] [20,35,43,23,20,1,23,8,11,17,25,4,5,26, 30] -- -- > spdata = ScatterPointData Plus 3 3 C.black 0.8 -- -- > main :: IO () -- > main = do -- > let -- > frameTo = frameFromFigData fdat -- > frameFrom = frameFromPoints $ _lp <$> dats -- > vmin = minimum $ _lplabel <$> dats -- > vmax = maximum $ _lplabel <$> dats -- > f l sz = 15 / (1 + exp(- (0.3 * x)) ) -- > where x = l + sz -- > g l w = w * (1 + l / (1 + abs l)) -- > h l col = C.blend l' C.red col -- > where -- > l' = (l - vmin)/(vmax - vmin) -- > i l alp = alp * ( 1 + l / (1 + abs l)) -- > 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 i spdata dats' -- > scatterLPBar fdat 50 vmin vmax 3 TopRight 100 f g h i spdata -- > T.writeFile fnameOut $ T.pack $ renderSvg svg_t -- module Graphics.Rendering.Plot.Light ( -- * Plot types -- ** 1D plot plotFun, Plot1DOptions(..), -- ** Heatmap heatmap, heatmap', plotFun2, colourBar, -- ** Scatter scatter, scatterLP, scatterLPBar, ScatterPointData(..), GlyphShape_(..), -- * Plot elements -- ** Geometric primitives -- *** Rectangle, square rect, rectCentered, squareCentered, -- *** Circle circle, -- *** Line line, -- *** Text text, TextAnchor_(..), -- *** Polyline polyline, filledPolyline, StrokeLineJoin_(..), -- *** Pixel pixel, pixel', -- ** Composite plot elements filledBand, candlestick, histogram, -- ** Plot utilities axes, toPlot, FigureData(..), -- ** Element attributes LineStroke_(..), LegendPosition_(..), -- ** Operations on frames frameFromPoints, frameFromFigData, mkFrame, mkFrameOrigin, width, height, figFWidth, figFHeight, -- ** Colour utilities blendTwo, palette, pickColour, -- *** ShapeCol-related ShapeCol(..), Col(..), shapeColNoBorder, shapeColNoFill, shapeColBoth, -- ** TimeSeries utilities fromTick, toTick, -- ** SVG utilities svgHeader, translateSvg, toSvgFrame, toSvgFrameLP, -- * Geometric types -- ** Frame Frame(..), -- ** Point, LabeledPoint Point(..), LabeledPoint(..), labelPoint, mapLabel, -- ** Axis Axis(..), -- * Helpers meshGrid, -- *** Misc. toFloat, wholeDecimal ) where -- import qualified Data.Text as T -- import Text.Blaze.Svg -- import Text.Blaze.Svg.Renderer.String (renderSvg) -- import qualified Data.Colour as C -- import qualified Data.Colour.Names as C -- import qualified Data.Colour.SRGB as C import Graphics.Rendering.Plot.Light.Internal import Graphics.Rendering.Plot.Light.PlotTypes