23
Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Embed Size (px)

Citation preview

Page 1: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Functional Reactive ANimation

Mark StobbeAlesya Sheremet

Based on the work by Conal Elliott of Microsoft Research

Page 2: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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

Page 3: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Behaviors

One of the basic concepts in Fran:

Behavior a :: Time -> a

Type is not restricted to numbers only

Page 4: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Behavior Example

wiggle :: Behavior Double

wiggle = sin (pi * time)

ball :: ImageB

ball = stretch 0.2 circle

bball, rball :: ImageB

bball = moveXY 0 wiggle (withColor blue ball)

rball = moveXY wiggle 0 (withColor red ball)

main = display $ bball `over` rball

Place an image `over` the other

Overloading lets us use these operators like we are

used to

Page 5: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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

Page 6: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Time flows like a river…

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"

overs :: [ImageB] -> ImageBovers = foldl1 over

Page 7: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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

Event a is a stream of event occurrences associated with a time and value of type a

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

Page 8: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Core of Fran’s Reactivity

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

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

Event Transformers

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

Page 9: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Multiple Events

(.|.) :: 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

We can combine events, to wait for whichever happens first

Page 10: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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 bwhenE :: Event a -> BoolB -> Event a

Page 11: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Switchers and Steppers

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

stepper :: a -> Event a -> Behavior aswitcher :: Behavior a -> Event (Behavior a) -> Behavior a

Page 12: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Demo: disappearing circles

circ :: User -> (ColorB,RealB,RealB) -> ImageBcirc 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 RealBdisappear = whenSnap (lbp u) apart (\ x p -> (p <= 0.2)) -=> -2 where apart = distance2 (mouse u) position

Page 13: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Demo: grab and followfollowMouse u p0 = (pos, closeEnough) where

pos, lastRelease :: Point2Bpos = ifB grabbing (mouse u) lastReleaselastRelease = stepper p0 (release `snapshot_` pos)

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

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

grabDistance :: RealBgrabDistance = 0.1

Page 14: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Predicate

Behavior Event

predicate

stepper

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

stepper :: a -> Event a -> Behavior a

Page 15: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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

Page 16: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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’

Page 17: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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

Page 18: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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)

Page 19: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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

Page 20: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

Modelling a bouncing ball game (5)

Why not only keep the last border we bounced against?

We can’t get out the corner!

Page 21: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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)

Page 22: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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)

Page 23: Functional Reactive ANimation Mark Stobbe Alesya Sheremet Based on the work by Conal Elliott of Microsoft Research

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