{-| Effects represent modifications applied to frames of the 'Animation'.
Effects can (and usually do) depend on time.
One or more effects can be applied over the entire duration of animation, or modified to affect
only a specific portion at the beginning \/ middle \/ end of the animation.
-}
module Reanimate.Effect
  ( -- * Primitive Effects
  Effect
  , fadeInE
  , fadeOutE
  , fadeLineInE
  , fadeLineOutE
  , fillInE
  , drawInE
  , drawOutE
  , translateE
  , scaleE
  , constE
  -- * Modifying Effects
  , overBeginning
  , overEnding
  , overInterval
  , reverseE
  , delayE
  , aroundCenterE
  -- * Applying Effects to Animations
  , applyE
  ) where

import           Graphics.SvgTree    (Tree)
import           Reanimate.Animation
import           Reanimate.Svg

-- | An Effect represents a modification of a SVG 'Tree' that can vary with time.
type Effect = Duration -- ^ Duration of the effect (in seconds)
           -> Time -- ^ Time elapsed from when the effect started (in seconds)
           -> Tree -- ^ Image to be modified
           -> Tree -- ^ Image after modification

-- | Modify the effect so that it only applies to the initial part of the animation.
overBeginning :: Duration -- ^ Duration of the initial segment of the animation over which the Effect should be applied
              -> Effect -- ^ The Effect to modify
              -> Effect -- ^ Effect which will only affect the initial segment of the animation
overBeginning :: Duration -> Effect -> Effect
overBeginning Duration
maxT Effect
effect Duration
_d Duration
t =
  if Duration
t Duration -> Duration -> Bool
forall a. Ord a => a -> a -> Bool
< Duration
maxT
    then Effect
effect Duration
maxT Duration
t
    else Tree -> Tree
forall a. a -> a
id

-- | Modify the effect so that it only applies to the ending part of the animation.
overEnding :: Duration -- ^ Duration of the ending segment of the animation over which the Effect should be applied
           -> Effect  -- ^ The Effect to modify
           -> Effect -- ^ Effect which will only affect the ending segment of the animation
overEnding :: Duration -> Effect -> Effect
overEnding Duration
minT Effect
effect Duration
d Duration
t =
  if Duration
t Duration -> Duration -> Bool
forall a. Ord a => a -> a -> Bool
>= Duration
blankDur
    then Effect
effect Duration
minT (Duration
tDuration -> Duration -> Duration
forall a. Num a => a -> a -> a
-Duration
blankDur)
    else Tree -> Tree
forall a. a -> a
id
  where
    blankDur :: Duration
blankDur = Duration
dDuration -> Duration -> Duration
forall a. Num a => a -> a -> a
-Duration
minT

-- | Modify the effect so that it only applies within given interval of animation's running time.
overInterval :: Time -- ^ time after start of animation when the effect should start
             -> Time -- ^ time after start of the animation when the effect should finish
             -> Effect  -- ^ The Effect to modify
             -> Effect -- ^ Effect which will only affect the specified interval within the animation
overInterval :: Duration -> Duration -> Effect -> Effect
overInterval Duration
start Duration
end Effect
effect Duration
_d Duration
t =
  if Duration
start Duration -> Duration -> Bool
forall a. Ord a => a -> a -> Bool
<= Duration
t Bool -> Bool -> Bool
&& Duration
t Duration -> Duration -> Bool
forall a. Ord a => a -> a -> Bool
<= Duration
end
    then Effect
effect Duration
dur ((Duration
t Duration -> Duration -> Duration
forall a. Num a => a -> a -> a
- Duration
start) Duration -> Duration -> Duration
forall a. Fractional a => a -> a -> a
/ Duration
dur)
    else Tree -> Tree
forall a. a -> a
id
  where
    dur :: Duration
dur = Duration
end Duration -> Duration -> Duration
forall a. Num a => a -> a -> a
- Duration
start

-- | @reverseE effect@ starts where the @effect@ ends and vice versa.
reverseE :: Effect -> Effect
reverseE :: Effect -> Effect
reverseE Effect
fn Duration
d Duration
t = Effect
fn Duration
d (Duration
dDuration -> Duration -> Duration
forall a. Num a => a -> a -> a
-Duration
t)

-- | Delay the effect so that it only starts after specified duration and then runs till the end of animation.
delayE :: Duration -> Effect -> Effect
delayE :: Duration -> Effect -> Effect
delayE Duration
delayT Effect
fn Duration
d = Duration -> Effect -> Effect
overEnding (Duration
dDuration -> Duration -> Duration
forall a. Num a => a -> a -> a
-Duration
delayT) Effect
fn Duration
d

-- | Modify the animation by applying the effect. If desired, you can apply multiple effects to single animation by calling this function multiple times.
applyE :: Effect -> Animation -> Animation
applyE :: Effect -> Animation -> Animation
applyE Effect
fn Animation
ani = let d :: Duration
d = Animation -> Duration
duration Animation
ani
                in Duration -> (Duration -> Tree) -> Animation
mkAnimation Duration
d ((Duration -> Tree) -> Animation)
-> (Duration -> Tree) -> Animation
forall a b. (a -> b) -> a -> b
$ \Duration
t -> Effect
fn Duration
d (Duration
dDuration -> Duration -> Duration
forall a. Num a => a -> a -> a
*Duration
t) (Tree -> Tree) -> Tree -> Tree
forall a b. (a -> b) -> a -> b
$ Duration -> Animation -> Tree
frameAt (Duration
dDuration -> Duration -> Duration
forall a. Num a => a -> a -> a
*Duration
t) Animation
ani

-- | Build an effect from an image-modifying function. This effect does not change as time passes.
constE :: (Tree -> Tree) -> Effect
constE :: (Tree -> Tree) -> Effect
constE Tree -> Tree
fn Duration
_d Duration
_t = Tree -> Tree
fn

-- | Change image opacity from 0 to 1.
fadeInE :: Effect
fadeInE :: Effect
fadeInE Duration
d Duration
t = Duration -> Tree -> Tree
withGroupOpacity (Duration
tDuration -> Duration -> Duration
forall a. Fractional a => a -> a -> a
/Duration
d)

-- | Change image opacity from 1 to 0. Reverse of 'fadeInE'.
fadeOutE :: Effect
fadeOutE :: Effect
fadeOutE = Effect -> Effect
reverseE Effect
fadeInE

-- | Change stroke width from 0 to given value.
fadeLineInE :: Double -> Effect
fadeLineInE :: Duration -> Effect
fadeLineInE Duration
w Duration
d Duration
t = Duration -> Tree -> Tree
withStrokeWidth (Duration
wDuration -> Duration -> Duration
forall a. Num a => a -> a -> a
*(Duration
tDuration -> Duration -> Duration
forall a. Fractional a => a -> a -> a
/Duration
d))

-- | Change stroke width from given value to 0. Reverse of 'fadeLineInE'.
fadeLineOutE :: Double -> Effect
fadeLineOutE :: Duration -> Effect
fadeLineOutE = Effect -> Effect
reverseE (Effect -> Effect) -> (Duration -> Effect) -> Duration -> Effect
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Duration -> Effect
fadeLineInE

-- | Effect of progressively drawing the image. Note that this will only affect primitive shapes (see 'pathify').
drawInE :: Effect
drawInE :: Effect
drawInE Duration
d Duration
t = Duration -> Tree -> Tree
withFillOpacity Duration
0 (Tree -> Tree) -> (Tree -> Tree) -> Tree -> Tree
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Duration -> Tree -> Tree
partialSvg (Duration
tDuration -> Duration -> Duration
forall a. Fractional a => a -> a -> a
/Duration
d) (Tree -> Tree) -> (Tree -> Tree) -> Tree -> Tree
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Tree -> Tree
pathify

-- | Reverse of 'drawInE'.
drawOutE :: Effect
drawOutE :: Effect
drawOutE = Effect -> Effect
reverseE Effect
drawInE

-- | Change fill opacity from 0 to 1.
fillInE :: Effect
fillInE :: Effect
fillInE Duration
d Duration
t = Duration -> Tree -> Tree
withFillOpacity Duration
f
  where
    f :: Duration
f = Duration
tDuration -> Duration -> Duration
forall a. Fractional a => a -> a -> a
/Duration
d

-- | Change scale from 1 to given value.
scaleE :: Double -> Effect
scaleE :: Duration -> Effect
scaleE Duration
target Duration
d Duration
t = Duration -> Tree -> Tree
scale (Duration
1 Duration -> Duration -> Duration
forall a. Num a => a -> a -> a
+ (Duration
targetDuration -> Duration -> Duration
forall a. Num a => a -> a -> a
-Duration
1) Duration -> Duration -> Duration
forall a. Num a => a -> a -> a
* Duration
tDuration -> Duration -> Duration
forall a. Fractional a => a -> a -> a
/Duration
d)

-- | Move the image from its current position to the target x y coordinates.
translateE :: Double -> Double -> Effect
translateE :: Duration -> Duration -> Effect
translateE Duration
x Duration
y Duration
d Duration
t = Effect
translate (Duration
x Duration -> Duration -> Duration
forall a. Num a => a -> a -> a
* Duration
tDuration -> Duration -> Duration
forall a. Fractional a => a -> a -> a
/Duration
d) (Duration
y Duration -> Duration -> Duration
forall a. Num a => a -> a -> a
* Duration
tDuration -> Duration -> Duration
forall a. Fractional a => a -> a -> a
/Duration
d)

-- | Transform the effect so that the image passed to the effect's image-modifying
-- function has coordinates (0, 0) shifted to the center of its bounding box.
-- Also see 'aroundCenter'.
aroundCenterE :: Effect -> Effect
aroundCenterE :: Effect -> Effect
aroundCenterE Effect
e Duration
d Duration
t = (Tree -> Tree) -> Tree -> Tree
aroundCenter (Effect
e Duration
d Duration
t)