ansigraph (empty) → 0.1.0.0
raw patch · 10 files changed
+638/−0 lines, 10 filesdep +ansi-terminaldep +basesetup-changed
Dependencies added: ansi-terminal, base
Files
- LICENSE +22/−0
- Setup.hs +2/−0
- ansigraph.cabal +65/−0
- src/System/Console/Ansigraph.hs +27/−0
- src/System/Console/Ansigraph/Core.hs +173/−0
- src/System/Console/Ansigraph/Examples.hs +68/−0
- src/System/Console/Ansigraph/Internal/Core.hs +107/−0
- src/System/Console/Ansigraph/Internal/FlexInstances.hs +19/−0
- src/System/Console/Ansigraph/Internal/Horizontal.hs +94/−0
- src/System/Console/Ansigraph/Internal/Matrix.hs +61/−0
+ LICENSE view
@@ -0,0 +1,22 @@+The MIT License (MIT)++Copyright (c) 2015 Cliff Harvey++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ ansigraph.cabal view
@@ -0,0 +1,65 @@+name: ansigraph+version: 0.1.0.0+synopsis: Terminal-based graphing via ANSI and Unicode++description: Ansigraph is an ultralightweight terminal-based graphing utility. It uses+ Unicode characters and ANSI escape codes to display and animate colored graphs+ of vectors/functions in real and complex variables.+ .++ This functionality is provided by a 'Graphable' type class, whose method+ 'graphWith' draws a graph at the terminal. Another function 'animateWith' takes+ a list of Graphable elements and displays an animation by rendering them in+ sequence. Both of these functions take an options record as an argument. The+ 'graph' and 'animate' functions are defined to use the default options, and the+ user can define similar functions based on their own settings.+ .++ There are two main ways to use the package.++ Importing "System.Console.Ansigraph" provides all the functionality we+ typically use. This includes the /FlexibleInstances/ extension, which makes it+ marginally more convenient to use graphing functions by allowing instances like+ 'Graphable [Double]'.+ .++ If you want to use the package without activating /FlexibleInstances/ then you+ can import "System.Console.Ansigraph.Core", which provides everything except+ these instances. Then you must use one of a few newtype wrappers, namely:+ 'Graph', 'PosGraph', 'CGraph', 'Mat', 'CMat'. These wrappers are also+ available from the standard 'Ansigraph' module.+ .++ The "System.Console.Ansigraph.Examples" module contains examples of all the+ graph types with animations of various plane waves, and also shows the+ available ANSI colors.+++homepage: https://github.com/BlackBrane/ansigraph+license: MIT+license-file: LICENSE+author: Cliff Harvey+maintainer: cs.hbar+hs@gmail.com+copyright: 2015+category: Graphics+build-type: Simple++cabal-version: >=1.10++source-repository head+ type: git+ location: git://github.com/BlackBrane/ansigraph.git++library+ exposed-modules: System.Console.Ansigraph,+ System.Console.Ansigraph.Core,+ System.Console.Ansigraph.Examples++ other-modules: System.Console.Ansigraph.Internal.Core,+ System.Console.Ansigraph.Internal.FlexInstances,+ System.Console.Ansigraph.Internal.Horizontal,+ System.Console.Ansigraph.Internal.Matrix++ build-depends: base >=4.8 && <4.9, ansi-terminal >=0.6 && <0.7+ hs-source-dirs: src+ default-language: Haskell2010
+ src/System/Console/Ansigraph.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE FlexibleInstances #-}++-- | This is the primary module to import for use of the ansigraph package, which provides+-- terminal-based graphing for vectors and matrices of real and complex numbers.+--+-- This functionality is implemented via a 'Graphable' type class.+--+-- __Ansigraph is intended to be used in on of two ways:__+--+-- * __By importing "System.Console.Ansigraph"__.+-- This provides all the functionality we typically use, including the FlexibleInstances+-- extension which makes it easier to use graphing functions by allowing instances like+-- 'Graphable [Double]'. It also provides "System.Console.AnsiGraph.Core" which provides all the+-- core functionality. See the Haddock page for that module for more details.+--+-- * __By directly importing "System.Console.Ansigraph.Core"__, which does not activate+-- FlexibleInstances but includes everything else provided by the other module. This just means+-- you must use one of a handful of newtype wrappers, namely: 'Graph', 'PosGraph', 'CGraph',+-- 'Mat', 'CMat'. These wrappers are also available from the standard module.+module System.Console.Ansigraph (+ module System.Console.Ansigraph.Internal.FlexInstances+ , module System.Console.Ansigraph.Core+) where+++import System.Console.Ansigraph.Core+import System.Console.Ansigraph.Internal.FlexInstances
+ src/System/Console/Ansigraph/Core.hs view
@@ -0,0 +1,173 @@+-- | This module provides the core functionality of the ansigraph package:+-- terminal-based graphing for vectors and matrices of real and complex numbers.+--+-- This is implemented via a 'Graphable' type class.+--+-- __Ansigraph is intended to be used in on of two ways:__+--+-- * __By importing "System.Console.Ansigraph"__.+-- This provides all the functionality we typically use, including the FlexibleInstances+-- extension which makes it easier to use graphing functions by allowing instances like+-- 'Graphable [Double]'.+--+--+-- * __By directly importing "System.Console.Ansigraph.Core"__, which does not activate FlexibleInstances but+-- includes everything else provided by the other module. This just means you must use one of a+-- handful of newtype wrappers, namely: 'Graph', 'PosGraph', 'CGraph', 'Mat', 'CMat'.+-- These wrappers are also available from the standard module.+module System.Console.Ansigraph.Core (++ -- * Core Functionality+ -- *** The Graphable class+ Graphable (graphWith)+ , graph+ , animateWith+ , animate++ -- *** Graphing options+ , AGSettings (..)++ -- *** Default options+ , graphDefaults+ , blue, pink, white++ -- *** ANSI data+ , AnsiColor (..)+ , Coloring (..)++-- *** ANSI helpers+ , realColors+ , imagColors+ , colorSets+ , invert+ , setFG+ , setBG+ , lineClear+ , clear+ , applyColor+ , colorStr+ , colorStrLn++ -- * Graphable wrapper types+ , Graph (..)+ , CGraph (..)+ , PosGraph (..)+ , Mat (..)+ , CMat (..)++ -- * Graphing+ -- *** Horizontal vector graphing+ , displayRV+ , displayCV+ , displayPV+ , simpleRender+ , simpleRenderR++ -- *** Matrix graphing+ , matShow+ , displayMat+ , displayCMat++-- *** Simple (non-ANSI) graphing for strictly-positive data+ , posgraph+ , posanim++) where++import System.Console.Ansigraph.Internal.Core+import System.Console.Ansigraph.Internal.Horizontal+import System.Console.Ansigraph.Internal.Matrix++import System.Console.ANSI+import System.IO (hFlush,stdout)+import Control.Concurrent (threadDelay)+import Data.List (intersperse,transpose)+import Data.Complex (Complex,realPart,imagPart,magnitude)+++-- | Things that ansigraph knows how to render at the terminal are+-- instances of this class.+class Graphable a where+ graphWith :: AGSettings -> a -> IO ()++-- | Invokes the 'Graphable' type class method 'graphWith' with the+-- default 'AGSettings' record, 'graphDefaults'.+graph :: Graphable a => a -> IO ()+graph = graphWith graphDefaults+++---- IO / ANSI helpers ----++-- | Pauses for the specified number of µs.+pauseFor :: Int -> IO ()+pauseFor n = hFlush stdout >> threadDelay n++-- | 'pauseFor' some time interval, then clear screen and set the cursor at (0,0).+next :: Int -> IO ()+next n = do pauseFor n+ clearScreen+ setCursorPosition 0 0++-- | For some number of frames per second, return the corresponding time delta in microseconds.+deltaFromFPS :: Int -> Int+deltaFromFPS fps = 1000000 `div` fps+++---- Animation ----++-- | Any list of a 'Graphable' type can be made into an animation, by+-- 'graph'ing each element with a time delay and screen-clear after each.+-- 'AGSettings' are used to determine the time delta and any coloring/scaling options.+animateWith :: Graphable a => AGSettings -> [a] -> IO ()+animateWith s xs = let delta = deltaFromFPS $ framerate s in+ sequence_ $ intersperse (next delta) (graphWith s <$> xs)++-- | Perform 'animateWith' using default options. Equivalent to 'graph'ing each member+-- of the supplied list with a short delay and screen-clear after each.+animate :: Graphable a => [a] -> IO ()+animate = animateWith graphDefaults+++---- Wrappers to avoid needing FlexibleInstances ----++-- | Wrapper type for graph of a real vector/function+newtype Graph = Graph { unGraph :: [Double] }++-- | Wrapper type for graph of a complex vector/function+newtype CGraph = CGraph { unCGraph :: [Complex Double] }++-- | Wrapper type for graph of a non-negative real vector/function+newtype PosGraph = PosGraph { unPosGraph :: [Double] }++-- | Wrapper type for graph of a real two-index vector/two-argument function+newtype Mat = Mat { unMat :: [[Double]] }++-- | Wrapper type for graph of a complex two-index vector/two-argument function+newtype CMat = CMat { unCMat :: [[Complex Double]] }+++instance Graphable Graph where+ graphWith s = displayRV s . unGraph++instance Graphable CGraph where+ graphWith s = displayCV s . unCGraph++instance Graphable PosGraph where+ graphWith s = displayPV s . unPosGraph++instance Graphable Mat where+ graphWith s = displayMat s . unMat++instance Graphable CMat where+ graphWith s = displayCMat s . unCMat+++---- helpers for graphing/animating strictly-positive real functions ----++-- | Display a graph of the supplied (non-negative) real vector.+posgraph :: [Double] -> IO ()+posgraph = graph . PosGraph++-- | Display an animation of the supplied list of (non-negative) real vectors.+posanim :: [[Double]] -> IO ()+posanim = animate . map PosGraph
+ src/System/Console/Ansigraph/Examples.hs view
@@ -0,0 +1,68 @@+-- | A module that exports some simple demonstrations of how to use the package.+module System.Console.Ansigraph.Examples (+ waveDemo+ , waveDemoR+ , waveDemoP+ , showColors+ , demo+ , wave+) where++import System.Console.Ansigraph+import System.Console.ANSI+import Control.Monad (forM_)+import Data.Complex+++---- Wave Demo ----++wave :: [Complex Double]+wave = cis . (*) (pi/20) <$> [0..79]++deltas :: [Double]+deltas = (*) (-pi/10) <$> [0..50]++waves :: [[Complex Double]]+waves = zipWith (\z -> map (* z)) (cis <$> deltas) $ replicate 50 wave++pwave :: PosGraph+pwave = PosGraph $ (+1) . realPart <$> wave++rwaves :: [[Double]]+rwaves = map (map realPart) waves++pwaves :: [PosGraph]+pwaves = PosGraph . map (+1) <$> rwaves++-- | Display an animation of the positive real function /p(x,t) = cos(x-t) + 1/ in some units.+waveDemoP :: IO ()+waveDemoP = animate pwaves++-- | Display an animation of the real function /r(x,t) = cos(x-t)/ in the standard style, i.e. with both+-- positive and negative regions.+waveDemoR :: IO ()+waveDemoR = animate rwaves++-- | Display an animation of the complex wave /z(x,t) = exp(ix - it)/ in some units.+waveDemo :: IO ()+waveDemo = animate waves+++---- Show ANSI Colors ----++colors = [Black,Red,Green,Yellow,Blue,Magenta,Cyan,White]+intensities = [Dull,Vivid]++-- | Show all of the available 'AnsiColor's and corresponding 'ColorIntensity', 'Color' pairs.+showColors = do+ putStrLn "\n Available colors:"+ forM_ colors $ \clr -> do+ forM_ intensities $ \inten -> do+ setSGR [SetColor Foreground inten clr]+ putStr $ replicate 20 '█'+ setSGR [Reset]+ putStrLn $ " " ++ show inten ++ " " ++ show clr+ setSGR [Reset]++-- | Run all of the demos in sequence.+demo = sequence_ [waveDemoP,waveDemoR,waveDemo,showColors]
+ src/System/Console/Ansigraph/Internal/Core.hs view
@@ -0,0 +1,107 @@+module System.Console.Ansigraph.Internal.Core (+ AnsiColor (..)+ , AGSettings (..)+ , blue, pink, white+ , graphDefaults+ , Coloring (..)+ , realColors+ , imagColors+ , colorSets+ , invert+ , setFG+ , setBG+ , lineClear+ , clear+ , applyColor+ , colorStr+ , colorStrLn+) where++import System.Console.ANSI+import System.IO (hFlush, stdout)++---- Basics ----++-- | ANSI colors are characterized by a 'Color' and a 'ColorIntensity'. This+-- data type holds one of each.+data AnsiColor = AnsiColor ColorIntensity Color deriving Show++-- | Record that holds graphing options.+data AGSettings =+ AGSettings+ {+ -- | Foreground color for real number component+ realColor :: AnsiColor+ -- | Foreground color for imaginary number component.+ , imagColor :: AnsiColor+ -- | Background color for real number component.+ , realBG :: AnsiColor+ -- | Background color for imaginary number component.+ , imagBG :: AnsiColor+ -- | Framerate in fps.+ , framerate :: Int+ -- | How to rescale the size of a vector before displaying it+ -- (which is not implemented yet).+ , scaling :: (Int -> Int)+ }++blue = AnsiColor Vivid Blue+pink = AnsiColor Vivid Magenta+white = AnsiColor Vivid White++graphDefaults = AGSettings blue pink white white 15 (max 150)++-- | Holds two 'AnsiColor's representing foreground and background colors for display via ANSI.+data Coloring = Coloring AnsiColor AnsiColor deriving Show++-- | Projection retrieving foreground and background colors+-- for real number graphs in the form of a 'Coloring'.+realColors :: AGSettings -> Coloring+realColors sets = Coloring (realColor sets) (realBG sets)++-- | Projection retrieving foreground and background colors+-- for imaginary component of complex number graphs in the form of a 'Coloring'.+imagColors :: AGSettings -> Coloring+imagColors sets = Coloring (imagColor sets) (imagBG sets)++-- | Retrieves a pair of 'Coloring's for real and imaginary graph components respectively.+colorSets :: AGSettings -> (Coloring,Coloring)+colorSets s = (Coloring (realColor s) (realBG s), Coloring (imagColor s) (imagBG s))++-- | Swaps foreground and background colors within a 'Coloring'.+invert :: Coloring -> Coloring+invert (Coloring fg bg) = Coloring bg fg++-- | 'SGR' command to set the foreground to the specified 'AnsiColor'.+setFG :: AnsiColor -> SGR+setFG (AnsiColor ci c) = SetColor Foreground ci c++-- | 'SGR' command to set the background to the specified 'AnsiColor'.+setBG :: AnsiColor -> SGR+setBG (AnsiColor ci c) = SetColor Background ci c++-- | Clear any SGR settings, then print a new line and flush stdout.+lineClear :: IO ()+lineClear = setSGR [Reset] >> putStrLn "" >> hFlush stdout++-- | Clear any SGR settings and then flush stdout.+clear :: IO ()+clear = setSGR [Reset] >> hFlush stdout++-- | Apply both foreground and background color.+applyColor :: Coloring -> IO ()+applyColor (Coloring fg bg) = setSGR [setFG fg, setBG bg]++-- | Use a particular ANSI 'Coloring' to print a string at the terminal (without a newline),+-- then clear all ANSI SGR codes and flush stdout.+colorStr :: Coloring -> String -> IO ()+colorStr c s = do applyColor c+ putStr s+ clear++-- | Use a particular ANSI 'Coloring' to print a string at the terminal,+-- then clear all ANSI SGR codes, print a newline and flush stdout.+colorStrLn :: Coloring -> String -> IO ()+colorStrLn c s = do applyColor c+ putStr s+ lineClear
+ src/System/Console/Ansigraph/Internal/FlexInstances.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE FlexibleInstances #-}++module System.Console.Ansigraph.Internal.FlexInstances where++import System.Console.Ansigraph.Core+import Data.Complex+++instance Graphable [Double] where+ graphWith = displayRV++instance Graphable [Complex Double] where+ graphWith = displayCV++instance Graphable [[Double]] where+ graphWith = displayMat++instance Graphable [[Complex Double]] where+ graphWith = displayCMat
+ src/System/Console/Ansigraph/Internal/Horizontal.hs view
@@ -0,0 +1,94 @@+module System.Console.Ansigraph.Internal.Horizontal (+ displayRV+ , displayCV+ , displayPV+ , simpleRender+ , simpleRenderR+) where++import System.Console.Ansigraph.Internal.Core+import Data.Complex+import System.IO (hFlush, stdout)++---- Graphing Infrastructure ----++barChars = "█▇▆▅▄▃▂▁"++-- These values delineate regions for rounding to the nearest 1/8.+barVals :: [Double]+barVals = (+ 0.0625) . (/8) <$> [7,6..0]+ -- = [15/16, 13/16, 11/16, 9/16, 7/16, 5/16, 3/16, 1/16]++{- forward and reverse versions of unicode bar selection+ for positive and negative graph regions respectively -}++bars, barsR :: [(Double,Char)]+bars = zipWith (,) barVals barChars++barsR = zipWith (,) barVals (reverse barChars)+++selectBar, selectBarR :: Double -> Char+selectBar x = let l = filter (\p -> fst p < x) bars in case l of+ [] -> ' '+ (p:ss) -> snd p++selectBarR x = let l = filter (\p -> fst p < x) barsR in case l of+ [] -> '█'+ (p:ss) -> snd p++-- | Simple vector rendering. Yields a string of unicode chars representing graph bars+-- varying in units of 1/8. To be primarily invoked via 'graph', 'graphWith',+-- 'animate', 'animateWith'.+simpleRender :: [Double] -> String+simpleRender xs = let mx = maximum xs in+ (selectBar . (/mx)) <$> xs++-- | Simple vector rendering – inverted version. Rarely used directly.+-- It is needed to render negative graph regions.+simpleRenderR :: [Double] -> String+simpleRenderR xs = let mx = maximum xs in+ (selectBarR . (/mx)) <$> xs+++renderCV :: [Complex Double] -> (String,String,String,String)+renderCV l = let rp = realPart <$> l+ rm = negate <$> rp+ ip = imagPart <$> l+ im = negate <$> ip+ mx = maximum $ rp ++ rm ++ ip ++ im+ in (selectBar . (/mx) <$> rp,+ selectBarR . (/mx) <$> rm,+ selectBar . (/mx) <$> ip,+ selectBarR . (/mx) <$> im)++renderRV :: [Double] -> (String,String)+renderRV l = let rp = l+ rm = negate <$> rp+ mx = maximum $ rp ++ rm+ in (selectBar . (/mx) <$> rp,+ selectBarR . (/mx) <$> rm)++-- | ANSI based display for complex vectors. To be primarily invoked via 'graph', 'graphWith',+-- 'animate', 'animateWith'.+displayCV :: AGSettings -> [Complex Double] -> IO ()+displayCV s l = let (rp,rm,ip,im) = renderCV l+ (rcol,icol) = colorSets s+ in do colorStrLn rcol rp+ colorStrLn (invert rcol) rm+ colorStrLn icol ip+ colorStrLn (invert icol) im++-- | ANSI based display for real vectors. To be primarily invoked via 'graph', 'graphWith',+-- 'animate', 'animateWith'.+displayRV :: AGSettings -> [Double] -> IO ()+displayRV s l = let (rp,rm) = renderRV l+ rcol = realColors s+ in do colorStrLn rcol rp+ colorStrLn (invert rcol) rm++-- | ANSI based display for positive real vectors. To be primarily invoked via 'graph', 'graphWith',+-- 'animate', 'animateWith'.+displayPV :: AGSettings -> [Double] -> IO ()+displayPV s l = let (rp,_) = renderRV l+ rcol = realColors s in colorStrLn rcol rp
+ src/System/Console/Ansigraph/Internal/Matrix.hs view
@@ -0,0 +1,61 @@+module System.Console.Ansigraph.Internal.Matrix (+ matShow+ , displayMat+ , displayCMat+) where++import System.Console.Ansigraph.Internal.Core+import Data.Complex++---- Matrices ----++mmap :: (a -> b) -> [[a]] -> [[b]]+mmap = map . map++mmax :: (Num a, Ord a) => [[a]] -> a+mmax = maximum . map maximum . mmap abs++densityChars = "█▓▒░"++densityVals :: [Double]+densityVals = (+ 0.125) . (/4) <$> [3,2,1,0]+ -- = [7/8, 5/8, 3/8, 1/8]++blocks, blocksR :: [(Double,Char)]+blocks = zipWith (,) densityVals densityChars++blocksR = zipWith (,) densityVals (reverse densityChars)+++selectBlock :: Double -> Char+selectBlock x = let l = filter (\p -> fst p < abs x) blocks in case l of+ [] -> ' '+ (p:ss) -> snd p++-- | Given a matrix of Doubles, return the list of strings illustrating the absolute value+-- of each entry relative to the largest, via unicode chars that denote a particular density.+matShow :: [[Double]] -> [String]+matShow m = let mx = mmax m+ in mmap (selectBlock . (/ mx)) m++-- | Use ANSI coloring (specified by an 'AGSettings') to visually display a Real matrix.+displayMat :: AGSettings -> [[Double]] -> IO ()+displayMat s = mapM_ (colorStrLn (realColors s)) . matShow+++matShow_Imag :: [[Complex Double]] -> [String]+matShow_Imag m = let mx = max (mmax $ mmap realPart m)+ (mmax $ mmap imagPart m)+ in mmap (selectBlock . (/ mx) . imagPart) m++matShow_Real :: [[Complex Double]] -> [String]+matShow_Real m = let mx = max (mmax $ mmap realPart m)+ (mmax $ mmap imagPart m)+ in mmap (selectBlock . (/ mx) . realPart) m++-- | Use ANSI coloring (specified by an 'AGSettings') to visually display a Complex matrix.+displayCMat :: AGSettings -> [[Complex Double]] -> IO ()+displayCMat s m = sequence_ $+ zipWith (\x y -> x >> putStr " " >> y)+ (colorStr (realColors s) <$> matShow_Real m)+ (colorStrLn (imagColors s) <$> matShow_Imag m)