packages feed

granite-0.7.2.0: src/Granite/Render/Scene.hs

{-# LANGUAGE Strict #-}

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

Backend-agnostic IR. Coordinates are in logical pixels; the terminal
backend maps them to character cells via 'pxPerChar' / 'pxPerLine'
(Braille gives 2×4 sub-cell resolution).
-}
module Granite.Render.Scene (
    Point (..),
    Rect (..),
    Mark (..),
    Scene (..),
    Style (..),
    defaultStyle,
    TextStyle (..),
    defaultTextStyle,
    TextAnchor (..),
    pxPerChar,
    pxPerLine,
) where

import Data.Text (Text)
import Granite.Color (Color (..))

data Point = Point !Double !Double
    deriving (Eq, Show)

-- | Axis-aligned rectangle by top-left + size.
data Rect = Rect
    { rectX :: !Double
    , rectY :: !Double
    , rectW :: !Double
    , rectH :: !Double
    }
    deriving (Eq, Show)

-- | 'Nothing' on fill or stroke means "don't paint that side".
data Style = Style
    { styleFill :: !(Maybe Color)
    , styleStroke :: !(Maybe Color)
    , styleStrokeWidth :: !Double
    , styleFillOpacity :: !Double
    }
    deriving (Eq, Show)

defaultStyle :: Style
defaultStyle = Style Nothing Nothing 1 1

data TextAnchor = AnchorStart | AnchorMiddle | AnchorEnd
    deriving (Eq, Show)

data TextStyle = TextStyle
    { textFill :: !Color
    , textSize :: !Double
    , textAnchor :: !TextAnchor
    , textRotate :: !Double
    -- ^ Rotation in degrees about the anchor point; 0 = upright.
    , textTitle :: !(Maybe Text)
    -- ^ A @\<title\>@ tooltip (the untruncated label); 'Nothing' = none.
    }
    deriving (Eq, Show)

defaultTextStyle :: TextStyle
defaultTextStyle = TextStyle Default 11 AnchorStart 0 Nothing

{- | Primitive drawable marks. 'MArc' takes (center, radius, a0, a1)
in radians CCW from +x. 'MPolygon' is a closed filled polyline.
'MAxisLine' is an axis-aligned 1-px line that the terminal backend
writes as box-drawing chars (@│@ / @─@ / @┼@) for crisp axes;
everything else is rasterised through Braille.
-}
data Mark
    = MCircle !Point !Double !Style
    | MRect !Rect !Style
    | MPolyline ![Point] !Style
    | MPolygon ![Point] !Style
    | MPath !Text !Style
    | MText !Point !Text !TextStyle
    | MArc !Point !Double !Double !Double !Style
    | MAxisLine !Point !Point !Style
    | MGroup ![Mark]
    deriving (Eq, Show)

data Scene = Scene
    { sceneWidth :: !Double
    , sceneHeight :: !Double
    , sceneMarks :: ![Mark]
    }
    deriving (Eq, Show)

pxPerChar :: Double
pxPerChar = 10

pxPerLine :: Double
pxPerLine = 16