packages feed

amby (empty) → 0.2.0

raw patch · 12 files changed

+581/−0 lines, 12 filesdep +Chartdep +Chart-cairodep +Chart-diagramssetup-changed

Dependencies added: Chart, Chart-cairo, Chart-diagrams, amby, base, colour, data-default, data-default-class, either, exceptions, microlens, mtl, pretty-display, process, safe, scientific, statistics, vector

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Justin Sermeno (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ amby.cabal view
@@ -0,0 +1,65 @@+name:                amby+version:             0.2.0+synopsis:            Statistical data visualization.+description:         Statistical data visualization. Provides a high-level+                     interface built on top of+                     <https://github.com/timbod7/haskell-chart/wiki Chart>+                     to quickly display attractive visualizations within GHCi.+homepage:            https://github.com/githubuser/amby#readme+license:             BSD3+license-file:        LICENSE+author:              Justin Sermeno+maintainer:          Justin Sermeno+copyright:           Copyright (c) 2014-2016 Justin Sermeno+category:            Graphics+build-type:          Simple+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Amby+  other-modules:       Amby.Types+                     , Amby.Style+                     , Amby.Theme+                     , Amby.Numeric+                     , Amby.Plot+                     , Amby.Display+  build-depends:       base >= 4.7 && < 5+                     , data-default-class+                     , Chart-cairo+                     , Chart-diagrams+                     , Chart+                     , vector+                     , statistics+                     , microlens+                     , colour+                     , scientific+                     , mtl+                     , safe+                     , either+                     , pretty-display+                     , process+                     , exceptions+                     , data-default+  default-language:    Haskell2010++executable amby-exe+  hs-source-dirs:      app+  main-is:             Main.hs+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  build-depends:       base+                     , amby+  default-language:    Haskell2010++test-suite amby-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base+                     , amby+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/githubuser/amby
+ app/Main.hs view
@@ -0,0 +1,6 @@+module Main where++import Amby++main :: IO ()+main = return ()
+ src/Amby.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Amby+  (+  -- * Modules+    module Amby.Theme+  , module Amby.Numeric+  , module Amby.Types+  , module Amby.Plot+  ) where++import Amby.Types+import Amby.Numeric+import Amby.Theme+import Amby.Style+import Amby.Plot+import Amby.Display ()
+ src/Amby/Display.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Amby.Display where++import Control.Monad.Catch+import Data.Default (def)+import Text.Display+import System.IO.Unsafe (unsafePerformIO)+import System.Exit (ExitCode(..))+import System.Process (readProcessWithExitCode)++import Graphics.Rendering.Chart.Easy (Layout, EC)+import qualified Graphics.Rendering.Chart.Backend.Cairo as Cairo++import qualified Amby.Plot as Plot+import Amby.Types++instance {-# OVERLAPPING #-} Display (AmbyChart ()) where+  display a = saveAndDisplay a++instance {-# OVERLAPPING #-} Display (EC (Layout Double Double) ()) where+  display a = saveAndDisplayEC a++saveAndDisplay :: AmbyChart () -> DisplayText+saveAndDisplay chart = saveAndDisplayIO $ do+  Plot.save chart++saveAndDisplayEC :: EC (Layout Double Double) () -> DisplayText+saveAndDisplayEC chart = saveAndDisplayIO $ do+  Cairo.toFile def Plot.cairoDefSave chart++saveAndDisplayIO :: IO () -> DisplayText+{-# NOINLINE saveAndDisplayIO #-}+saveAndDisplayIO ioAction = unsafePerformIO $ do+  ioAction+  catch readImg cHandler+  where+    readImg = do+      (ec, stdout, stderr) <- readProcessWithExitCode "imgcat" [Plot.cairoDefSave] ""+      if | (ExitFailure code) <- ec -> return (mkDtStr "Unable to display chart.")+         | otherwise -> return (mkDtStr stdout)+    cHandler e+      | Just (e :: SomeException) <- fromException e =+        return $ mkDt "Could not find imgcat executable."+
+ src/Amby/Numeric.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE FlexibleContexts #-}+module Amby.Numeric+  ( contDistrDomain+  , contDistrRange+  , linspace+  , arange+  ) where++import Data.Either.Combinators+import Data.Scientific+import qualified Data.Vector.Generic as G+import qualified Data.Vector.Unboxed as U+import qualified Data.Vector as V+import Statistics.Distribution++contDistrDomain :: (ContDistr d) => d -> Int -> U.Vector Double+contDistrDomain d num = linspace (quantile d 0.0001) (quantile d 0.9999) num++contDistrRange :: (ContDistr d) => d -> U.Vector Double -> U.Vector Double+contDistrRange d x = U.map (density d) x++linspace :: Double -> Double -> Int -> U.Vector Double+linspace start stop num+  | num < 0 = error ("Number of samples, " ++ show num ++ ", must be non-negative.")+  | num == 0 || num == 1 = addStart $ U.generate num ((* delta) . fromIntegral)+  | otherwise = addStart $ U.generate num ((* step) . fromIntegral)+  where+    delta = stop - start+    step = delta / fromIntegral (num - 1)+    addStart = U.map (realToFrac . (+ start))++arange :: Double -> Double -> Double -> U.Vector Double+arange start stop step = U.fromList [start,(start + step)..stop]
+ src/Amby/Plot.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE IncoherentInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+module Amby.Plot+  ( plot+  , plotEq+  , getEC+  , save+  , saveSvg++  , cairoDefSave+  , diagramsDefSave+  ) where++import Control.Arrow+import Control.Monad+import Control.Monad.State+import qualified Data.List as L+import qualified Data.Vector.Generic as G++import Data.Default.Class+import Graphics.Rendering.Chart.Easy hiding (plot)+import qualified Graphics.Rendering.Chart.Easy as Chart+import qualified Graphics.Rendering.Chart.Backend.Cairo as Cairo+import qualified Graphics.Rendering.Chart.Backend.Diagrams as Diagrams+import Lens.Micro+import Statistics.Distribution++import Amby.Types+import Amby.Numeric+import Amby.Theme+import Amby.Style++cairoDefSave :: FilePath+cairoDefSave = ".__amby.png"++diagramsDefSave :: FilePath+diagramsDefSave = ".__amby.svg"++getEC :: AmbyChart () -> EC (Layout Double Double) ()+getEC compute = getLayout $ execState compute def++getState :: AmbyChart () -> AmbyState+getState compute = execState compute def++-- | Quick render.+-- Short-hand to render to png file using Cairo backend.+save :: AmbyChart () -> IO ()+save chart = Cairo.toFile+    def { Cairo._fo_size = size }+    cairoDefSave+    (getLayout st)+  where+    st = getState chart+    size = getSize st++-- | Short-hand to render to svg using Cairo backend+saveSvg :: AmbyChart () -> IO ()+saveSvg chart = Diagrams.toFile+    def { Diagrams._fo_size = join (***) fromIntegral size }+    diagramsDefSave+    (getLayout st)+  where+    st = getState chart+    size = getSize st++-- | Basic x y line plot.+instance (G.Vector v Double, G.Vector v (Double, Double))+  => AmbyContainer (v Double) Double where+  plot :: v Double -> v Double -> AmbyChart ()+  plot x y = plotList $ G.toList (G.zip x y)++  plotEq :: v Double -> (Double -> Double) -> AmbyChart ()+  plotEq x fn = plotList $ G.toList (G.zip x (G.map fn x))++instance (Real a) => AmbyContainer [a] a where+  plot :: [a] -> [a] -> AmbyChart ()+  plot x y = plotList $ L.zipWith (\a b -> (realToFrac a, realToFrac b)) x y++  plotEq x fn = plotList $ L.zipWith (\a b -> (realToFrac a, realToFrac b)) x (map fn x)++plotList :: [(Double, Double)] -> AmbyChart ()+plotList plotVals = do+    theme <- takeTheme+    layout <- takeLayout+    putLayout $ layout >> mkLayout theme+  where++    linePlot :: EC l (PlotLines Double Double)+    linePlot = liftEC $ do+      nextColor <- takeColor+      plot_lines_values .= [plotVals]+      plot_lines_style . line_width .= 2.5+      plot_lines_style . line_color .= nextColor++    mkLayout :: Theme -> EC (Layout Double Double) ()+    mkLayout theme = do+      Chart.plot linePlot
+ src/Amby/Style.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+module Amby.Style+  ( roundAxisData+  , noEdgeAxisData+  , scaledPaddedAxis+  , setThemeStyles+  )+  where++import Safe+import qualified Data.List as L+import Numeric++import Graphics.Rendering.Chart.Easy hiding (plot)++import Amby.Theme++-- | Equalizes precision for all labels.+-- For example the labels [0, 0.2] become [0.0, 0.2].+roundAxisData :: AxisData x -> AxisData x+roundAxisData axisData = axisData & axis_labels %~ go+  where+    go xs = map go' xs+    go' xs = xs+        & mapped . _2+        %~ \x -> case readMay x of+          Just a -> showFFloat (Just precision) a ""+          Nothing -> x+      where+        precision = maximum $ map (countAfterDecimal . snd) xs++noEdgeAxisData :: AxisData x -> AxisData x+noEdgeAxisData axisData = axisData & axis_grid %~ removeEdges+  where+    removeEdges xs+      | length xs < 2 = []+      | otherwise = (init . tail) xs++scaledPaddedAxis :: forall x. (RealFloat x, PlotValue x) => [x] -> AxisData x+scaledPaddedAxis axisPoints = defaultAxis+    & axis_grid %~ (\xs -> gridMin : xs ++ [gridMax])+  where+    getGridLimits xs+      | length xs < 1 = error "Not enough grid points"+      | otherwise = (head xs - spacing, last xs + spacing)+      where+        getSpacing (x:y:xs) = y - x+        spacing = getSpacing xs+    defaultAxis = autoAxis axisPoints+    (gridMin, gridMax) = getGridLimits (defaultAxis ^. axis_grid)++countAfterDecimal :: String -> Int+countAfterDecimal xs = case L.findIndex (== '.') xs of+  Nothing -> 0+  Just idx -> (length xs - 1) - idx++setThemeStyles :: Theme -> EC (Layout Double Double) ()+setThemeStyles theme = do+  layout_background .= (FillStyleSolid $ getBgColor theme)+  layout_plot_background .= Just (FillStyleSolid $ getPlotBgColor theme)+  layout_all_font_styles . font_name .= (getFontFamily theme)+  layout_all_font_styles . font_size .= (getFontSize theme)+  layout_axes_styles . axis_line_style . line_width .= 0.0+  layout_axes_styles . axis_grid_style . line_color .= (getGridLineColor theme)+  layout_axes_styles . axis_grid_style . line_width .= 1.5+  layout_axes_styles . axis_grid_style . line_dashes .= []+  layout_axes_styles . axis_label_gap .= 8+  layout_x_axis . laxis_override .= roundAxisData+  layout_y_axis . laxis_override .= roundAxisData+  layout_margin .= 10
+ src/Amby/Theme.hs view
@@ -0,0 +1,99 @@+module Amby.Theme+  ( Theme++  -- * Themes+  , mutedTheme+  , deepTheme+  , cleanTheme++  -- * Accessors+  , getBgColor+  , getPlotBgColor+  , getGridLineColor+  , getColorCycle+  , getFontFamily+  , getFontSize+  )+  where++import Data.Colour+import Data.Colour.SRGB+import Data.Default.Class++data Theme = Theme+  { bgColor :: AlphaColour Double+  , plotBgColor :: AlphaColour Double+  , gridLineColor :: AlphaColour Double+  , fontFamily :: String+  , fontSize :: Double+  , colorCycle :: [AlphaColour Double]+  }++instance Default Theme where+  def = deepTheme++mutedTheme :: Theme+mutedTheme = Theme+  { bgColor = opaque (sRGB24read "#FFFFFF")+  , plotBgColor = opaque (sRGB24read "#EAEAF2")+  , gridLineColor = opaque (sRGB24read "#FFFFFF")+  , fontFamily = "Verdana"+  , fontSize = 14+  , colorCycle =+    [ opaque (sRGB24read "#4878CF")+    , opaque (sRGB24read "#6ACC65")+    , opaque (sRGB24read "#D65F5F")+    , opaque (sRGB24read "#B47CC7")+    , opaque (sRGB24read "#C4AD66")+    , opaque (sRGB24read "#77BEDB")+    ]+  }++deepTheme :: Theme+deepTheme = Theme+  { bgColor = opaque (sRGB24read "#FFFFFF")+  , plotBgColor = opaque (sRGB24read "#EAEAF2")+  , gridLineColor = opaque (sRGB24read "#FFFFFF")+  , fontFamily = "Verdana"+  , fontSize = 14+  , colorCycle =+    [ opaque (sRGB24read "#4C72B0")+    , opaque (sRGB24read "#55A868")+    , opaque (sRGB24read "#C44E52")+    , opaque (sRGB24read "#8172B2")+    , opaque (sRGB24read "#CCB974")+    , opaque (sRGB24read "#64B5CD")+    ]+  }++cleanTheme :: Theme+cleanTheme = def+  { bgColor = opaque (sRGB24read "#FFFFFF")+  , plotBgColor = opaque (sRGB24read "#FFFFFF")+  , gridLineColor = opaque (sRGB24read "#EEEEEE")+  , colorCycle =+    [ opaque (sRGB24read "#1776B6")+    , opaque (sRGB24read "#FF962A")+    , opaque (sRGB24read "#24A122")+    , opaque (sRGB24read "#CF0407")+    , opaque (sRGB24read "#9564BF")+    ]+  }++getBgColor :: Theme -> AlphaColour Double+getBgColor = bgColor++getPlotBgColor :: Theme -> AlphaColour Double+getPlotBgColor = plotBgColor++getGridLineColor :: Theme -> AlphaColour Double+getGridLineColor = gridLineColor++getColorCycle :: Theme -> [AlphaColour Double]+getColorCycle = colorCycle++getFontFamily :: Theme -> String+getFontFamily = fontFamily++getFontSize :: Theme -> Double+getFontSize = fontSize
+ src/Amby/Types.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE TemplateHaskell #-}+module Amby.Types+  ( AmbyContainer(..)+  , AmbyState+  , AmbyChart++  -- * Accessors+  , takeTheme+  , theme+  , xlim+  , ylim+  , size+  , takeLayout+  , getLayout+  , getSize+  , putLayout+  )+  where++import Control.Monad.State+import Data.Default.Class+import Graphics.Rendering.Chart.Easy hiding (plot)+import Graphics.Rendering.Chart.Backend.Cairo (FileOptions(..))++import Amby.Theme+import Amby.Style++data AmbyState = AmbyState+  { _asThemeState :: Theme+  , _asLayoutState :: EC (Layout Double Double) ()+  , _asSize :: (Int, Int)+  }++instance Default AmbyState where+  def = AmbyState+    { _asThemeState = def+    , _asLayoutState = do+      setColors (getColorCycle def)+      setThemeStyles def+    , _asSize = _fo_size def+    }++$( makeLenses ''AmbyState)++type AmbyChart a = State AmbyState a++class AmbyContainer c a | c -> a where+  plot :: c -> c -> AmbyChart ()+  plotEq :: c -> (a -> a) -> AmbyChart ()++getLayout :: AmbyState -> EC (Layout Double Double) ()+getLayout s = s ^. asLayoutState++getSize :: AmbyState -> (Int, Int)+getSize s = s ^. asSize++takeTheme :: AmbyChart Theme+takeTheme = do+  t <- use asThemeState+  return t++takeLayout :: AmbyChart (EC (Layout Double Double) ())+takeLayout = do+  l <- use asLayoutState+  return l++putLayout :: EC (Layout Double Double) () -> AmbyChart ()+putLayout l = do+  asLayoutState .= l++theme :: Theme -> AmbyChart ()+theme t = do+  l <- use asLayoutState+  asLayoutState .= do+    l+    setColors (getColorCycle t)+    setThemeStyles t+  asThemeState .= t++xlim :: (Double, Double) -> AmbyChart ()+xlim rs = do+  l <- use asLayoutState+  asLayoutState .= do+    l+    layout_x_axis . laxis_generate .= scaledAxis def rs++ylim :: (Double, Double) -> AmbyChart ()+ylim rs = do+  l <- use asLayoutState+  asLayoutState .= do+    l+    layout_y_axis . laxis_generate .= scaledAxis def rs++size :: (Int, Int) -> AmbyChart ()+size rs = do+  l <- use asSize+  asSize .= rs+
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"