packages feed

nano-ui-diagrams-0.1.0.0: lib/NanoUI/Plot/Series.hs

-- | Series constructors (line, scatter, bar, area, step) and their style
-- modifiers.
module NanoUI.Plot.Series
  ( line
  , scatter
  , bar
  , area
  , step
  , withColor
  , withStrokeWidth
  , withMarker
  , withBaseline
  , lineVec
  , scatterVec
  , areaVec
  , stepVec
  , barVec
  ) where

import Data.Text (Text)
import Data.Foldable (toList)
import Data.Primitive.PrimArray (generatePrimArray)
import Data.Primitive.SmallArray (createSmallArray, writeSmallArray)
import Data.Vector (Vector)
import Data.Vector qualified as V
import Data.Vector.Generic qualified as G
import Data.Vector.Unboxed qualified as U
import NanoUI (Color)
import NanoUI.Plot.Types
  ( MarkShape (..)
  , Series (..)
  , SeriesData (..)
  , SeriesKind (..)
  )

line :: Foldable f => Text -> f (Double, Double) -> Series
line name = lineVec name . U.fromList . toList

scatter :: Foldable f => Text -> f (Double, Double) -> Series
scatter name = scatterVec name . U.fromList . toList

bar :: Foldable f => Text -> f (Text, Double) -> Series
bar name = barVec name . V.fromList . toList

area :: Foldable f => Text -> f (Double, Double) -> Series
area name = areaVec name . U.fromList . toList

step :: Foldable f => Text -> f (Double, Double) -> Series
step name = stepVec name . U.fromList . toList

withColor :: Color -> Series -> Series
withColor c s = s {seriesColor = Just c}

withStrokeWidth :: Float -> Series -> Series
withStrokeWidth w s =
  case seriesKind s of
    LineSeries _ m -> s {seriesKind = LineSeries w m}
    ScatterSeries _ mk -> s {seriesKind = ScatterSeries w mk}
    BarSeries _ -> s {seriesKind = BarSeries w}
    StepSeries _ -> s {seriesKind = StepSeries w}
    _ -> s

withMarker :: MarkShape -> Series -> Series
withMarker mk s =
  case seriesKind s of
    LineSeries w _ -> s {seriesKind = LineSeries w (Just mk)}
    ScatterSeries w _ -> s {seriesKind = ScatterSeries w mk}
    _ -> s

withBaseline :: Double -> Series -> Series
withBaseline b s =
  case seriesKind s of
    AreaSeries _ -> s {seriesKind = AreaSeries b}
    _ -> s

-- | Numeric series retain unboxed coordinates. Unboxed inputs are shared;
-- boxed/storable inputs are converted once at the construction boundary.
{-# INLINE lineVec #-}
lineVec :: G.Vector v (Double, Double) => Text -> v (Double, Double) -> Series
lineVec name pts = Series name Nothing (LineSeries 1.5 Nothing) (PointsXY (G.convert pts))

{-# INLINE scatterVec #-}
scatterVec :: G.Vector v (Double, Double) => Text -> v (Double, Double) -> Series
scatterVec name pts = Series name Nothing (ScatterSeries 3 MarkCircle) (PointsXY (G.convert pts))

{-# INLINE areaVec #-}
areaVec :: G.Vector v (Double, Double) => Text -> v (Double, Double) -> Series
areaVec name pts = Series name Nothing (AreaSeries 0) (PointsXY (G.convert pts))

{-# INLINE stepVec #-}
stepVec :: G.Vector v (Double, Double) => Text -> v (Double, Double) -> Series
stepVec name pts = Series name Nothing (StepSeries 1.5) (PointsXY (G.convert pts))

barVec :: Text -> Vector (Text, Double) -> Series
barVec name rows =
  Series name Nothing (BarSeries 0.72) $
    CategoryY
      (createSmallArray n "" (\out -> V.imapM_ (\i (t, _) -> writeSmallArray out i t) rows))
      (generatePrimArray n (snd . V.unsafeIndex rows))
  where
    n = V.length rows