Docstoc

Fran

Document Sample
Fran Powered By Docstoc
					       Functional
       Reactive
       ANimation
Based on the work by Conal Elliott of Microsoft Research


     Mark Stobbe
   Alesya Sheremet
What is Fran?
• Haskell library for interactive animations with 2D and 3D graphics and sound
• Behaviors (animation) and events (reactive)
• DSEL designed for describing what an animation is (and not how to present it)
• Created by Conal Elliott at Microsoft Research, 1997
• Project has been suspended, last version works under Hugs98 (2002)
• Related work: Pan# and Yampa
Behaviors
One of the basic concepts in Fran:



               Behavior a :: Time -> a




                      Type is not restricted
                        to numbers only
Behavior Example
                                    Overloading lets us use
                                  these operators like we are
wiggle :: Behavior Double                  used to
wiggle = sin (pi * time)


ball :: ImageB
ball = stretch 0.2 circle         Place an image `over` the
                                            other

bball, rball :: ImageB
bball = moveXY 0 wiggle (withColor blue ball)
rball = moveXY wiggle 0 (withColor red ball)


main = display $ bball `over` rball
Delaying Behaviors
We can slow down animation without touching existing
code, simply by transforming time
main = display $ bball `over` later 0.5 rball

main = display $ bball `over` later (time/2.0) rball

main = display $ bball `over` later (wiggle/2.0) rball

 Animation 1          Animation 2           Animation 3
                  river… ->
Time flows like a overs ::foldl1 over ImageB
                  overs =
                           [ImageB]


delayImgs :: Behavior Double -> [ImageB] -> ImageB
delayImgs dt imgs = overs (zipWith later [0, dt ..] imgs)


trailWords :: Vector2B -> String -> ImageB
trailWords vec str = delayImgs 0.3 imgWords
       where imgWords = map (moveWord vec) (words str)


moveWord :: Vector2B -> String -> ImageB
moveWord vec word = move vec (stringIm word)


main :: IO ()
main = displayU $ \u -> trailWords (mouseMotion u) trailText
       where trailText = “Time flows like a river"
Reacting to Events
   Behaviors are continuous, but sometimes we should
    react to discrete events
   Conceptually, events are Maybe a-behaviors
   Implemented as a separate type


       newtype Event a = Event [(Time, Maybe a)]



   Event a is a stream of event occurrences associated with
    a time and value of type a
 Core of Fran’s Reactivity
     Event Transformers



newtype Event a = Event [(Time, Maybe a)]

(==>) :: Event a -> (a -> b) -> Event b
(-=>) :: Event a -> b -> Event b
untilB :: Behavior a -> Event (Behavior a) -> Behavior a

cycle u =
  withColor(cycle green yellow red u)
           (stretch (wiggleRange 0.5 1)
              circle
            )
    where
        cycle c1 c2 c3 u =
          c1 `untilB` lbp u ==>
          cycle c2 c3 c1
 Multiple Events
We can combine events, to wait for whichever happens first



(.|.) :: Event a -> Event a -> Event a




updown n u =
     n `untilB` ( lbp u ==> updown (n+1)
               .|. rbp u ==> updown (n-1)
                )
anim = displayU $ \u -> stretch (0.3*updown 3 u) circle
Snapshots
A snapshot captures the value of a behavior at an event occurrence.




snapshot     :: Event a -> Behavior b -> Event (a,b)
snapshot_    :: Event a -> Behavior b -> Event b
whenE        :: Event a -> BoolB -> Event a
  Switchers and Steppers
stepper :: a -> Event a -> Behavior a
switcher :: Behavior a -> Event (Behavior a) -> Behavior a


anim u = withColor c circle
     where
        c = switcher red (lbp u -=> blue .|. rbp u -=> red)



mouseEvs u = lbp u `snapshot_` mouseMotion u

anim u = withColor blue
       $ move (stepper 0 (mouseEvs u))
       $ stretch 0.25 circle
Demo: disappearing circles
circ :: User -> (ColorB,RealB,RealB) -> ImageB
circ u (c,d,s)
  = moveXY x y (stretch 0.2 (withColor c circle))
  where
    position :: Point2B
    position = point2XY x y
    x = wiggle
    y = (switcher (sin(pi*time*s - d)) disappear)

-- A ball disappears when the left button of the mouse is
-- pressed within 0.2 of the centre of the ball.

disappear :: Event RealB
disappear =
   whenSnap (lbp u) apart (\ x p -> (p <= 0.2)) -=> -2
     where apart = distance2 (mouse u) position
Demo: grab and follow
followMouse u p0 = (pos, closeEnough)
  where
   pos, lastRelease :: Point2B
   pos         = ifB grabbing (mouse u) lastRelease
   lastRelease = stepper p0 (release `snapshot_` pos)

   closeEnough, grabbing :: BoolB
   closeEnough = distance2 pos (mouse u) <* grabDistance
   grabbing    = stepper False
                   (grab -=> True .|. release -=> False)

   grab, release :: Event ()
   grab    = lbp u `whenE` closeEnough
   release = lbr u `whenE` grabbing

   grabDistance :: RealB
   grabDistance = 0.1
Predicate
                      predicate


           Behavior               Event

                      stepper




     predicate :: BoolB -> User -> Event()


     stepper :: a -> Event a -> Behavior a
Modelling a bouncing ball game

       We are going to model a very simple game:
                           Pong


But we limit our self to a basic variant:
• No randomization
• Only one player
Modelling a bouncing ball game
-- constants (all type Double)
playfield_size = 2.0      paddle_height = 0.1
ball_radius = 0.05        paddle_width = 0.3
ball_dx0 = 0.2            paddle_velocity = 1.0
ball_dy0 = 0.4


-- playfield, ball and paddle (all type ImageB)
playfield = withColor blue (stretch (constantB playfield_size) square)
ball = withColor yellow (stretch (constantB ball_radius) circle)
paddle =
  let paddle’ = withColor green (rect (constantB paddle_width)

                                      (constantB paddle_height))
  in   moveXY 0 (constantB (-(playfield_size / 2.0))) paddle’
Modelling a bouncing ball game (2)

 main :: IO ()
 main = displayU $ \u ->
     let (px,py) = movePaddle u
          (bx,by) = bounceOnPaddle ball_dx0 ball_dy0 px u
          paddle' = moveXY px py paddle
          ball'   = moveXY bx by ball
     in   paddle' `over` ball' `over` playfield
Modelling a bouncing ball game (3)
 movePaddle :: User -> (Behavior Double, Behavior Double)
 movePaddle u =
  let ux = stepper 0 (     rightKey u -=> paddle_velocity
                      .|. leftKey   u -=> -(paddle_velocity)
                      .|. anyKey    u -=> 0
                       )
       dx = condB (hitLeft &&* ux <* 0 ||* hitRight &&* ux >* 0) 0 ux
       -- test if we cross a border
       hitLeft    = x <* (constantB ((paddle_width/2) - playfield_size))
       hitRight   = x >* (constantB (playfield_size - (paddle_width/2)))
       -- return (x,y) integrated over time
       x = integral dx u
       y = constantB 0
  in   (x, y)
Modelling a bouncing ball game (4)
 bounceOnPaddle :: Double -> Double -> Behavior Double ->
                   User -> (Behavior Double, Behavior Double)
 bounceOnPaddle dx0 dy0 px u =
 let x = integral dx u
     y = integral dy u
     -- check if the ball is about to cross a border
     bLeft    = x <* (constantB (ball_radius-playfield_size))
     bRight   = x >* (constantB (playfield_size-ball_radius))
     bBottom = y <* (constantB (ball_radius-playfield_size+paddle_height))
     bTop     = y >* (constantB (playfield_size-ball_radius))
     outside = bLeft ||* bRight ||* bBottom ||* bTop
     -- bounce against border, if allowed
     dx = bounce dx0 dx doHBounce
     dy = bounce dy0 dy doVBounce
Modelling a bouncing ball game (5)



                                   Why not only keep the last border
    We can’t get out the corner!
                                        we bounced against?
Modelling a bouncing ball game (6)
  -- what was the last bounce direction (vertical or horizontal)
  lastBounceLR = switcher (negOrPos dx0) (      predicate bRight u -=> 1
                                         .|. predicate bLeft      u -=> (-1))
  lastBounceTB = switcher (negOrPos dy0) (      predicate bTop      u -=> 1
                                         .|. predicate bBottom u -=> (-1))
  -- is bouncing allowed (vertical or horizontal)
  doVBounce = predicate ((lastBounceTB ==* (-1) &&* bTop)
                      ||* (lastBounceTB ==* 1       &&* bBottom
                                                    &&* doPBounce)) u
  doHBounce = predicate ((lastBounceLR ==* (-1) &&* bRight)
                      ||* (lastBounceLR ==* 1       &&* bLeft)) u
  -- paddle in the right place?
  doPBounce = x <* (px + (constantB paddle_width / 2))
            &&* x >* (px - (constantB paddle_width / 2))
 in (x,y)
Modelling a bouncing ball game (7)
 -- calculate initial direction
 negOrPos :: Double -> Behavior Double
 negOrPos x = if (x < 0) then (constantB 1) else (constantB (-1))


 -- bounce the ball by negating the direction
 bounce :: Double -> Behavior Double -> Event a -> Behavior Double
 bounce dz0 dz doBounce = stepper dz0 (doBounce `snapshot_` dz ==> negate)




 Some problems:
 • imprecision
 • missing state (not in the example)
Conclusion
   Modeling approach to animation
   Events and Behaviors are first class values
   Behaviors are used for
       Describing what, not how, something should vary over time
       Not limited to numbers
   Event-oriented programming allows
       Remember events (snapshots and steppers)
       Enrich events with (-=>) and (.|.)
   Difficult to “get it right”
   Speed is ok, but successors like Yampa perform better

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:1
posted:8/5/2012
language:
pages:23