packages feed

tinytools-0.1.0.0: src/Potato/Flow/RenderCache.hs

{-# LANGUAGE RecordWildCards #-}

module Potato.Flow.RenderCache where

import           Relude

import Potato.Data.Text.Unicode
import           Potato.Flow.Math
import           Potato.Flow.Methods.Types
import           Potato.Flow.SElts
import Potato.Flow.Types
import           Potato.Flow.OwlItem
import Potato.Flow.Owl
import           Potato.Flow.Controller.Types
import           Potato.Flow.Methods.LineTypes


import qualified Data.IntMap             as IM
import qualified Data.Text               as T
import qualified Data.Text.IO as T
import qualified Data.Vector.Unboxed     as V
import qualified Data.Sequence as Seq
import Control.Exception (assert)

-- TODO move these methods to Math
-- | input index must be contained in the box
toPoint :: LBox -> Int -> XY
toPoint (LBox (V2 x y) (V2 w _)) i = V2 (i `mod` w + x) (i `div` w + y)

-- | input XY point must be contained in the box
toIndex :: LBox -> XY -> Int
toIndex (LBox (V2 x y) (V2 w _)) (V2 px py) = (py-y)*w+(px-x)

-- | same as above but does bounds checking
toIndexSafe :: LBox -> XY -> Maybe Int
toIndexSafe lbx xy = if does_lBox_contains_XY lbx xy
  then Just $ toIndex lbx xy
  else Nothing



-- | WidePChar represents part of a PChar that
-- the Int8 parameter is offset from where the PChar originates from, so for example
-- '😱' <- PChar
--  01 <- Int8 offset parameter
--
-- -1 value for offset means there is no character in the space, the PChar value is ignored in this case
type MWidePChar = (Int8, PChar)

emptyMWidePChar :: MWidePChar 
emptyMWidePChar = (-1, ' ') -- ' ' is a dummy character needed to pad the unboxed vector

-- TODO consider making sparse variant
-- | the LBox may exceed the logical bounding box of the object that is being represented if that object contains wide chars
data PreRender = PreRender (V.Vector (MWidePChar)) LBox deriving (Show)

emptyPreRender :: PreRender
emptyPreRender = PreRender V.empty (LBox 0 0)

preRender_lookup :: (HasCallStack) => PreRender -> XY -> MWidePChar
preRender_lookup (PreRender v lbox) pos = r where
  -- we still have to do this check here since toIndex expects point to be contained in box
  r = if does_lBox_contains_XY lbox pos 
    then case v V.!? (toIndex lbox pos) of
      Nothing -> assert False emptyMWidePChar
      Just x -> x
    else emptyMWidePChar


-- NOTE OwlIteCache is intended to be used at several stages in the event loop
-- it can be used in Handlers, it can be used when generating SEltDrawers and it can be used for rendering itself
data OwlItemCache =
  -- TODO change to LineAnchorsForRenderList prob
  OwlItemCache_Line LineAnchorsForRender PreRender
  | OwlItemCache_Generic PreRender deriving (Show)

owlItemCache_preRender :: OwlItemCache -> Maybe PreRender
owlItemCache_preRender = \case
  OwlItemCache_Line _ x -> Just x
  OwlItemCache_Generic x -> Just x

newtype RenderCache = RenderCache {
    -- map for REltId to cache for each owl
    unRenderCache :: REltIdMap OwlItemCache
  } deriving (Show)

emptyRenderCache :: RenderCache
emptyRenderCache = RenderCache IM.empty

renderCache_clearAtKeys :: RenderCache -> [REltId] -> RenderCache
renderCache_clearAtKeys rcache rids = RenderCache $ foldr IM.delete (unRenderCache rcache) rids

renderCache_lookup :: RenderCache -> REltId -> Maybe OwlItemCache
renderCache_lookup rcache rid = IM.lookup rid (unRenderCache rcache)


-- UNTESTED
makePreRender :: forall a. (HasOwlTree a) => a -> SEltDrawer -> PreRender
makePreRender ot SEltDrawer {..} = r where
  
  lbox' = _sEltDrawer_box ot 
  lbox@(LBox _ (V2 w _)) = lBox_expand lbox' (0, _sEltDrawer_maxCharWidth-1, 0, 0)
  area = lBox_area lbox

  -- the (Int8, Int8, PChar) is (distance from prev wide pchar, width of prev wide pchar, wide pchar)
  -- width of prev wide pchar could be determined from wide pchar of course but may as well cache it
  unfoldrfn :: (Int, Maybe (Int8, Int8, PChar)) -> Maybe (MWidePChar, (Int, Maybe (Int8, Int8, PChar)))
  unfoldrfn (i, mprevwidechar) = r2 where
    pt = toPoint lbox i
    mchar = _sEltDrawer_renderFn ot pt
    mcharwithwidth = fmap (\pch -> (pch, getPCharWidth pch)) mchar
    eol = (i+1) `mod` w == 0

    moutputchar = case mprevwidechar of
      Nothing        -> case mchar of  
        Just pch -> (0, pch)
        Nothing -> emptyMWidePChar 
      Just (a,_,pch) -> (a, pch)

    mnextwidechar = if eol 
      then Nothing 
      else case mprevwidechar of 
        Nothing -> case mcharwithwidth of 
          Nothing -> Nothing
          Just (pch, width) -> if width > 1
            then Just (1,width,pch)
            else Nothing
        Just (a,b,_) | a == b-1 -> Nothing
        Just (a,b,pch) -> Just (a+1, b, pch)

    r2 = if i == area 
      then Nothing
      else Just (moutputchar, (i+1, mnextwidechar))

  r = PreRender (V.unfoldr unfoldrfn (0,Nothing)) lbox