module Reanimate.Animation where
import Control.Arrow ()
import Data.Fixed (mod')
import qualified Data.Map as M
import Graphics.SvgTree (Alignment (..), Document (..),
Number (..),
PreserveAspectRatio (..),
Tree (..), xmlOfTree)
import Graphics.SvgTree.Printer
import Reanimate.Constants
import Reanimate.Signal
import Reanimate.Svg.Constructors
import Text.XML.Light.Output
type Duration = Double
type Time = Double
type SVG = Tree
data Animation = Animation Duration (Time -> SVG)
mkAnimation :: Duration -> (Time -> SVG) -> Animation
mkAnimation = Animation
animate :: (Time -> SVG) -> Animation
animate = Animation 1
duration :: Animation -> Duration
duration (Animation d _) = d
seqA :: Animation -> Animation -> Animation
seqA (Animation d1 f1) (Animation d2 f2) =
Animation totalD $ \t ->
if t < d1/totalD
then f1 (t * totalD/d1)
else f2 ((t-d1/totalD) * totalD/d2)
where
totalD = d1+d2
parA :: Animation -> Animation -> Animation
parA (Animation d1 f1) (Animation d2 f2) =
Animation (max d1 d2) $ \t ->
let t1 = t * totalD/d1
t2 = t * totalD/d2 in
mkGroup
[ f1 (min 1 t1)
, f2 (min 1 t2) ]
where
totalD = max d1 d2
parLoopA :: Animation -> Animation -> Animation
parLoopA (Animation d1 f1) (Animation d2 f2) =
Animation totalD $ \t ->
let t1 = t * totalD/d1
t2 = t * totalD/d2 in
mkGroup
[ f1 (t1 `mod'` 1)
, f2 (t2 `mod'` 1) ]
where
totalD = max d1 d2
parDropA :: Animation -> Animation -> Animation
parDropA (Animation d1 f1) (Animation d2 f2) =
Animation totalD $ \t ->
let t1 = t * totalD/d1
t2 = t * totalD/d2 in
mkGroup
[ if t1>1 then None else f1 t1
, if t2>1 then None else f2 t2 ]
where
totalD = max d1 d2
pause :: Duration -> Animation
pause d = Animation d (const None)
andThen :: Animation -> Animation -> Animation
andThen a b = a `parA` (pause (duration a) `seqA` b)
frameAt :: Double -> Animation -> Tree
frameAt t (Animation d f) = f t'
where
t' = min 1 (max 0 (t/d))
renderTree :: Tree -> String
renderTree t = maybe "" ppElement $ xmlOfTree t
renderSvg :: Maybe Number -> Maybe Number -> Tree -> String
renderSvg w h t = ppDocument doc
where
width = 16
height = 9
doc = Document
{ _viewBox = Just (-width/2, -height/2, width, height)
, _width = w
, _height = h
, _elements = [withStrokeWidth defaultStrokeWidth $ scaleXY 1 (-1) t]
, _definitions = M.empty
, _description = ""
, _documentLocation = ""
, _documentAspectRatio = PreserveAspectRatio False AlignNone Nothing
}
mapA :: (Tree -> Tree) -> Animation -> Animation
mapA fn (Animation d f) = Animation d (fn . f)
pauseAtEnd :: Duration -> Animation -> Animation
pauseAtEnd t a = a `andThen` pause t
pauseAtBeginning :: Duration -> Animation -> Animation
pauseAtBeginning t a =
Animation t (freezeFrame 0 a) `seqA` a
pauseAround :: Duration -> Duration -> Animation -> Animation
pauseAround start end = pauseAtEnd end . pauseAtBeginning start
pauseUntil :: Duration -> Animation -> Animation
pauseUntil d a = pauseAtEnd (d-duration a) a
freezeFrame :: Double -> Animation -> (Time -> SVG)
freezeFrame t (Animation d f) = const $ f (t/d)
adjustDuration :: (Duration -> Duration) -> Animation -> Animation
adjustDuration fn (Animation d gen) =
Animation (fn d) gen
setDuration :: Duration -> Animation -> Animation
setDuration newD = adjustDuration (const newD)
reverseA :: Animation -> Animation
reverseA = signalA reverseS
playThenReverseA :: Animation -> Animation
playThenReverseA a = a `seqA` reverseA a
repeatA :: Double -> Animation -> Animation
repeatA n (Animation d f) = Animation (d*n) $ \t ->
f ((t*n) `mod'` 1)
freezeAtPercentage :: Time -> Animation -> Animation
freezeAtPercentage frac (Animation d genFrame) =
Animation d $ const $ genFrame frac
signalA :: Signal -> Animation -> Animation
signalA fn (Animation d gen) = Animation d $ gen . fn