{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Imj.Graphics.UI.Animation
(
UIEvolutions(..)
, mkUIAnimation
, UIAnimation(..)
, getDeltaTime
, getUIAnimationDeadline
, renderUIAnimation
, isFinished
, mkTextAnimRightAligned
) where
import Imj.Prelude
import Control.Monad.IO.Class(MonadIO)
import Control.Monad.Reader.Class(MonadReader)
import Imj.Geo.Discrete
import Imj.Graphics.Render
import Imj.Graphics.Text.Alignment
import Imj.Graphics.Text.Animation
import Imj.Graphics.Text.ColorString
import Imj.Graphics.UI.Colored
import Imj.Graphics.UI.RectContainer
import Imj.Timing
data UIAnimation = UIAnimation {
_uiAnimationEvs :: !UIEvolutions
, _uiAnimationDeadline :: !(Maybe KeyTime)
, _uiAnimationProgress :: !Iteration
} deriving(Show)
data UIEvolutions = UIEvolutions {
_uiEvolutionContainer :: !(Evolution (Colored RectContainer))
, _uiEvolutionsUpDown :: !(TextAnimation AnchorChars)
, _uiEvolutionLeft :: !(TextAnimation AnchorStrings)
} deriving(Show)
getUIAnimationDeadline :: UIAnimation -> Maybe KeyTime
getUIAnimationDeadline (UIAnimation _ mayDeadline _) =
mayDeadline
isFinished :: UIAnimation -> Bool
isFinished (UIAnimation _ Nothing _) = True
isFinished _ = False
{-# INLINABLE renderUIAnimation #-}
renderUIAnimation :: (Draw e, MonadReader e m, MonadIO m)
=> UIAnimation
-> m ()
renderUIAnimation (UIAnimation we@(UIEvolutions frameE upDown left) _ (Iteration _ frame)) = do
let (relFrameFrameE, relFrameUD, relFrameLeft) = getRelativeFrames we frame
drawMorphingAt frameE relFrameFrameE
renderAnimatedTextCharAnchored upDown relFrameUD
renderAnimatedTextStringAnchored left relFrameLeft
getDeltaTime :: UIEvolutions -> Frame -> Maybe Float
getDeltaTime we@(UIEvolutions frameE (TextAnimation _ _ (EaseClock upDown)) (TextAnimation _ _ (EaseClock left))) frame =
let (relFrameFrameE, relFrameUD, relFrameLeft) = getRelativeFrames we frame
in getDeltaTimeToNextFrame frameE relFrameFrameE
<|> getDeltaTimeToNextFrame upDown relFrameUD
<|> getDeltaTimeToNextFrame left relFrameLeft
getRelativeFrames :: UIEvolutions
-> Frame
-> (Frame, Frame, Frame)
getRelativeFrames
(UIEvolutions (Evolution _ lastFrameE _ _)
(TextAnimation _ _ (EaseClock (Evolution _ lastFrameUD _ _))) _) frame =
let relFrameRectFrameEvol = max 0 frame
relFrameUD = max 0 (relFrameRectFrameEvol - lastFrameE)
relFrameLeft = max 0 (relFrameUD - lastFrameUD)
in (relFrameRectFrameEvol, relFrameUD, relFrameLeft)
mkUIAnimation :: (Colored RectContainer, (([ColorString], [ColorString]), [[ColorString]]))
-> (Colored RectContainer, (([ColorString], [ColorString]), [[ColorString]]))
-> SystemTime
-> UIAnimation
mkUIAnimation (from@(Colored _ fromR), ((f1,f2),f3))
(to@(Colored _ toR), ((t1,t2),t3)) t =
UIAnimation evolutions deadline (Iteration (Speed 1) zeroFrame)
where
frameE = mkEvolutionEaseQuart (Successive [from, to]) 1
(ta1,ta2) = createUITextAnimations fromR toR (f1++t1, f2++t2, zipWith (++) f3 t3) 1
evolutions = UIEvolutions frameE ta1 ta2
deadline =
maybe
Nothing
(\dt -> Just $ KeyTime $ addToSystemTime (floatSecondsToDiffTime dt) t)
$ getDeltaTime evolutions zeroFrame
createUITextAnimations :: RectContainer
-> RectContainer
-> ([ColorString],[ColorString],[[ColorString]])
-> Float
-> (TextAnimation AnchorChars, TextAnimation AnchorStrings)
createUITextAnimations from to (ups, downs, lefts) duration =
let (centerUpFrom, centerDownFrom, leftMiddleFrom, _) = getSideCentersAtDistance from 3 2
(centerUpTo, centerDownTo, leftMiddleTo, _) = getSideCentersAtDistance to 3 2
ta1 = mkTextAnimCenteredUpDown (centerUpFrom, centerDownFrom) (centerUpTo, centerDownTo) (ups, downs) duration
ta2 = mkTextAnimRightAligned leftMiddleFrom leftMiddleTo lefts duration
in (ta1, ta2)
mkTextAnimRightAligned :: Coords Pos
-> Coords Pos
-> [[ColorString]]
-> Float
-> TextAnimation AnchorStrings
mkTextAnimRightAligned refFrom refTo listTxts duration =
let l = zipWith (\i txts ->
let firstTxt = head txts
lastTxt = last txts
rightAlign pos = move (2*i) Down . alignTxt (mkRightAlign pos)
fromAligned = rightAlign refFrom firstTxt
toAligned = rightAlign refTo lastTxt
in (txts, fromAligned, toAligned))
[0..] listTxts
in mkSequentialTextTranslationsStringAnchored l duration
mkTextAnimCenteredUpDown :: (Coords Pos, Coords Pos)
-> (Coords Pos, Coords Pos)
-> ([ColorString], [ColorString])
-> Float
-> TextAnimation AnchorChars
mkTextAnimCenteredUpDown (centerUpFrom, centerDownFrom) (centerUpTo, centerDownTo) (txtUppers, txtLowers)
duration =
let alignTxtCentered pos = alignTxt $ mkCentered pos
centerUpFromAligned = alignTxtCentered centerUpFrom (head txtUppers)
centerUpToAligned = alignTxtCentered centerUpTo (last txtUppers)
centerDownFromAligned = alignTxtCentered centerDownFrom (head txtLowers)
centerDownToAligned = alignTxtCentered centerDownTo (last txtLowers)
in mkSequentialTextTranslationsCharAnchored
[(txtUppers, centerUpFromAligned, centerUpToAligned),
(txtLowers, centerDownFromAligned, centerDownToAligned)]
duration
alignTxt :: Alignment -> ColorString -> Coords Pos
alignTxt (Alignment al pos) txt =
uncurry move (align al $ countChars txt) pos