granite-0.7.4.0: Easy terminal plotting.
Copyright(c) 2025
LicenseMIT
Maintainermschavinda@gmail.com
Stabilityexperimental
PortabilityPOSIX
Safe HaskellNone
LanguageGHC2021

Granite

Description

A terminal-based plotting library that renders beautiful charts using Unicode Braille characters and ANSI colors. Granite provides a variety of chart types including scatter plots, line graphs, bar charts, pie charts, histograms, heatmaps, and box plots.

Basic Usage

Create a simple scatter plot:

{-# LANGUAGE OverloadedStrings #-}
import Granite
import Data.Text.IO as T

main = do
  let points = [(x, sin x) | x <- [0, 0.1 .. 6.28]]
      chart = scatter [series "sin(x)" points] defPlot
  T.putStrLn chart

Customization

Plots can be customized using record update syntax:

let customPlot = defPlot
      { widthChars = 80
      , heightChars = 30
      , plotTitle = "My Chart"
      , legendPos = LegendBottom
      }

Terminal Requirements

This library requires a terminal that supports:

  • Unicode (specifically Braille patterns U+2800-U+28FF)
  • ANSI color codes
  • Monospace font with proper Braille character rendering
Synopsis

Plot Configuration

data Plot Source #

Plot configuration parameters.

Controls the appearance and layout of generated charts.

Constructors

Plot 

Fields

defPlot :: Plot Source #

Default plot configuration.

Creates a 60×20 character plot with reasonable defaults:

defPlot = Plot
  { widthChars   = 60
  , heightChars  = 20
  , leftMargin   = 6
  , bottomMargin = 2
  , titleMargin  = 1
  , xBounds      = (Nothing, Nothing)
  , yBounds      = (Nothing, Nothing)
  , plotTitle    = ""
  , legendPos    = LegendRight
  , colorPalette = [ BrightBlue, BrightMagenta, BrightCyan, BrightGreen, BrightYellow, BrightRed, BrightWhite, BrightBlack]
  , xFormatter   = \ _ _ v -> show v
  , yFormatter   = \ _ _ v -> show v
  , xNumTicks    = 2
  , yNumTicks    = 2
  }

data LegendPos Source #

Position of the legend in the plot.

Constructors

LegendRight

Display legend on the right side of the plot

LegendBottom

Display legend below the plot

LegendNone

Do not display legend.

Instances

Instances details
Show LegendPos Source # 
Instance details

Defined in Granite

Eq LegendPos Source # 
Instance details

Defined in Granite

Formatting

data Color Source #

A 24-bit RGB colour.

Constructors

Color !Word8 !Word8 !Word8 

Bundled Patterns

pattern Black :: Color 
pattern Red :: Color 
pattern Green :: Color 
pattern Yellow :: Color 
pattern Blue :: Color 
pattern Magenta :: Color 
pattern Cyan :: Color 
pattern White :: Color 
pattern BrightBlack :: Color 
pattern BrightRed :: Color 
pattern BrightGreen :: Color 
pattern BrightYellow :: Color 
pattern BrightBlue :: Color 
pattern BrightMagenta :: Color 
pattern BrightCyan :: Color 
pattern BrightWhite :: Color 
pattern Default :: Color 

Instances

Instances details
Read Color Source # 
Instance details

Defined in Granite.Color

Show Color Source # 
Instance details

Defined in Granite.Color

Methods

showsPrec :: Int -> Color -> ShowS #

show :: Color -> String #

showList :: [Color] -> ShowS #

Eq Color Source # 
Instance details

Defined in Granite.Color

Methods

(==) :: Color -> Color -> Bool #

(/=) :: Color -> Color -> Bool #

type LabelFormatter Source #

Arguments

 = AxisEnv

Axis context (domain, tick index/count, etc)

-> Int

Slot width budget in characters for this tick.

-> Double

Raw data value for the tick

-> Text

Rendered label (if it doesn't fit in the slot it will be truncated)

Axis-aware, width-limited, tick-label formatter.

Given:

  • axis context
  • a per-tick width budget (in terminal cells)
  • and the raw tick value.

returns the label to render.

data AxisEnv Source #

What the formatter gets to know about the axis/ticks

Constructors

AxisEnv 

Fields

Data Preparation

series Source #

Arguments

:: Text

Name of the series (appears in legend)

-> [(Double, Double)]

List of (x, y) data points

-> (Text, [(Double, Double)]) 

Create a named data series for multi-series plots.

let s1 = series "Dataset A" [(1,2), (2,4), (3,6)]
    s2 = series "Dataset B" [(1,3), (2,5), (3,7)]
    chart = scatter [s1, s2] defPlot

bins :: Int -> Double -> Double -> Bins Source #

Create a bin configuration for histograms.

bins 10 0 100  -- 10 bins from 0 to 100
bins 20 (-5) 5 -- 20 bins from -5 to 5

data Bins Source #

Defines the binning parameters.

Constructors

Bins 

Fields

Instances

Instances details
Show Bins Source # 
Instance details

Defined in Granite

Methods

showsPrec :: Int -> Bins -> ShowS #

show :: Bins -> String #

showList :: [Bins] -> ShowS #

Eq Bins Source # 
Instance details

Defined in Granite

Methods

(==) :: Bins -> Bins -> Bool #

(/=) :: Bins -> Bins -> Bool #

Chart Types

histogram Source #

Arguments

:: Bins

Binning configuration

-> [Double]

Raw data values to bin

-> Plot

Plot configuration

-> Text

Rendered chart as Text

Create a histogram from numerical data.

Data is binned according to the provided Bins configuration.

Example

Expand
import System.Random

-- Generate random normal-like distribution
let values = take 1000 $ randomRs (0, 100) gen
    chart = histogram (bins 20 0 100) values defPlot

bars Source #

Arguments

:: [(Text, Double)]

List of (category, value) pairs

-> Plot

Plot configuration

-> Text

Rendered chart as Text

Create a bar chart from categorical data.

Each bar is colored differently and labeled with its category name.

Example

Expand
let fruits = [("Apple", 45.2), ("Banana", 38.1), ("Orange", 52.7)]
    chart = bars fruits defPlot { plotTitle = "Fruit Sales" }

scatter Source #

Arguments

:: [(Text, [(Double, Double)])]

List of named data series

-> Plot

Plot configuration

-> Text

Rendered chart as Text

Create a scatter plot from multiple data series.

Each series is rendered with a different color and pattern. Points are plotted using Braille characters for sub-character resolution.

Example

Expand
let points1 = [(x, x^2) | x <- [-3, -2.5 .. 3]]
    points2 = [(x, 2*x + 1) | x <- [-3, -2.5 .. 3]]
    chart = scatter [series "y = x²" points1,
                     series "y = 2x + 1" points2] defPlot

pie Source #

Arguments

:: [(Text, Double)]

List of (category, value) pairs

-> Plot

Plot configuration

-> Text

Rendered chart as Text

Create a pie chart showing proportions.

Values are normalized to sum to 100%. Negative values are treated as zero.

Example

Expand
let browsers = [("Chrome", 65), ("Firefox", 20), ("Safari", 10), ("Other", 5)]
    chart = pie browsers defPlot { plotTitle = "Browser Market Share" }

stackedBars Source #

Arguments

:: [(Text, [(Text, Double)])]

Categories with stacked components

-> Plot

Plot configuration

-> Text

Rendered chart as Text

Create a stacked bar chart.

Each category can have multiple stacked components.

Example

Expand
let sales = [("Q1", [("Product A", 100), ("Product B", 150)]),
             ("Q2", [("Product A", 120), ("Product B", 180)])]
    chart = stackedBars sales defPlot

heatmap Source #

Arguments

:: [[Double]]

2D matrix of values (rows × columns)

-> Plot

Plot configuration

-> Text

Rendered chart as Text

Create a heatmap visualization of a 2D matrix.

Values are mapped to a color gradient from blue (low) to red (high).

Example

Expand
let matrix = [[x * y | x <- [1..10]] | y <- [1..10]]
    chart = heatmap matrix defPlot { plotTitle = "Multiplication Table" }

lineGraph Source #

Arguments

:: [(Text, [(Double, Double)])]

List of named data series

-> Plot

Plot configuration

-> Text

Rendered chart as Text

Create a line graph connecting data points.

Similar to scatter but connects consecutive points with lines. Points are automatically sorted by x-coordinate before connecting.

Example

Expand
let sine = [(x, sin x) | x <- [0, 0.1 .. 2*pi]]
    cosine = [(x, cos x) | x <- [0, 0.1 .. 2*pi]]
    chart = lineGraph [series "sin" sine, series "cos" cosine] defPlot

boxPlot Source #

Arguments

:: [(Text, [Double])]

Named datasets

-> Plot

Plot configuration

-> Text

Rendered chart as Text

Create a box plot showing statistical distributions.

Displays quartiles, median, and min/max values for each dataset.

Example

Expand
let data1 = [1.2, 2.3, 2.1, 3.4, 2.8, 4.1, 3.9]
    data2 = [5.1, 4.8, 6.2, 5.9, 7.1, 6.5, 5.5]
    chart = boxPlot [("Group A", data1), ("Group B", data2)] defPlot

The box plot displays:

  • Box: First quartile (Q1) to third quartile (Q3)
  • Line inside box: Median (Q2)
  • Whiskers: Minimum and maximum values

Plotly-Express-style helpers

area :: [(Text, [(Double, Double)])] -> Plot -> Text Source #

Filled-area chart with the curve closed down to y=0.

ribbon :: [(Text, [(Double, Double, Double)])] -> Plot -> Text Source #

Filled band between (x, ymin, ymax) curves — e.g. CI envelopes.

density :: [(Text, [Double])] -> Plot -> Text Source #

Gaussian KDE per series (Silverman bandwidth).

errorBars :: [(Text, [(Double, Double, Double, Double)])] -> Plot -> Text Source #

Points with vertical error bars: (x, y, ymin, ymax) per row.

funnel :: [(Text, Double)] -> Plot -> Text Source #

Horizontal bars sized by their values.

polarLine :: [(Text, [(Double, Double)])] -> Plot -> Text Source #

Polar line chart; theta in radians CCW from +x.

waterfall :: [(Text, Double, Double)] -> Plot -> Text Source #

Waterfall chart: rows are (label, start, end).

distPlot :: [(Text, [Double])] -> Plot -> Text Source #

Histogram + KDE overlay per series.

gauss Source #

Arguments

:: [Double]

Population sample (used to compute μ and σ)

-> [(Text, Double)]

Named markers as (label, raw value); the largest z is highlighted

-> Plot

Plot configuration

-> Text

Rendered chart as Text

Gaussian / normal-distribution chart in standard-deviation (z-score) units.

Given a population sample, gauss computes the mean (μ) and standard deviation (σ), draws the kernel-density estimate of the distribution as a stippled bell curve, and annotates named markers at their z-score positions. The marker with the largest z-score is highlighted as the outlier.

This reproduces the "where does X sit on the curve?" style of chart: a smooth density curve filled with scattered dots, an x-axis labelled in σ units, and lollipop annotations dropping from each named value down to the axis.

Example

Expand
let -- goals + assists per 90 for every attacker in the league
    population = ...
    stars =
        [ (Lewandowski, 1.05)
        , (Mbappé,      1.02)
        , (Haaland,     1.10)
        , (Ronaldo,     1.12)
        , (Messi,       1.45)
        ]
chart = gauss population stars defPlot{plotTitle = "g + a per 90"}

Differential flame graphs

data FlameNode Source #

Constructors

FlameNode 

Fields

  • fnLabel :: Text

    Frame name.

  • fnPos :: Double

    Summed positive self-delta in this subtree (an increase), in bytes (tooltips format it as MB).

  • fnNeg :: Double

    Summed negative magnitude in this subtree (a decrease, >= 0), in bytes (tooltips format it as MB).

  • fnChildren :: [FlameNode]
     

Instances

Instances details
Show FlameNode Source # 
Instance details

Defined in Granite.Flame

Eq FlameNode Source # 
Instance details

Defined in Granite.Flame

data FlameOpts Source #

Constructors

FlameOpts 

Fields

Instances

Instances details
Show FlameOpts Source # 
Instance details

Defined in Granite.Flame

Eq FlameOpts Source # 
Instance details

Defined in Granite.Flame

flameDiff :: FlameNode -> FlameOpts -> Text Source #

Render a differential flame graph as a full standalone <svg> document. Rows go top→down by depth; a frame's width is proportional to its churn over the root churn; children are laid left→right sorted by descending churn; frames narrower than foMinPx are dropped and recursion stops past foMaxDepth.