chart-unit Build Status


latest development:

scratch $ pad 1.1 $ beside (r2 (-1,0)) (beside (r2 (0,-1)) (((unitSquare # fcA (color $ Color 0.2 0.2 0.2 0.05) # lw 0) <> (centerXY $ scaleX (1/1.5) $ scaleY (1/10) $ (clipped (pathFromTrail $ (closeTrail $ fromVertices (p2 <$> [(0.5,0),(0.5,10),(2,10),(2,0)]))) $ ((mconcat . zipWith line1 (zipWith LineConfig [0.01,0.02,0.03] (opacs 0.5 palette1)))) (fmap r2 <$> [ [(0.0,1.0),(1.0,1.0),(2.0,5.0)], [(0.0,0.0),(3.0,3.0)]]))))) (axis def (Range (0.5,2)))) (axis (axisOrientation .~ Y $ def) (Range (0,2)))

This is my manual hack of what should happen to the line chart example when drawn using a scale different to the data scale.

Next step is to refactor back into the chartHub


This slowly growing collection of charts:

charts - svg







Labelled Bar Chart

Arrows Chart

api notes

Most of the api can be seen in the chartWith sig:

    chartWith :: (Traversable f, Traversable g, R2 r) => 
        -> (g (f (r Double)) -> Chart b) -- double-containered R? chart renderer
        -> V2 (Range Double)             -- render range
        -> (V2 (Range Double) -> g (f (r Double)) -> g (f (r Double))) -- scaler
        -> g (f (r Double)) -- data
        -> Chart b
    chartWith (ChartConfig p axes) renderer range' scaler ms =


Double-containered dimensioned data covers what a chart charts - one or more data sets with at least an x and y dimension (called R2 in the linear library).

Most chart variations are about what to do with the extra dimensions in the data. A rectangle, for example, is built from 4 dimensions, an anchoring of position in XY space, with a width (W) and height (Z). A contour map is three dimensional data (V3 in linear), with placement as XY and color as Z.


A range represents boundaries of a space. Diagrams often uses unit to refer to a V2 (Range (-0.5,0.5)) (Range (-0.5,0.5)).


A scaler converts data to other ranges, and helps get everything on the same page (literally).

Scale Robustness

Starting with the lowest level scatter chart (see examples/examples.hs for complete code):

scatter1 def (xys 1000 0.7)

The size of the dots scale with the data, so to bring it back, we run the data through a scaling routine, which normalises the data according to the diagrams unit, which is V2 (-0.5,0.5) (-0.5,0.5):

scatter1 def $ ((\x -> scaleR2 (rangeR2 x) x) <$> (xys 1000 0.7))

This scaling ensures that configuration paramters such as dot size, and axis look-n-feel is invariant to data scale. Hiding the scaling and axes creation in a more general function:

scatter def [def] [xys 1000 0.7]

The chart is then robust to a wide range of magnitudes:

scatter def [def] $ fmap (1e-8 *) <$> [xys 1000 0.7]

Separation of Chart and HUD

It's useful to separate a chart into two distinct bits:

The code below, for example, draws an X and Y axis without any data:

chartHud def (const mempty) (V2 (Range (-1e8,1e8)) (Range (-1e-8,1e-8))) rescaleR2s ([[]] :: [[V2 Double]])

HUDs can be built with chart routines. Here's a grid overlay that can be attached to any unit chart. Note how the axis ticks line up exactly with middle of the dots. I now think of the axes as a small subset of possible HUDs to help users interpret data.

unitScatter def [def] [V2 <$> [0..10] <*> [0..10]]

diagrams development recipe

In constructing new chart units, I keep this list around:

You can slide up and down the various diagrams abstraction levels creating transformations at each level. For example, here's something I use to work at the point level:

unitp f = unitSquare # f # fromVertices # closeTrail # strokeTrail


scratch :: Chart SVG -> IO ()
scratch = fileSvg "other/scratchpad.svg" (400,400)

I tend to work in ghci a lot, using scratch to try code out, and mashing the refresh button in the browser.

As new charts emerge I then includ ethem in examples and integrate them with

stack install && chart-unit-examples && pandoc -f markdown -t html -i -o index.html --mathjax --filter pandoc-include

rasterific space leak

Until I can work out a space leak, I'm letting png slide.

-- import Diagrams.Backend.Rasterific (Rasterific) -- scratchPng :: Chart Rasterific -> IO () -- scratchPng = filePng "other/scratchpad.png" (400,400) -- filePng "other/bar.png" s exampleBar