module Data.Animate
( Seconds
, DeltaSeconds
, Frame(..)
, Animations
, animations
, framesByAnimation
, Loop(..)
, Position(..)
, FrameStep(..)
, stepFrame
, stepAnimation
, isAnimationComplete
, positionHasLooped
) where
import qualified Data.Vector as V (Vector, (!), length, fromList)
type Seconds = Float
type DeltaSeconds = Seconds
data Frame loc = Frame
{ _fLocation :: loc
, _fDelay :: Seconds
} deriving (Show, Eq)
newtype Animations a loc = Animations { unAnimations :: V.Vector (V.Vector (Frame loc)) }
deriving (Show, Eq)
animations :: (Enum a, Bounded a) => (a -> [Frame loc]) -> Animations a loc
animations getFrames = Animations $ V.fromList $ map (V.fromList . getFrames) [minBound..maxBound]
framesByAnimation :: Enum a => Animations a loc -> a -> V.Vector (Frame loc)
framesByAnimation (Animations as) a = as V.! fromEnum a
data Loop
= LoopForever
| LoopCount Int
deriving (Show, Eq)
data Position a = Position
{ _pAnimation :: a
, _pFrameIndex :: Int
, _pCounter :: Seconds
, _pLoop :: Loop
} deriving (Show, Eq)
data FrameStep
= FrameStepCounter Seconds
| FrameStepDelta DeltaSeconds
deriving (Show, Eq)
stepFrame :: Frame loc -> Position a -> DeltaSeconds -> FrameStep
stepFrame Frame{_fDelay} Position{_pCounter} delta =
if _pCounter + delta >= _fDelay
then FrameStepDelta $ _pCounter + delta _fDelay
else FrameStepCounter $ _pCounter + delta
stepAnimation :: Enum a => Animations a loc -> Position a -> DeltaSeconds -> Position a
stepAnimation as p d =
case frameStep of
FrameStepCounter counter -> p{_pCounter = counter }
FrameStepDelta delta -> stepAnimation as p' delta
where
frameStep = stepFrame f p d
fs = unAnimations as V.! fromEnum (_pAnimation p)
f = fs V.! _pFrameIndex p
p'= case _pLoop p of
LoopForever -> p{_pFrameIndex = (_pFrameIndex p + 1) `mod` V.length fs, _pCounter = 0}
LoopCount n -> let
index = (_pFrameIndex p + 1) `mod` V.length fs
n' = if index == 0 then n 1 else n
in p
{ _pFrameIndex = if n' < 0 then _pFrameIndex p else index
, _pCounter = 0
, _pLoop = LoopCount n' }
isAnimationComplete :: Enum a => Animations a loc -> Position a -> Bool
isAnimationComplete as p = case _pLoop p of
LoopForever -> False
LoopCount n -> n < 0 && _pFrameIndex p == lastIndex && _pCounter p >= _fDelay lastFrame
where
frames = framesByAnimation as (_pAnimation p)
lastIndex = V.length frames 1
lastFrame = frames V.! lastIndex
positionHasLooped
:: Position a
-> Position a
-> Bool
positionHasLooped Position{ _pLoop = LoopCount c } Position{ _pLoop = LoopCount c' } = c > c'
positionHasLooped Position{ _pLoop = LoopForever } _ = False
positionHasLooped _ Position{ _pLoop = LoopForever } = False