{-# 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