tinytools-0.1.0.4: src/Potato/Flow/RenderCache.hs
{-# LANGUAGE RecordWildCards #-}
module Potato.Flow.RenderCache where
import Relude
import Potato.Flow.Math
import Potato.Flow.Methods.Types
import Potato.Flow.SElts
import Potato.Flow.Types
import Potato.Flow.Owl
import Potato.Flow.Methods.LineTypes
import qualified Data.IntMap as IM
import qualified Data.Text.IO as T
import qualified Data.Vector.Unboxed as V
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