Reactors
We will create animations using a tool called a reactor. A reactor allows us to write an animation in terms of three (and one optional) things:
- some initial value, such as a
Point
; - an update function that transforms the value into its next value every clock tick, such as moving the
Point
; - a rendering function that turns the value into an
Image
; and - an optional condition that determines when the animation stop.
Here's an example that moves a circle from left to right, stopping when the the circle gets to the point (300, 0).
val travellingCircle =
Reactor.init(Point(-300, 0))
.withOnTick(pt => Point(pt.x + 1, pt.y))
.withRender(pt => Image.circle(10).at(pt))
.withStop(pt => pt.x >= 300)
(We could write the onTick
function as pt -> pt + Vec(1,0)
if we're comfortable with vector arithmetic.)
This constructs a reactor but it does not draw it. To do this we must call the run
method, passing a Frame
that tells the reactor how big to make the window it draws on. Here's an example:
travellingCircle.run(Frame.size(600, 600))
This generates the animation shown in Figure reactor:travelling.
Here's an another example that moves a circle in a circular orbit. This time the animation has no stopping condition, so it continues forever.
val orbitingCircle =
Reactor.init(Point(0, 300))
.withOnTick(pt => pt.rotate(2.degrees))
.withRender(pt => Image.circle(10).at(pt))
We run this reactor in the same way.
orbitingCircle.run(Frame.size(600, 600))
This generates the animation shown in Figure reactor:orbit.
Exercise: Rose Curve
Make an animation where an image moves in a rose curve (we saw the rose curve in an earlier chapter). Be as creative as you wish. You might find it fun to change the background of the Frame
on which you draw the animation; a dark background is often more effective than a light one. You can do this by calling the background
method on Frame
. For example, here is how you'd create a 600 by 600 frame with a dark blue background.
Frame.default.withSize(600, 600).withBackground(Color.midnightBlue)
Remember you will need to import doodle.reactor._
to make the reactor library available.