Fran Users Manual
John Peterson
Conal Elliott
Gary Shu Ling

1  Introduction

Fran (Function Reactive Animation) is a collection of data types and functions for composing interactive multimedia animations. It is implemented in Haskell and runs under the Hugs and ghc Haskell systems.

The Fran project has been carried out jointly by Microsoft Research and other Haskell researchers. Currently Fran runs under the Microsoft Windows '95/NT systems. This is research in progress; it is very likely that Fran will continue to change in the near future. We have tested all of the examples distributed with Fran but there are sure to be bugs in the current system. Please report any problems to fran-bugs@haskell.org. This document is associated with version 1.04 of Fran, distributed with Hugs 1.4. Newer versions of Fran will appear at http://www.research.microsoft.com/~conal/Fran. Information about the this version of Fran, including manuals and more animations, is at http://haskell.org/fran.

This manual contains a short introduction to Fran and an overview of the pre-defined types and functions available in Fran. A more detailed Fran tutorial is also included in the file tutorial.lhs.

All of the examples used in this manual are found in the hugs/lib/fran/demos/examples.hs. If you are unfamiliar with Fran, the best way to use this manual is to open (double click) examples.hs in the hugs/lib/fran/demos directory. An individual example can be executed using the run function: run n executes the nth example. The animation window may initially be hidden when you run an animation -- just click it on the task bar to make it visible. Terminate the animation by closing the animation window. Exit Hugs using :q when you are done. If you encounter a program error while an animation is running you may need to exit and restart Hugs. Running main displays all of the examples in sequence. Use the arrow keys to step through the examples.

Fran is based on two concepts: behaviors and events. A behavior is a value that varies over time; an event is an occurance at a specific time that generates a value. The interplay between behaviors and events is the essence of Fran. While this implementation of Fran is used for animations, the same model serves for other reactive systems. Indeed, this implementation cleanly separates the core behavior -- event interaction and the graphics library layered on top of it.

2  Behaviors

In Fran, a value of type Behavior T is a time-varying value of type T. Behaviors are similar to functions over time: operationally, the type Behavior T is the same as Time -> T in that it maps time values onto values of type T. For example, this behavior oscillates between -1 and 1:

wiggle :: Behavior Double
wiggle = sin (pi * time)

This definition of wiggle uses a pre-defined behavior: time. The type of time is Behavior Double (or TimeB, as defined by a synonym) and its value is the current time in seconds. Thus the wiggle behavior cycles from -1 to 1 with a period of 2 seconds. Here the sin and (*) functions are applied to behaviors instead of ordinary numeric values. This is possible since Fran defines an instance of the Behavior type for many built-in classes; in this case the Num and Floating classes are overloaded in the type Behavior. Much more will be said of this later.

Since behaviors change over time, a behavior is observed by playing it in some manner. That is, the user watches and listens to an object as it changes and reacts to input. Fran includes functions for constructing animations that are played in a graphics window. Full details of this library are presented later; here we will introduce just enough of it so that we can explore events and behaviors. Here is a small subset of the graphics library:

-- Basic data types
data   Color                         -- data type of colors
type   RealVal = Double
type   Time    = Double 
data   Point2  =                      -- a 2D point
data   Vector2 =                      -- a 2D vector
data   ImageB                         -- Reactive images

-- synonyms that abbreviate common behavioral types
type   RealB    = Behavior RealVal
type   ColorB   = Behavior Color
type   Point2B  = Behavior Point2
type   Vector2B = Behavior Point2
type   TimeB    = Behavior Time

-- Graphics operations 
-- A behavioral point constructor
point2XY   :: RealB -> RealB -> Point2B   -- Construct a 2D point
vector2XY  :: RealB -> RealB -> Vector2B  -- Construct a 2D vector
origin2    :: RealB                       -- The origin (maps to screen center)
circle     :: ImageB                      -- A circle at (0,0) with unit radius
withColor  :: ColorB -> ImageB -> ImageB  -- Paint with a solid color
move       :: Vector2B -> ImageB -> ImageB -- Move an image 
red, blue,green  :: ColorB                -- Some built-in colors
over       :: ImageB -> ImageB -> ImageB  -- Place one image over another
bigger     :: RealB -> ImageB -> ImageB   -- Enlarge (or reduce) the
                                          -- size of an image
-- Display routine.  Initial screen scaled to (-1,-1) , (1,1)
disp   :: (User -> ImageB) -> IO ()  -- display an image behavior

To avoid clutter in type signatures involving Behavior many types have pre-defined synonyms for their behavioral counterparts. The type declarations above show some of these synonyms. Some behavioral types, such as ImageB, are implemented directly instead of using the Behavior type constructor.

The disp function takes a reactive animation and plays it in a graphics window. Fran uses the type User to represent external events generated by the user. Images which don't react user input can usually ignore the User value that disp passes to the animation.

Here are a few of the built-in behaviors in Fran:

time        :: TimeB
constantB   :: a -> Behavior a     --  create a constant behavior
mouseMotion :: User -> Vector2B    --  tracks the position of the mouse

Here is a very simple program to display a pulsing circle:

module Examples where

import Fran    -- Basic Fran functionality

circ :: ImageB
circ = bigger (sin time) (withColor red circle)

example1 u = circ

Execute this example using either disp example1 or run 1, for short. The Fran module is found on the standard Hugs search path.

Here is a slightly more complex behavior:

ball1, ball2, movingBall1, movingBall2 :: ImageB
ball1         = bigger 0.3 (withColor red circle)
movingBall1   = move (vector2XY 0 wiggle) ball1
ball2         = bigger 0.4 (withColor blue circle)
movingBall2   = move (vector2XY wiggle 0) ball2

example2 u    = movingBall1 `over` movingBall2

Some behaviors are generated by user interaction. For example, the mouse motion is represented by the following behavior:

mouseMotion   ::  User -> Vector2B

As mouse motion is part of the user input, the User value passed into the animation by the disp function must then be passed on to mouseMotion. This program displays a ball which follows the mouse:

example3 u = move (mouseMotion u) ball1

3  Events

Behaviors are continuous, defined over a continuous range of time values. Events, in contrast, are instantaneous: an event occurs at a specific time. Each event occurance produces a value; the type Event T denotes an event that generates a value of type T when it happens. Events that do not generate interesting values have type Event (). Events in Fran are concrete values rather than ephemeral happenings and may be treated as any other data object.

Events are grouped into event streams. Every occurance of an event yields both a value and a new event containing the remainder of the stream. Thus a value of type Event T denotes a series of events, each generating a value of type T. The type Event T can be understood as [(Time,T)]: a (sorted) list of occurances containing the time and event value for each occurance. For example, this list represents a possible sequence of keyboard events:

[(1,'a'), (3,'b'), (7,'c')]

The residual stream of events yielded by an event occurance is the aged event. In the above example, after the first key event is detected at time 1, the aged event stream becomes:

[(3,'b'), (7,'c')]

The next keypress event at time 3 must be obtained from the aged event stream.

A Fran program reacts to external events; each kind of external event is represented by constructor in the data type UserAction. The stream of incoming events has the type Event UserAction. This synonym:

type User = Event UserAction

gives a shorter name to the program input. Specific kinds of events, such as `resize window' or `keyboard press' are extracted from the User type. For example, these events are associated with the mouse buttons:

lbpU,rbpU    :: User -> Event (User)        -- Mouse button presses

These events yield a new User value which holds the aged user event stream. (The lbpU and rbpU functions are not formally a part of Fran but can be defined trivially from built in Fran functions.)

Here are some other basic events:

neverE      :: Event a                   
constE      :: Time -> a -> Event a      
timeIs      :: Time -> Event ()          
timeIs t    =  constE () t 
alarmE      :: Time -> Time -> Event ()

The neverE event never happens. The constE and timeIs events have a given occurence time; when aged they become neverE. The alarmE event goes off at regular intervals: the arguments are the start time and the time between events.

4  Reactive Behaviors

Events are used to build reactive behaviors which change course in response to events. Reactive behaviors are defined using the untilB function:

untilB :: GBehavior bv => bv -> Event bv -> bv

The class GBehavior defines reactive data types. Some reactive types, such as RealB or ColorB are formed by applying the Behavior type constructor to an existing type. Other types, such as ImageB, have `built-in' reactivity. The untilB function changes the course of a behavior when an event occurs; the event occurance generates the new behavior to be followed after it happens.

Before we can use untilB in an example, we need to transform an event such as lbpU of type Event User into an event which generates a behavior. This function transforms an event:

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

Note the similarity between (==>) and the map function. Using (==>), we can now write a simple reactive behavior:

example4 u = withColor (doRed u) circle where
  doRed, doBlue :: User -> ColorB
  doRed  u = red  `untilB` (lbpU u ==> doBlue)
  doBlue u = blue `untilB` (lbpU u ==> doRed)

The circle changes between red and blue with each left button press. The parenthesis around the ==> expressions have been added for clarity; they are not needed since `untilB` has a lower fixity than ==> . The (==>) operator passes the new User value generated by lbp on to the next cycle.

5  Using Events and Behaviors

Fran contains a rich library of functions involving events and behaviors.

5.1  Event Transformations and Utilities

The choice operator combines two event streams:

(.|.)   :: Event a -> Event a -> Event a
anyE    :: [Event a ->] -> Event a
anyE    =  foldr (.|.) neverE

For example, the definition

click u = lbpU u .|. rbpU u

defines click to be a stream of either left or right mouse button events. In the following example, the circle may turn either red or blue, depending on which mouse button is pressed first:

example5 u = withColor (c red u) circle where
  c :: ColorB -> User -> ColorB
  c cl u = cl `untilB` ((lbpU u ==> c blue)
                        .|. 
                        (rbpU u ==> c green))

Again, the parenthesis are not really needed. Note the use of currying to pass the updated User value generated by lbpU or rbpU to the next iteration of the loop.

The (==>) operator is a special case of a more general event handler, handleE. Using handleE, the event time, event value, and next event in the event stream are all revealed.

handleE       :: Event a -> (Time -> a -> Event a -> b) -> Event b
(==>)         :: Event a -> (a -> b) -> Event b
e ==> f       =  e `handleE` (\_ x _ -> f x)
(-=>)         :: Event a -> b -> Event b
e -=> v       =  e ==> const v
withRestE     :: Event a -> Event (a, Event a)
withRestE e   =  e `handleE` (\_ v e' -> (v,e'))
withTimeE     :: Event a -> Event (a, Time)
withTimeE e   =  e `handleE` (\t v _ -> (v,t))
nextE         :: Event a -> Event (Event a)
nextE e       =  e `handleE` \ te x e' -> e'

These functions associate the events in an event stream with values in a list:

withElemE        :: Event a -> [b] -> Event (a,b)
withElemE_       :: Event a -> [b] -> Event b
e `withElemE_` l =  (e `withElemE` l) ==> snd

Finally, these utilities convert event streams into behaviors:

-- Assemble a behavior piecewise from a series of events
switcher :: GBehavior bv => bv -> Event bv -> bv
switcher b0 e = b0 `untilB` (e `handleE` (\_ e' b' -> switcher b' e'))

-- A switcher for constant behaviors

stepper :: a -> Event a -> Behavior a
stepper x0 e = switcher (constantB x0) (e ==> constantB)

This example uses withElemE_ to map the left button presses onto a series of numbers then uses stepper to convert the event series into a behavior.

example6 u = move (vector2XY l l) ball
  where
     ball = bigger 0.3 (withColor red circle)
     l = stepper 0 (lbpCounter u)
     lbpCounter :: User -> Event (RealVal)
     lbpCounter u = withElemE_ (lbpU u) [0.1, 0.2 ..]

These functions filter a selected set of events out of an event stream:

filterE           :: Event a -> (a -> Maybe b) -> Event b
suchThat          :: Event a -> (a -> Bool) -> Event a
suchThat ev pred  =  filterE ev (\a -> if pred a then Just a else Nothing) 

For example, the event which recognizes a particular character is:

keyPress  ::    VKey -> Event -> Event User
keyPress k u = keyPressAny u `filterE` (\(k',u') ->
                                         if k == k' then Just u' else Nothing

5.2  User Interaction

These events and behaviors are derived from user input.

-- mouse button press and release events
lbp, rbp, lbr, rbr :: User -> Event ()
-- keyboard stuff.  VKey is defined in the Win32 module
keyPressAny        :: User -> Event VKey
keyPress           :: VKey -> User -> Event ()
keyReleaseAny      :: User -> Event VKey
keyRelease         :: VKey -> User -> Event ()
-- These use Char instead of VKey
asciiKey           :: User -> Event Char
oneAsciiKey        :: Char -> User -> Event ()
-- Returns size of resized window
resize             :: User -> Event Vector2
viewSize           :: User -> Vector2B
-- Mouse motion
mouseMove          :: User -> Event Point2
mouse              :: User -> Point2B
mouseMotion        :: User -> Vector2B
-- Allows synchronization with display events
updateDone         :: User -> Event Time
updatePeriod       :: User -> Behavior Double
-- Time since User was last aged
userTime           :: User -> TimeB
userTimeIs         :: Time -> User -> Event ()

5.3  Integration

The integral function integrates numeric behaviors over time. Both reals and vectors can be integrated. The type of integral is:

integral :: (VectorSpace v) => Behavior v -> User -> Behavior v
atRate   :: (VectorSpace v) => Behavior v -> User -> Behavior v

Function atRate is another name for integral. The types in VectorSpace include Real, Vector2, and Vector3. The Point2 and Point3 types cannot be integrated directly -- they must be converted to vectors first.

The User argument supplies the integration start time and a sampling clock which determines the step size used by the underlying numerical method.

This example uses integration to express the motion of a falling ball:

example7 u = withColor red (moveXY 0 (position u) (bigger 0.1 circle)) 
 where
   gravity :: RealB
   gravity = -0.1

   velocity, position :: RealB
   velocity u = integral gravity u

   position u = integral (velocity u)

Integrals may be mutually recursive.

5.4  Snapshots

A snapshot samples the value of a behavior at an event occurance. The snapshot functions are:

snapshot          :: Event a -> Behavior b -> Event (a, b)
snapshot_         :: Event a -> Behavior b -> Event b
snapshot_ e b     =  (e `snapshot` b) ==> snd
whenSnap          :: Event a -> Behavior b -> (a -> b -> Bool) -> Event a
whenSnap e b pred =  (e `snapshot` b `suchThat` uncurry pred) ==> fst
whenE             :: Event a -> Behavior Bool -> Event a
e `whenE` b       =  whenSnap e b (curry snd)

This program captures the mouse position when the left button is pressed and moves the ball to that position:

mouseEvs :: User -> Event Point2B
mouseEvs u = lbp u `snapshot_` mouse u

example8 u = withColor red 
               (move (stepper origin2 (mouseEvs u) (bigger 0.1 circle)) 

5.5  Predicates

The predicate function creates an event which monitors a boolean behavior:

predicate         :: Behavior Bool -> User -> Event (User)

The User argument is used as a sampling clock. Care must be taken since sampling may miss occurances of the boolean behavior. Consider the following program:

example9 u = withColor red (moveXY 0 (position u 0 (-0.1))
                                     (bigger 0.1 circle))
  where
    gravity :: RealB
    gravity = -0.6

    velocity :: RealB -> User -> RealB
    velocity v0 u = v0 + integral gravity u t0

    -- Assume floor is at y = -1
    -- Bounces with only 0.9 of the velocity each time

    position :: RealB -> RealB -> User -> RealB
    position x0 v0 u = p where
      v = velocity v0 u
      p = x0 + integral v u `untilB`
               (predicate (p <=* (-1)) u) `snapshot` v ==>
                 \(u',v') -> position (-1) (constantB (-v' * 0.9)) u'

The <=* operator is a behavioral version of <=, as explained in the next section. The predicate checks that the position is less or equal to the floor height rather than simply checking for equality since sampling may miss the instant at which the position is exactly at the floor.

5.6  Time Transformation

A time transform alters the time frame within a behavior.

timeTransform  :: GBehavior bv => bv -> Behavior Time -> bv

later, earlier :: GBehavior bv => TimeB -> bv -> bv
later dt b     =  b `timeTransform` (time - dt)
earlier dt     =  later (-dt)
faster, slower :: GBehavior bv => TimeB -> bv -> bv
faster x b     =  b `timeTransform` (time * x)
slower x       =  faster (1/x)

The expression timeTransform b1 tb yields a new behavior which is evaluated in a transformed time-frame according to behavior tb. For example:

example10 u = move (mouseMotion u)
                   (bigger 0.2 (withColor red circle))
                `over` 
              move (later 2 (mouseMotion u))
                   (bigger 0.3 (withColor blue circle))


5.7  Utilities

These are some miscellaneous utilities:

-- explicit behavior aging:
afterE :: GBehavior bv => Event a -> bv -> Event (a, bv) 
afterE_ :: GBehavior bv => Event a -> bv -> Event bv

-- Ties switcher to an event stream
repeatE :: GBehavior bv => Event bv -> Event bv
repeatE e = withRestE e ==> uncurry switcher

-- An event based version of scanl:
scanlE :: (a -> b -> a) -> a -> Event b -> Event a

countE :: Event a -> Behavior Int
countE e = stepper 0 (scanlE (\ c _ -> c + 1) 0 e)

-- Maps a pair of events onto a boolean behavior.  Initially false.  
toggle :: Event a -> Event b -> BoolB
toggle go stop =
  stepper False (  go   -=> True
               .|. stop -=> False)

-- Adds the previous value to the current event value:
withPrevE  :: Event a -> a -> Event (a,a)
withPrevE_ :: Event a -> a -> Event a

timeSince :: Time -> Behavior DTime
timeSince t0 = time - constantB t0

-- modify an ongoing behavior using event stream values
accumB :: GBehavior bv => (bv -> b -> bv) -> bv -> Event b -> bv

nextUser :: (User -> Event a) -> (User -> Event (a,User))
nextUser f u = f u `afterE` u
nextUser_ :: (User -> Event a) -> (User -> Event User)
nextUser_ f u = nextUser f u ==> snd

leftButton, rightButton :: User -> BoolB
leftButton  u = toggle (lbp u) (lbr u)
rightButton u = toggle (rbp u) (rbr u)

-- Debugging support
data TraceEFlag = TraceOccsE | TraceAllE   deriving (Show, Eq)
traceE :: Show a => String -> TraceEFlag -> Event a -> Event a

6  Lifted Behaviors

We say a type or function which has been raised from the domain of ordinary Haskell values to behaviors is "lifted". For example, a function such as

(&&)   ::    Bool -> Bool -> Bool

can be promoted to a corresponding function over behaviors:

(&&*)   ::    BoolB -> BoolB -> BoolB

The type BoolB is a synonym for Behavior Bool; most commonly used types have a behavioral synonym defined in Fran. The name &&* arises from a simple naming convention in Fran: lifted operators are appended with a * and lifted vars are appended with B.

The renaming required by && can sometimes be avoided using type classes. For example, an instance declaration such as the following

instance  Num a => Num (Behavior a) 

allows all of the methods in Num to be applied directly to behaviors without renaming. Constant types in the class definition cannot be lifted by such a declaration. In the Num instance above, the type of fromInteger is

fromInteger    ::  Num a => Integer -> (Behavior a)

The argument to fromInteger is not lifted - only the result. This allows integer constants to be treated as constant behaviors. While fromInteger works in the expected way, other class methods cannot be used. In the declaration

instance Ord a => Ord (Behavior a) 

is not useful since it defines operations such as

(>)     :: Behavior a -> Behavior a -> Bool

Unfortunately, Fran needs a > function which returns Behavior Bool instance of just Bool. The Eq and Ord classes are not lifted using instance declarations. Rather, each method is individually renamed and lifted. These are the lifting functions: they transform a non-behavioral function into its behavioral counterpart:

constantB       :: a -> Behavior a
($*)            :: Behavior (a -> b) -> Behavior a -> Behavior b
lift0           :: a -> Behavior a
lift0           =  constantB
lift1           :: (a -> b) -> Behavior a -> Behavior b
lift1 f b1      = lift0 f $* b1
lift2           :: (a -> b -> c) -> Behavior a -> Behavior b -> Behavior c
lift2 f b1 b2   = lift1 f b1 $* b2
lift3           :: (a -> b -> c -> d)
                   -> Behavior a -> Behavior b -> Behavior c -> Behavior d
lift3 f b1 b2 b3 = lift2 f b1 b2 $* b3
...
lift7  ...

Using these functions, the definition of (>*) is

(>*)  = lift2 (>)

Many Prelude functions have been lifted in Fran via overloading:

(+)         :: Num a => Behavior a -> Behavior a -> Behavior a
(*)         :: Num a => Behavior a -> Behavior a -> Behavior a
negate      :: Num a => Behavior a -> Behavior a 
abs         :: Num a => Behavior a -> Behavior a 
fromInteger :: Num a => Integer -> Behavior a 
fromInt     :: Num a => Int -> Behavior a 

quot        :: Integral a => Behavior a -> Behavior a -> Behavior a
rem         :: Integral a => Behavior a -> Behavior a -> Behavior a
div         :: Integral a => Behavior a -> Behavior a -> Behavior a
mod         :: Integral a => Behavior a -> Behavior a -> Behavior a
quotRem     :: Integral a => Behavior a -> Behavior a -> 
                             (Behavior a, Behavior a)
divMod      :: Integral a => Behavior a -> Behavior a ->
                             (Behavior a, Behavior a)

fromDouble   :: Fractional a => Double -> Behavior a
fromRational :: Fractional a => Rational -> Behavior a
(/)          :: Fractional a => Behavior a -> Behavior a -> Behavior a

sin          :: Floating a => Behavior a -> Behavior a
cos          :: Floating a => Behavior a -> Behavior a
tan          :: Floating a => Behavior a -> Behavior a
asin         :: Floating a => Behavior a -> Behavior a
acos         :: Floating a => Behavior a -> Behavior a
atan         :: Floating a => Behavior a -> Behavior a
sinh         :: Floating a => Behavior a -> Behavior a
cosh         :: Floating a => Behavior a -> Behavior a
tanh         :: Floating a => Behavior a -> Behavior a
asinh        :: Floating a => Behavior a -> Behavior a
acosh        :: Floating a => Behavior a -> Behavior a
atanh        :: Floating a => Behavior a -> Behavior a
pi           :: Floating a => Behavior a
exp          :: Floating a => Behavior a -> Behavior a
log          :: Floating a => Behavior a -> Behavior a
sqrt         :: Floating a => Behavior a -> Behavior a
(**)         :: Floating a => Behavior a -> Behavior a -> Behavior a
logBase      :: Floating a => Behavior a -> Behavior a -> Behavior a

These operations correspond to functions which cannot be overloaded for behaviors. The convention is to use the B suffix for vars and a * suffix for ops.

fromIntegerB     :: Num a => IntegerB -> Behavior a
toRationalB      :: Real a => Behavior a -> Behavior Rational
toIntegerB       :: Integral a => Behavior a -> IntegerB
evenB, oddB      :: Integral a => Behavior a -> BoolB
toIntB           :: Integral a => Behavior a -> IntB
properFractionB  :: (RealFrac a, Integral b) => Behavior a -> Behavior (b,a)
truncateB        :: (RealFrac a, Integral b) => Behavior a -> Behavior b
roundB           :: (RealFrac a, Integral b) => Behavior a -> Behavior b
ceilingB         :: (RealFrac a, Integral b) => Behavior a -> Behavior b
floorB           :: (RealFrac a, Integral b) => Behavior a -> Behavior b
(^*)             :: (Num a, Integral b) =>
                            Behavior a -> Behavior b -> Behavior a
(^^*)            :: (Fractional a, Integral b) =>
                            Behavior a -> Behavior b -> Behavior a
(==*)            :: Eq a => Behavior a -> Behavior a -> BoolB
(/=*)            :: Eq a => Behavior a -> Behavior a -> BoolB
(<*)             :: Ord a => Behavior a -> Behavior a -> BoolB
(<=*)            :: Ord a => Behavior a -> Behavior a -> BoolB
(>=*)            :: Ord a => Behavior a -> Behavior a -> BoolB
(>*)             :: Ord a => Behavior a -> Behavior a -> BoolB
cond             :: BoolB -> Behavior a -> Behavior a -> Behavior a
notB             :: BoolB -> BoolB
(&&*)            :: BoolB -> BoolB -> BoolB
(||*)            :: BoolB -> BoolB -> BoolB
pairB            :: Behavior a -> Behavior b -> Behavior (a,b)
fstB             :: Behavior (a,b) -> Behavior a
sndB             :: Behavior (a,b) -> Behavior b
pairBSplit       :: Behavior (a,b) -> (Behavior a, Behavior b)
showB            :: (Show a) => Behavior a -> Behavior String

A few list-based functions are lifted, although most of the functions in PreludeList are not lifted.

nilB             :: Behavior [a]
consB            :: Behavior a -> Behavior [b] -> Behavior [b] 
headB            :: Behavior [a] -> Behavior a 
tailB            :: Behavior [a] -> Behavior [a] 
liftLs           :: [Behavior a] -> Behavior [a]

7  Numeric Types

The numeric types and functions are available both a static values and as behaviors. Since the same name is generally used for both the static and behavioral version of a function, only the behavioral names are exported by the Fran module. If the non-behavioral functions are needed, the convention is to add

import qualified StaticTypes as S

to the program and qualify static names with S., as in S.origin2.

7.1  Basic Numeric Types

All scalar types are essentially the same in Fran. Synonyms allow type signatures to contain extra descriptive information such as Fraction for values between 0 and 1 but no explicit type conversions are required between the various scalar types.

type RealVal  = Double
type Length   = RealVal  -- non-negative
type Radians  = RealVal  -- 0 .. 2pi (when generated)
type Fraction = RealVal  -- 0 to 1 (inclusive)
type Scalar   = Double

type Time     = Double
type DTime    = Time     -- Time deltas, i.e., durations

data Point2     -- 2D point
data Vector2    -- 2D vector
data Transform2 -- 2D transformation

data Point3     -- 3D point
data Vector3    -- 3D vector
data Transform3 -- 3D transformation

type RealB       = Behavior RealVal
type FractionB   = Behavior Fraction
type RadiansB    = Behavior Radians
type LengthB     = Behavior Length
type TimeB       = Behavior Time
type IntB        = Behavior Int

type Point2B     = Behavior Point2
type Vector2B    = Behavior Vector2
type Transform2B = Behavior Transform2

type Point3B     = Behavior Point3
type Vector3B    = Behavior Vector3
type Transform3B = Behavior Transform3


7.2  Points and Vectors



origin2             :: Point2B                                   
point2XY            :: RealB -> RealB -> Point2B
point2Polar         :: LengthB  -> RadiansB -> Point2B
point2XYCoords      :: Point2B  -> Behavior (RealVal, RealVal)
point2PolarCoords   :: Point2B  -> Behavior (Radians, Length )            
distance2           :: Point2B  -> Point2B  -> LengthB             
distance2Squared    :: Point2B  -> Point2B  -> LengthB             
linearInterpolate2  :: Point2B  -> Point2B  -> RealB -> Point2B  
(.+^)               :: Point2B  -> Vector2B -> Point2B             
(.-^)               :: Point2B  -> Vector2B -> Point2B             
(.-.)               :: Point2B  -> Point2B  -> Vector2B            

origin3             :: Point3B                                   
point3XYZ           :: RealB -> RealB -> RealB -> Point3B
point3XYZCoords     :: Point3B  -> Behavior (RealVal, RealVal, RealVal)            
distance3           :: Point3B  -> Point3B  -> LengthB             
distance3Squared    :: Point3B  -> Point3B  -> LengthB             
linearInterpolate3  :: Point3B  -> Point3B  -> RealB -> Point3B  
(.+^#)              :: Point3B  -> Vector3B -> Point3B             
(.-^#)              :: Point3B  -> Vector3B -> Point3B             
(.-.#)              :: Point3B  -> Point3B  -> Vector3B            

xVector2, yVector2 :: Vector2B   -- unit vectors
vector2XY          :: RealB -> RealB -> Vector2B
vector2Polar       :: LengthB  -> RadiansB -> Vector2B
vector2XYCoords    :: Vector2B -> Behavior (RealVal, RealVal)
vector2PolarCoords :: Vector2B -> Behavior (Length,  Radians)
rotateVector2      :: RealB -> Vector2B -> Vector2B
instance Num Vector2 -- fromInteger, * not allowed

xVector3           :: Vector3B   -- unit vector
yVector3           :: Vector3B   -- unit vector
zVector3           :: Vector3B   -- unit vector
vector3XYZ         :: RealB -> RealB -> RealB -> Vector3B
vector3XYZCoords   :: Vector3B -> Behavior (RealVal, RealVal, RealVal)
vector3Spherical   :: LengthB  -> RadiansB ->
                      RadiansB -> Vector3B
vector3PolarCoords :: Vector3B -> Behavior (Length,  Radians)
instance Num Vector3 -- fromInteger, * not allowed

Note that vectors and points have distinct types. You cannot use + to add a point to a vector. Vectors are a member of the Num class while points are not; thus + works with vectors but not points. Although it is in class Num, the * operator cannot be used for vectors.

Read the `.' in the operators above as `point' and `^' as `vector'. Thus .+^ means `point plus vector'.

7.3  Vector Spaces



zeroVector       :: VectorSpace v => Behavior v
(*^)             :: VectorSpace v => ScalarB -> Behavior v -> Behavior v
(^/)             :: VectorSpace v => Behavior v -> ScalarB -> Behavior v
(^+^),(^-^)      :: VectorSpace v => Behavior v -> Behavior v -> Behavior v
dot              :: VectorSpace v => Behavior v -> Behavior v -> ScalarB
magnitude        :: VectorSpace v => Behavior v -> ScalarB
magnitudeSquared :: VectorSpace v => Behavior v -> ScalarB
normalize        :: VectorSpace v => Behavior v -> Behavior v

instance VectorSpace Double
instance VectorSpace Float
instance VectorSpace Vector2
instance VectorSpace Vector3

7.4  Transformations

The types Transformation2B and Transformation3B represent geometric transformation on images, points, or vectors. The basic transformations are translation, rotation, and scaling. Complex transformations are created by composing basic transformations. The class Transformable2 contains 2-D transformable objects.

These are the transformation operations:

class Tranformable2B a where
   (*%)    :: Transform2B -> a -> a   -- Applies a transform

identity2  :: Transform2B
translate2 :: Vector2B -> Transform2B
rotate2    :: RealB -> Transform2B
compose2   :: Transform2B -> Transform2B -> Transform2B
inverse2   :: Transform2B -> Transform2B
uscale2    :: RealB -> Transform2B  -- only uniform scaling
factorTransform2 :: Transform2B -> Behavior (Vector2, RealVal, RealVal)

instance Transformable2B Point2B
instance Transformable2B Vector2B

class Tranformable3B a where
   (**%)  :: Transform3B -> a -> a   -- not currently used
class Translateable3B a where
   translate3 :: a -> Transform3
class Scaleable3B a where
   scale3 :: a -> Transform3
identity3  :: Transform3B
rotate3    :: Vector3B -> RealB -> Transform3B
compose3   :: Transform3B -> Transform3B -> Transform3B
uscale3    :: RealB -> Trabsform3
factorTransform3 :: Transform3B -> Behavior (Vector3, RealVal, RealVal)

instance Translatable3B Point3B
instance Translatable3B Vector3B
instance Scaleable3B Point3B
instance Scaleable3B Vector3B

A transformation which doubles the size of an object and then rotates it 90 degrees would be rotate2 (pi/2) `compose2` uscale2 2. Note that the first transform applied is the one on the right, as with Haskell's function composition operator (.).

8  Rendered Objects

Fran is capable of rendering 2-D images, 3-D images, and sounds.

8.1  Fonts

Fonts are defined as follows:
data Font   = Font Family Bool Bool -- family isBold isItalic
data Family = System | TimesRoman | Courier | Arial | Symbol
system, timesRoman, courier, arial, symbol :: Font
bold         :: Font -> Font
italic       :: Font -> Font

data TextT   = TextT Font String
type TextB   = Behavior TextT

simpleText   :: StringB -> TextB
boldT        :: TextB -> TextB          
italicT      :: TextB -> TextB
textFont     :: Font  -> TextB -> TextB

The simpleText function creates a text object using a default font. The other operators transform text objects by changing their font.

8.2  Colors

These functions define Fran colors:

colorRGB            :: FractionB -> FractionB -> FractionB -> ColorB
colorHSL            :: RealB -> FractionB -> FractionB -> ColorB
colorRGBCoordsB     :: ColorB -> Behavior (Fraction, Fraction, Fraction)
colorHSLCoordsB     :: ColorB -> Behavior (RealVal, Fraction, Fraction)
colorRGBCoords      :: ColorB -> (FractionB, FractionB, FractionB)
colorHSLCoords      :: ColorB -> (RealB, FractionB, FractionB)
interpolateColorRGB :: ColorB -> ColorB -> RealB -> ColorB
interpolateColorHSL :: ColorB -> ColorB -> RealB -> ColorB
grey                :: FractionB -> ColorB

stronger            :: FractionB -> ColorB -> ColorB
duller              :: FractionB -> ColorB -> ColorB
darker              :: FractionB -> ColorB -> ColorB
brighter            :: FractionB -> ColorB -> ColorB
shade               :: FractionB -> ColorB -> ColorB

white, black, red, green, blue      :: ColorB
lightBlue, royalBlue, yellow, brown :: ColorB

asColorRef          :: ColorB -> Win32.COLORREF 

8.3  2-D Images

The type ImageB represents reactive 2-D images.

emptyImage     :: ImageB
solidImage     :: ImageB
flipImage      :: HFlipBook -> RealB -> ImageB
soundImage     :: SoundB -> ImageB
over           :: ImageB -> ImageB -> ImageB
overs          :: [ImageB] -> ImageB
withColor      :: ColorB -> ImageB -> ImageB
line           :: Point2B -> Point2B -> ImageB
circle         :: ImageB
polygon        :: [Point2B] -> ImageB
polyline       :: [Point2B] -> ImageB
polyBezier     :: [Point2B] -> ImageB
polygonB       :: Behavior [Point2] -> ImageB
polylineB      :: Behavior [Point2] -> ImageB
polyBezierB    :: Behavior [Point2] -> ImageB
bezier         :: Point2B -> Point2B -> Point2B -> Point2B -> ImageB
textImage      :: TextB -> ImageB
importBitmap   :: String -> ImageB
importBitmapWithSize :: String -> (ImageB, RealVal, RealVal)

instance Transformable2 ImageB
instance GBehavior ImageB  

Most of these operations are self-explanitory. The line, polyline, and bezier functions form lines and curves of a fixed system-determined width. Polygon filling uses a odd-even rule to determine whether a region is inside the polygon. All but the bitmaps are painted with a solid color, as selected by withColor. Most of these objects are centered at the origin. Text is centered just below the origin.

Bitmaps are centered at the origin and are displayed actual size unless scaled. Bitmaps must be stored in .bmp files.

These are some simple utilities for 2-D images. The Transformable2B context denotes images as well as 2-D points and vectors.



-- Star figure.  Arguments: skip and vertices.  
star              :: IntB -> IntB -> ImageB
regularPolygon    :: IntB -> ImageB
regularPolygon vs =  star 1 vs
square            :: ImageB
square            =  regularPolygon 4

move               :: Transformable2B a => Vector2B -> a -> a
move dp thing      =  translate2 dp *% thing
moveXY             :: Transformable2B a => RealB -> RealB -> a -> a
moveXY dx dy thing =  move (vector2XY dx dy) thing

bigger, smaller    :: RealB -> ImageB -> ImageB
bigger sc          =  (uscale2 sc *%)
smaller sc         =  bigger (1/sc)
stretch            :: RealB -> ImageB -> ImageB
stretch            =  bigger

-- 1.0 = 180 degrees
turnLeft, turnRight :: Transformable2B a => FractionB -> a -> a
turnLeft frac im    =  rotate2 (frac * pi) *% im
turnRight frac      =  turnLeft (-frac)

-- Oscillates between -1 and 1
wiggle, waggle      :: RealB
wiggle              =  sin (pi * time)
waggle              =  cos (pi * time)  -- 90 degrees out of phase 
wiggleRange         :: RealB -> RealB -> RealB
wiggleRange lo hi   =  lo + (hi-lo) * (wiggle+1)/2

stringBIm            :: Behavior String -> ImageB
stringIm             ::          String -> ImageB
stringBIm str        =  textImage (simpleText str)
stringIm             =  stringBIm . constantB

showBIm              :: Show a => Behavior a -> ImageB
showIm               :: Show a =>          a -> ImageB
showBIm              =  stringBIm . showB
showIm               =  showBIm . constantB

-- Given an image and a canonical size, stretch the image uniformly so
-- that the size maps exactly onto the window view size.
viewStretch          :: Vector2B -> User -> ImageB -> ImageB


8.4  3-D Geometry



emptyG               :: GeometryB
unionG               :: GeometryB -> GeometryB -> GeometryB
withColorG           :: ColorB -> GeometryB -> GeometryB
ambientLightG        :: GeometryB
pointLightG          :: GeometryB
spotLightG           :: GeometryB
directionalLightG    :: GeometryB
parallelPointLightG  :: GeometryB
-- Import geometry from a file
importX              :: String -> GeometryB

instance  GBehavior GeometryB  where
instance  Transformable3B GeometryB  where

8.5  Sound

Conal: I don't see any function to load sound files -- jcp



silence           :: SoundB
mix               :: SoundB -> SoundB -> SoundB
volume            :: RealB -> SoundB -> SoundB
pitch             :: RealB -> SoundB -> SoundB

instance  GBehavior SoundB  

9  Fixities

Here are all fixities defined in Fran:

infixr 0  $*               -- lifted $ 

infixr 1 `untilB`

infixl 2 .|.
infixr 2 ||*               -- lifted ||

infixl 3 ==>
infixl 3 -=>
infixr 3 &&*               -- lifted &&

infix 4 ==*, <*, <=* , >=*, >*     -- Lifted comparisons
infix 4 .+^, .-^, .-.              -- point / vector addition/subtraction
infix 4 .+^#, .-^#, .-.#           -- 3D points
infix 4 `pointPlusVector2`, `pointMinusVector2`, `pointMinusPoint2`

infixl 6 `unionG`
infixl 6 `over`
infixl 6 ^+^, ^-^                  -- vector add and subtract

infixr 7 `dot`, *^, ^/             -- scalar * vector, vector / scalar
infixr 7 *%, `compose2`            -- 2D transforms
infixr 7 **%, `compose3`           -- 3D transforms

infixr 8  ^*, ^^*                  -- lifted ^, ^^

infixl 9 `snapshot`, `snapshot_`, `whenE`
infixl 9 `handleE`, `filterE`