asciichart 1.0.0 → 1.0.1
raw patch · 4 files changed
+81/−56 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- README.md +5/−3
- asciichart.cabal +1/−1
- src/Data/Text/Chart.hs +74/−52
- test/Test.hs +1/−0
README.md view
@@ -1,11 +1,12 @@ # asciichart+ [](https://opensource.org/licenses/MIT) [](https://travis-ci.org/madnight/asciichart) -ASCII line charts in terminal ╭┈╯. Console line charts in pure Haskell. +ASCII line charts in terminal ╭┈╯. Console line charts in pure Haskell. This is a Haskell port of the Javascript library [kroitor/asciichart](https://github.com/kroitor/asciichart). Free for any usage (MIT License). -+ ## Usage ```bash@@ -19,7 +20,8 @@ main = plot [1..20] ``` -For more examples e.g. sinus wave see [examples folder](https://github.com/madnight/asciichart/tree/master/examples).+For more examples e.g. sinus wave see [examples folder](https://github.com/madnight/asciichart/tree/master/examples). +You can also find this package on [Hackage](http://hackage.haskell.org/package/asciichart). ## References
asciichart.cabal view
@@ -1,5 +1,5 @@ name: asciichart-version: 1.0.0+version: 1.0.1 synopsis: Line charts in terminal license: MIT license-file: LICENSE
src/Data/Text/Chart.hs view
@@ -1,39 +1,59 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE Safe #-}+{-# LANGUAGE FlexibleContexts #-} +-----------------------------------------------------------------------------+-- |+-- Module : Data.Text.Chart+-- License : MIT+-- Maintainer : Fabian Beuke <mail@beuke.org>+--+-- This module contains 4 functions. The plot function provides a very simple+-- interface for plotting. It takes a List of Integers and prints out a+-- corresponding chart with a default terminal height of 14 blocks.+-- The 'plot' function is therefore equivalent to @'plotWith'+-- options {height = 14}@. You can find some examples+-- <https://github.com/madnight/asciichart/tree/master/examples here>.+-----------------------------------------------------------------------------+ module Data.Text.Chart- ( plot+ ( -- * Plot+ plot , plotWith+ -- * Options , options , height ) where -import Control.Monad (forM_)-import Data.Array.IO (newArray, IOArray, getElems, writeArray)-import Data.Char (isSpace)-import Data.List (unfoldr, dropWhileEnd)-import Text.Printf (printf)- #if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>))+import Control.Applicative ((<$>))+import Control.Monad.ST.Safe (ST, runST)+#else+import Control.Monad.ST (ST, runST) #endif+import Control.Monad (forM_)+import Data.Array.ST.Safe (STArray, getElems, writeArray, newArray)+import Data.Char (isSpace)+import Data.List (unfoldr, dropWhileEnd)+import Text.Printf (printf)+import Data.Bool (bool) data Options =- Options { height :: Int }+ Options { height :: Int -- ^ Allows to set the height of the chart.+ } --- default options+-- | Provides default options: @Options { 'height' = 14 }@. options :: Options options = Options { height = 14 } -newArray2D :: Integer -> Integer -> IO (IOArray (Integer, Integer) String)+newArray2D :: Integer -> Integer ->+ ST s (STArray s (Integer, Integer) String) newArray2D dimX dimY = newArray ((0,0), (dimX, dimY)) " " splitEvery :: Int -> [a] -> [[a]] splitEvery n = takeWhile (not . null) . unfoldr (Just . splitAt n) -plot :: [Integer] -> IO ()-plot = plotWith options- pad :: Integral a => [a] -> Int pad series = let floats = fromIntegral <$> series@@ -41,60 +61,62 @@ toStr = fmap (printf "%0.2f") in maximum $ length <$> toStr floats -plotWith :: Options -> [Integer] -> IO ()-plotWith options series = do+plotWith' :: Options -> [Integer] -> [String]+plotWith' opts series = -- variables and functions let min' = minimum series- let max' = maximum series- let range = abs $ max' - min'- let offset' = 3- let height' = height options- let ratio = fromIntegral height' / fromIntegral range :: Float- let min2 = fromIntegral min' * ratio- let max2 = fromIntegral max' * ratio- let rows = round $ abs $ max2 - min2- let width = length series + 3- let pad' = pad series+ max' = maximum series+ range = abs $ max' - min'+ offset = 3+ ratio = fromIntegral (height opts) / fromIntegral range :: Float+ min2 = fromIntegral min' * ratio+ max2 = fromIntegral max' * ratio+ rows = round $ abs $ max2 - min2+ width = toInteger $ length series + 3 + in runST $ do+ -- array creation- arr <- newArray2D rows (toInteger width)- let write arr [x] [y] = writeArray arr (x, y)- let result = write arr+ arr <- newArray2D rows width+ let result x y = writeArray arr (head x, head y) -- axis and labels forM_ [min2..max2] $ \y -> do let label = fromInteger max' - (y - min2) * fromInteger range / fromIntegral rows- result [round(y - min2)] [maximum [offset' - 5, 0]] $- printf ("%"++ show pad' ++".2f") label- result [round(y - min2)] [offset' - 1] $- if y == 0 then "┼" else "┤"+ result [round $ y - min2] [max 0 $ offset - 5] $+ printf ("%" ++ show (pad series) ++ ".2f") label+ result [round $ y - min2] [offset - 1] . bool "┤" "┼" $ y == 0 -- initial value let first = fromInteger (head series) * ratio - min2- result [round(fromInteger rows - first)] [offset' - 1] "┼"+ result [round $ fromInteger rows - first] [offset - 1] "┼" -- plot the line- forM_ [0..(length series - 2)] $ \x' -> do- let x = toInteger x'- let offset'' = x + offset'- let y0 = round (fromInteger (series !! (x' + 0)) * ratio) - round min2- let y1 = round (fromInteger (series !! (x' + 1)) * ratio) - round min2+ forM_ [0..length series - 2] $ \x -> do+ let offset' = toInteger x + offset+ let y' i = round (fromInteger (series !! i) * ratio) - round min2+ let (y0, y1) = (y' x, y' $ x + 1) if y0 == y1 then- result [rows - y0] [offset''] "─"+ result [rows - y0] [offset'] "─" else do- result [rows - y1] [offset''] $- if y0 > y1 then "╰" else "╭"- result [rows - y0] [offset''] $- if y0 > y1 then "╮" else "╯"- let start = minimum [y0, y1] + 1- let end = maximum [y0, y1]+ result [rows - y1] [offset'] . bool "╭" "╰" $ y0 > y1+ result [rows - y0] [offset'] . bool "╯" "╮" $ y0 > y1 - forM_ [start..(end - 1)] $ \y ->- result [rows - y] [offset''] "│"+ forM_ [min y0 y1 + 1..max y0 y1 - 1] $ \y ->+ result [rows - y] [offset'] "│" - -- print the results- elements <- getElems arr- let result = splitEvery (width + 1) elements- forM_ result $ putStrLn . dropWhileEnd isSpace . concat+ getElems arr++-- | Takes a List of Integers and prints out a+-- corresponding chart with a default terminal height of 14 blocks.+plot :: [Integer] -> IO ()+plot = plotWith options++-- | Same as plot but it's possible to define custom options.+-- Example: @'plotWith' options { 'height' = 20 }@+plotWith :: Options -> [Integer] -> IO ()+plotWith options' series = forM_ result $+ putStrLn . dropWhileEnd isSpace . concat+ where result = splitEvery (length series + 4) $ plotWith' options' series
test/Test.hs view
@@ -1,3 +1,4 @@+{-# OPTIONS_GHC -fno-warn-type-defaults #-} {-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-}