packages feed

chart-unit 0.3.2 → 0.4.0

raw patch · 6 files changed

+309/−17 lines, 6 filesdep +JuicyPixelsdep +diagrams-rasterificdep ~numhask-rangePVP ok

version bump matches the API change (PVP)

Dependencies added: JuicyPixels, diagrams-rasterific

Dependency ranges changed: numhask-range

API changes (from Hackage documentation)

+ Chart.Unit: arrowChartWithRange :: (Traversable f) => V4 (Range Double) -> ArrowConfig Double -> V4 (Range Double) -> f (V4 Double) -> Chart a
+ Chart.Unit: histChartWithRange :: (Traversable f) => Rect Double -> [RectConfig] -> Aspect -> [f (Rect Double)] -> Chart a
+ Chart.Unit: lineChartWithRange :: (R2 r, Traversable f) => Rect Double -> [LineConfig] -> Aspect -> [f (r Double)] -> Chart a
+ Chart.Unit: scatterChartWithRange :: (R2 r, Traversable f) => Rect Double -> [ScatterConfig] -> Aspect -> [f (r Double)] -> Chart a
- Chart.Types: arrowColor :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) Color
+ Chart.Types: arrowColor :: forall a_am4s. Lens' (ArrowConfig a_am4s) Color
- Chart.Types: arrowHeadSize :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) a_alLZ
+ Chart.Types: arrowHeadSize :: forall a_am4s. Lens' (ArrowConfig a_am4s) a_am4s
- Chart.Types: arrowMaxHeadSize :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) a_alLZ
+ Chart.Types: arrowMaxHeadSize :: forall a_am4s. Lens' (ArrowConfig a_am4s) a_am4s
- Chart.Types: arrowMaxStaffLength :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) a_alLZ
+ Chart.Types: arrowMaxStaffLength :: forall a_am4s. Lens' (ArrowConfig a_am4s) a_am4s
- Chart.Types: arrowMaxStaffWidth :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) a_alLZ
+ Chart.Types: arrowMaxStaffWidth :: forall a_am4s. Lens' (ArrowConfig a_am4s) a_am4s
- Chart.Types: arrowMinHeadSize :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) a_alLZ
+ Chart.Types: arrowMinHeadSize :: forall a_am4s. Lens' (ArrowConfig a_am4s) a_am4s
- Chart.Types: arrowMinStaffLength :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) a_alLZ
+ Chart.Types: arrowMinStaffLength :: forall a_am4s. Lens' (ArrowConfig a_am4s) a_am4s
- Chart.Types: arrowMinStaffWidth :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) a_alLZ
+ Chart.Types: arrowMinStaffWidth :: forall a_am4s. Lens' (ArrowConfig a_am4s) a_am4s
- Chart.Types: arrowStaffLength :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) a_alLZ
+ Chart.Types: arrowStaffLength :: forall a_am4s. Lens' (ArrowConfig a_am4s) a_am4s
- Chart.Types: arrowStaffWidth :: forall a_alLZ. Lens' (ArrowConfig a_alLZ) a_alLZ
+ Chart.Types: arrowStaffWidth :: forall a_am4s. Lens' (ArrowConfig a_am4s) a_am4s
- Chart.Types: qChart :: forall a_aho3 b_aho4. (Renderable (Text Double) a_aho3, Renderable (Path V2 Double) a_aho3) => Getter (QChart a_aho3 b_aho4) (Aspect -> b_aho4 -> QDiagram a_aho3 V2 Double Any)
+ Chart.Types: qChart :: forall a_ahGw b_ahGx. (Renderable (Text Double) a_ahGw, Renderable (Path V2 Double) a_ahGw) => Getter (QChart a_ahGw b_ahGx) (Aspect -> b_ahGx -> QDiagram a_ahGw V2 Double Any)
- Chart.Types: qData :: forall a_aho3 b_aho4. Lens' (QChart a_aho3 b_aho4) b_aho4
+ Chart.Types: qData :: forall a_ahGw b_ahGx. Lens' (QChart a_ahGw b_ahGx) b_ahGx
- Chart.Types: qXY :: forall a_aho3 b_aho4. Lens' (QChart a_aho3 b_aho4) (Rect Double)
+ Chart.Types: qXY :: forall a_ahGw b_ahGx. Lens' (QChart a_ahGw b_ahGx) (Rect Double)

Files

chart-unit.cabal view
@@ -1,11 +1,22 @@ name:   chart-unit version:-  0.3.2+  0.4.0 synopsis:-  A set of native haskell charts.+  Native haskell charts. description:-  See https://tonyday567.github.io/chart-unit/index.html for some description.+  See <https://tonyday567.github.io/chart-unit/index.html chart-unit> for chart bling and usage.+  .+  .+  <<https://tonyday567.github.io/other/examplePixels.svg>>+  .+  These charts are designed to be scale independent and amenable to combinatory busywork (hence the unit suffix).+  .+  .+extra-source-files:+  readme.md+  index.html+  stack.yaml category:   charts homepage:@@ -45,7 +56,7 @@     lens,     linear,     numhask >= 0.0.4 && < 1,-    numhask-range >= 0.0.2 && < 1,+    numhask-range >= 0.0.3 && < 1,     text   default-language:     Haskell2010@@ -89,6 +100,9 @@   ghc-options:     -funbox-strict-fields     -fforce-recomp+    -threaded+    -rtsopts+    -with-rtsopts=-N   hs-source-dirs:     examples   main-is:@@ -108,7 +122,11 @@     primitive,     ad,     reflection,-    tdigest+    tdigest,+    -- for animation example+    diagrams-rasterific,+    JuicyPixels,+    diagrams-lib    default-extensions:     NoImplicitPrelude,
examples/examples.hs view
@@ -3,6 +3,9 @@  import Chart import NumHask.Prelude+import Codec.Picture.Gif (GifDelay)+import Diagrams.Backend.Rasterific.CmdLine (B, GifOpts(..))+import Diagrams.Backend.CmdLine (DiagramOpts(..), mainRender)  import FakeData @@ -289,6 +292,33 @@   where     skinny = Aspect (Rect (V2 ((5*) <$> one) one)) +displayHeader :: FilePath -> [(Diagram B, GifDelay)] -> IO ()+displayHeader fn =+  mainRender ( DiagramOpts (Just 900) (Just 700) fn+             , GifOpts {_dither = False, _noLooping = False, _loopRepeat = Just 2}+             )++chartRange' :: [[Rect Double]] -> Rect Double+chartRange' = fold . fold++exampleHistAnim :: Rect Double -> Aspect -> [[Rect Double]] -> Chart' a+exampleHistAnim cr asp rs =+    histChartWithRange cr histDefs asp rs <>+    axes+    ( chartRange .~ Just cr+    $ chartAspect .~ asp+    $ def)++exampleAnimation :: FilePath -> IO ()+exampleAnimation f = do+    xs <- mkHistData+    let yss = inits (xs!!0)+    let cr = chartRange' yss+    let us :: [Diagram B ]+        us = exampleHistAnim cr widescreen . pure <$> yss++    displayHeader f $ zip us (repeat (10 :: Int))+ main :: IO () main = do   let sOne = (400,400)@@ -322,6 +352,7 @@   fileSvg "other/examplePixels.svg" sOne examplePixels   qss <- makeOneDim   fileSvg "other/exampleOneDim.svg" (750,150) (exampleOneDim qss)+  exampleAnimation "other/anim.gif"    scratch (exampleHistCompare (IncludeOvers 1) (hs!!0) (hs!!1))   -- fileSvg "other/scratchpad.svg" (600,150) $ pad 1.1 $
+ index.html view
@@ -0,0 +1,158 @@+<meta charset="utf-8"> <link rel="stylesheet" href="https://tonyday567.github.io/other/lhs.css">+<script type="text/javascript" async+  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML">+</script>+<h1 id="chart-unit-build-status"><a href="https://tonyday567.github.io/chart-unit">chart-unit</a> <a href="https://travis-ci.org/tonyday567/chart-unit"><img src="https://travis-ci.org/tonyday567/chart-unit.png" alt="Build Status" /></a></h1>+<p><a href="https://tonyday567.github.com/chart-unit">repo</a></p>+<p><code>chart-unit</code> is an experimental haskell chart library.</p>+<p>scratchpad</p>+<p><img style="border:5px solid grey" src="other/scratchpad.svg"></p>+<h2 id="unital-charting">unital charting</h2>+<p>This slowly growing collection of charts:</p>+<ul class="incremental">+<li>renders nicely in svg over a wide chart size.</li>+<li>retain a similar look-n-feel across different scales</li>+<li>are highly customizable</li>+<li>have a unit scale in the spirit of the <a href="http://projects.haskell.org/diagrams/doc/quickstart.html">diagrams</a> design space, making combining a snap.</li>+<li>can be quickly integrated into ad-hoc haskell data analytics, providing a visual feedback loop.</li>+</ul>+<h1 id="charts">charts</h1>+<p>A chart can be anything that is represented on an XY plane, and usually comes with visual clues about what is being represented, such as axes, titles and legends. Here's the default blank chart:</p>+<div class="figure">+<img src="other/exampleAxes.svg" />++</div>+<pre><code>axes (chartAspect .~ sixbyfour $ chartRange .~ Just one $ def)</code></pre>+<p><code>sixbyfour</code> is a value of type XY, and represents the rectangle that the chart will be rendered with. As with any SVG element, the ultimate product is still subject to sizing on use, so think of <code>sixbyfour</code> as a relative aspect ratio rather than a concrete size.</p>+<h2 id="xy">XY</h2>+<p>chartRange is the range which we would like to plot. chartRange .~ Nothing represents that the range should be whatever the data range is.</p>+<h2 id="customization">customization</h2>+<p>Axes and most other things are highly customizable. Here's another style of chart:</p>+<div class="figure">+<img src="other/exampleGgplot.svg" />++</div>+<h2 id="line-chart">line chart</h2>+<p>Some actual, real-life lines to be plotted:</p>+<div class="figure">+<img src="other/exampleLine.svg" />++</div>+<pre><code>lines lineDefs sixbyfour lineData</code></pre>+<p>Breaking the code down:</p>+<ul class="incremental">+<li><code>lines</code> is a typical chart renderer, taking a</li>+<li><code>[LineConfig]</code>, which is a list of configurations for each line, an</li>+<li><code>Aspect</code>, the aspect ratio to render the chart, and, finally</li>+<li>the data, a <code>(Traversable g, Traversable f, R2~r) =&gt; g (f (r a))</code>, or in this case, a <code>[[V2 Double]]</code>, which is a double container of the values to chart</li>+</ul>+<h2 id="withchart">withChart</h2>+<p>You don't have to do anything special to combine these lines with axes.</p>+<div class="figure">+<img src="other/exampleLineAxes.svg" />++</div>+<pre><code>lines lineDefs sixbyfour lineData &lt;&gt; +axes (chartRange .~ Just (rangeR2s lineData) $ def)</code></pre>+<p><code>withChart</code> is a convenience function for this common operation, and the code below is equivalent to the above code:</p>+<pre><code>withChart def (lines lineDefs) lineData</code></pre>+<h2 id="scatter">scatter</h2>+<p>Other default chart types follow this same pattern:</p>+<div class="figure">+<img src="other/exampleScatter.svg" />++</div>+<pre><code>xys &lt;- mkScatterData+withChart (chartAspect .~ asquare $ def) (scatters scatterDefs) xys</code></pre>+<p>As with <code>line</code>, <code>scatter</code> zips together multiple configurations and multiple containers of data. It's often much easier to construct charts assuming multiple data sets.</p>+<p>A major point of the chart-unit library is that the look-n-feel of a chart is invariant to the data scale.</p>+<div class="figure">+<img src="other/exampleScatter2.svg" />++</div>+<pre><code>let xys1 = fmap (over _x (*1e8) . over _y (*1e-8)) &lt;$&gt; xys in+withChart (chartAspect .~ asquare $ def) (scatters scatterDefs) xys1</code></pre>+<h2 id="histogram">histogram</h2>+<p>A histogram, in widescreen</p>+<div class="figure">+<img src="other/exampleHist.svg" />++</div>+<p>A histogram, with unequal bin sizes (based on quantiles)</p>+<div class="figure">+<img src="other/exampleHistUnequal.svg" />++</div>+<p>... converted to a line chart</p>+<div class="figure">+<img src="other/exampleHistUnequal2.svg" />++</div>+<p>A labelled bar chart:</p>+<div class="figure">+<img src="other/exampleLabelledBar.svg" />++</div>+<h2 id="pixel-chart">pixel chart</h2>+<p>A chart with a color for each XY point in a plane. Close synonyms are probably an area chart, a surface chart. A contour chart must be close.</p>+<div class="figure">+<img src="other/examplePixels.svg" />++</div>+<h2 id="one-dim-chart">One-dim chart</h2>+<p>axes technology can be re-purposed to create skinny charts:</p>+<div class="figure">+<img src="other/exampleOneDim.svg" />++</div>+<h2 id="compound-charts">Compound charts</h2>+<p><code>chart-unit</code> is a fairly thin wrapper over diagrams that establishes the basics of what a chart is, and provides some sane defaults. From this starting point, production of high-quality charting is easy and pleasant. Some examples:</p>+<p>Comparing two histograms.</p>+<div class="figure">+<img src="other/exampleHistCompare.svg" />++</div>+<p>A scatter chart with histograms of the data along the x &amp; y dimensions.</p>+<div class="figure">+<img src="other/exampleScatterHist.svg" />++</div>+<p>A unital gradient chart, using diagrams arrows:</p>+<div class="figure">+<img src="other/exampleArrow.svg" />++</div>+<p>The <code>QChart</code> type exists to enable combining different types of charts using the same scale.</p>+<div class="figure">+<img src="other/exampleCompound.svg" />++</div>+<p>Clipping charts</p>+<div class="figure">+<img src="other/exampleClipping.svg" />++</div>+<h2 id="animation">animation</h2>+<p><img style="border:5px solid grey" src="other/anim.gif"></p>+<h2 id="data">data</h2>+<p>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).</p>+<p>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.</p>+<h2 id="diagrams-development-recipe">diagrams development recipe</h2>+<p>In constructing new chart units:</p>+<ul class="incremental">+<li>diagrams go from polymorphic &amp; abstract to strongly-typed &amp; concrete</li>+<li>V2 (-0.5 ... 0.5) (-0.5 ... 0.5) forms a unitSquare: one unit by one unit, origin in the center</li>+<li>regularly check where the origin is, usually not where it needs to be.</li>+<li>turn pointful shapes into a Trail</li>+<li>close the Trail into a SVG-like loop</li>+<li>turn the closed Trail into a QDiagram</li>+</ul>+<p>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:</p>+<pre><code>unitp f = unitSquare # f # fromVertices # closeTrail # strokeTrail</code></pre>+<h2 id="workflow">workflow</h2>+<p>recipe 1</p>+<div class="sourceCode"><pre class="sourceCode literate haskell"><code class="sourceCode haskell"><span class="ot">scratch ::</span> <span class="dt">Chart</span> <span class="dt">SVG</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()+scratch <span class="fu">=</span> fileSvg <span class="st">&quot;other/scratchpad.svg&quot;</span> (<span class="dv">600</span>,<span class="dv">400</span>)</code></pre></div>+<p>I tend to work in ghci a lot, using the above <code>scratch</code> to try code out, mashing the refresh button in the browser. Or I switch on stacks --file-watch ...</p>+<p>recipe 2</p>+<pre><code>stack build --test --exec &quot;$(stack path --local-install-root)/bin/chart-unit-examples&quot; --exec &quot;$(stack path --local-bin)/pandoc -f markdown+lhs -i app/examples.hs -t html -o index.html --filter pandoc-include --mathjax&quot; --file-watch</code></pre>
+ readme.md view
@@ -0,0 +1,23 @@++[chart-unit](https://tonyday567.github.io/chart-unit) [![Build Status](https://travis-ci.org/tonyday567/chart-unit.png)](https://travis-ci.org/tonyday567/chart-unit)+===++[repo](https://tonyday567.github.com/chart-unit)++`chart-unit` is an experimental haskell chart library.  ++At the moment, only SVG is supported, and github markdown doesn't include support for svg images.++See https://tonyday567.github.io/chart-unit for a readme.++compile recipe+--------++~~~+stack build --test --exec "$(stack path --local-install-root)/bin/chart-unit-examples" --exec "$(stack path --local-bin)/pandoc -f markdown+lhs -i app/examples.hs -t html -o index.html --filter pandoc-include --mathjax" --file-watch+~~~++to do+---++- legends, text (titles, etc)
src/Chart/Unit.hs view
@@ -11,10 +11,14 @@   , pixel1   , arrow1   , box-  , lineChart   , scatterChart+  , scatterChartWithRange+  , lineChart+  , lineChartWithRange   , histChart+  , histChartWithRange   , arrowChart+  , arrowChartWithRange   , rangeV4   , rangeV42Rect   , scaleV4s@@ -148,33 +152,70 @@         lw 0        )) <$> rs --- * charts are recipes for constructing a QDiagram from a specification of the XY plane to be projected on to (XY), a list of traversable vector containers and a list of configurations.-+-- * charts are recipes for constructing a QDiagram from a specification of the XY plane to be projected on to (XY), a list of traversable vector containers and a list of configurations.  The charts are self-scaling.+-- | a chart of scattered dot points scaled to its own range scatterChart ::     (R2 r, Traversable f) =>     [ScatterConfig] ->     Aspect ->     [f (r Double)] ->     Chart a-scatterChart defs (Aspect xy) xyss = mconcat $ zipWith scatter1 defs (scaleR2s xy xyss)+scatterChart defs asp xyss =+    scatterChartWithRange (foldMap rangeR2 xyss) defs asp xyss +-- | a chart of scattered dot points with a specific range+scatterChartWithRange ::+    (R2 r, Traversable f) =>+    Rect Double ->+    [ScatterConfig] ->+    Aspect ->+    [f (r Double)] ->+    Chart a+scatterChartWithRange cr defs (Aspect xy) xyss =+    mconcat $ zipWith scatter1 defs (projectR2 cr xy <$> xyss)++-- | a chart of lines scaled to its own range lineChart ::     (R2 r, Traversable f) =>     [LineConfig] ->     Aspect ->     [f (r Double)] ->     Chart a-lineChart defs (Aspect xy) xyss = mconcat $ zipWith line1 defs (scaleR2s xy xyss)+lineChart defs asp xyss =+    lineChartWithRange (foldMap rangeR2 xyss) defs asp xyss +-- | a chart of lines with a specific range+lineChartWithRange ::+    (R2 r, Traversable f) =>+    Rect Double ->+    [LineConfig] ->+    Aspect ->+    [f (r Double)] ->+    Chart a+lineChartWithRange cr defs (Aspect xy) xyss =+    mconcat $ zipWith line1 defs (projectR2 cr xy <$> xyss)++-- | a chart of histograms scaled to its own range histChart ::     (Traversable f) =>     [RectConfig] ->     Aspect ->     [f (Rect Double)] ->     Chart a-histChart defs (Aspect xy) rs =-    centerXY . mconcat . zipWith rect1 defs $ scaleRectss xy rs+histChart defs asp rs =+    histChartWithRange (fold $ fold <$> rs) defs asp rs +-- | a chart of histograms with a specific range+histChartWithRange ::+    (Traversable f) =>+    Rect Double ->+    [RectConfig] ->+    Aspect ->+    [f (Rect Double)] ->+    Chart a+histChartWithRange cr defs (Aspect xy) rs =+    mconcat . zipWith rect1 defs $ fmap (projectRect cr xy) <$> rs+ toPixels :: Rect Double -> (V2 Double -> Double) -> PixelConfig -> [(Rect Double, Color)] toPixels xy f cfg = zip g cs     where@@ -187,10 +228,10 @@ rescalePixels :: Rect Double -> [(Rect Double, Color)] -> [(Rect Double, Color)] rescalePixels xy xys = zip vs cs   where-    vs = scaleRects xy (fst <$> xys)+    vs = projectRect (fold $ fst <$> xys) xy . fst <$> xys     cs = snd <$> xys --- | pixels over an XY using a function+-- | pixels over an XY plane using a function pixelf ::     PixelConfig ->     Aspect ->@@ -201,6 +242,16 @@     pixel1 $ rescalePixels asp (toPixels xy f cfg)  -- | arrow lengths and sizes also need to be scaled, and so arrows doesnt fit as neatly into the whole scaling idea+arrowChartWithRange ::+    (Traversable f) =>+    V4 (Range Double) ->+    ArrowConfig Double ->+    V4 (Range Double) ->+    f (V4 Double) ->+    Chart a+arrowChartWithRange cr cfg xy xs =+    arrow1 cfg $ rescaleV4P cr xy <$> xs+ arrowChart ::     (Traversable f) =>     ArrowConfig Double ->@@ -208,7 +259,7 @@     f (V4 Double) ->     Chart a arrowChart cfg xy xs =-    arrow1 cfg $ scaleV4s xy xs+    arrow1 cfg $ rescaleV4P (rangeV4 xs) xy <$> xs  -- | rescale a V4 from rold to rnew rescaleV4P :: V4 (Range Double) -> V4 (Range Double) -> V4 Double -> V4 Double@@ -254,7 +305,7 @@ withChart conf renderer d = case conf^.chartRange of   Nothing ->       renderer (conf^.chartAspect) d <>-      axes (chartRange .~ Just (rangeR2s d) $ conf)+      axes (chartRange .~ Just (foldMap rangeR2 d) $ conf)   Just axesRange ->       combine (conf ^. chartAspect)       [ QChart renderer r d@@ -268,7 +319,7 @@         []       ]     where-      r = rangeR2s d+      r = foldMap rangeR2 d  axes ::     ChartConfig ->
+ stack.yaml view
@@ -0,0 +1,11 @@+resolver: lts-8.9++packages:+- '.'++extra-deps:+- erf-native-1.0.0.1+- polynomial-0.7.2+- tdigest-0.1+- numhask-0.0.4+- numhask-range-0.0.3