packages feed

granite-0.6.0.0: src/Granite/Chart.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Strict #-}

{- |
Module      : Granite.Chart
Copyright   : (c) 2025
License     : MIT
Maintainer  : mschavinda@gmail.com

Convenience builders that construct a 'Chart' from the same kinds of
arguments accepted by the legacy chart functions in "Granite". Use
these when you want the declarative IR while still keeping ergonomic
construction:

@
import Granite.Chart
import qualified Data.Text.IO as T
import Granite.Render.Pipeline

main = do
  let ch = scatterChart
              [("A", [(0,0),(1,1),(2,4)])
              ,("B", [(0,1),(1,3),(2,6)])
              ] (Just "Random points")
  T.putStrLn (renderChartTerminal ch)
@
-}
module Granite.Chart (
    scatterChart,
    lineChart,
) where

import Data.Text (Text)

import Granite.Spec

{- | Build an IR scatter chart from N series, each with its own (x, y)
pairs. Each series becomes a separate 'GeomPoint' layer carrying its
own data frame and a group aesthetic that drives the legend.
-}
scatterChart :: [(Text, [(Double, Double)])] -> Maybe Text -> Chart
scatterChart sers title =
    emptyChart
        { chartTitle = title
        , chartLayers = map mkLayer sers
        }
  where
    mkLayer (name, pts) =
        let xs = map fst pts
            ys = map snd pts
            df = fromColumns [("x", ColNum xs), ("y", ColNum ys)]
         in (defLayer GeomPoint)
                { layerData = Just df
                , layerMapping =
                    emptyMapping
                        { aesX = Just (ColumnRef "x")
                        , aesY = Just (ColumnRef "y")
                        , aesGroup = Just (ColumnRef name)
                        }
                }

{- | Build an IR line chart from N series; analogous to 'scatterChart'
but with one 'GeomLine' layer per series.
-}
lineChart :: [(Text, [(Double, Double)])] -> Maybe Text -> Chart
lineChart sers title =
    emptyChart
        { chartTitle = title
        , chartLayers = map mkLayer sers
        }
  where
    mkLayer (name, pts) =
        let xs = map fst pts
            ys = map snd pts
            df = fromColumns [("x", ColNum xs), ("y", ColNum ys)]
         in (defLayer GeomLine)
                { layerData = Just df
                , layerMapping =
                    emptyMapping
                        { aesX = Just (ColumnRef "x")
                        , aesY = Just (ColumnRef "y")
                        , aesGroup = Just (ColumnRef name)
                        }
                }