This week we’ll add to Field four kinds of “simulations” to our
The first, and perhaps the most generally useful, is a general purpose 2D physics engine. This is actually an implementation of a popular physics system called Box2D that has been used in a wide variety of computer games over the last 10 years or so. If you’ve “Cut the Rope” or Angered a Bird in any way on an phone you have interacted with this particular physics engine.
As all physics engines do we combine three things which, taken together, cover what we mean by “physics” for a 2d world: motions on bodies controlled by forces and torques, collisions that end up applying forces and torques to bodies in motion and joints that couple and constrain the motion of bodies. Here, in Field, “body” is an
Let’s start, as usual, with some example code:
This should come as no surprise:
Up until now if we wanted this line to move we might have some kind of a loop:
This would cause
f1 to jitter around randomly. Rather than doing that, let’s add some physically simulated motion to that
We’ve added four things to our standard drawing of a rectangle. First, we’ve made a
PhysicsSystem which is a ‘world’ for (simulated) physical bodies to interact in. FLines can participate in at most one
PhysicsSystem. Secondly, we associate an
FLine with this particular
p with the line
p.addPhysics(f1). Thirdly we’ve applied a torque to the
FLine (torque being the precise word to talk about a purely rotational force). We do this by calling
f1 got a
.physics from our call to
addPhysics. Finally we have an animation loop that called
p.update() — this gives the
PhysicsSystem a chance to compute the next step of what the world looks like and modifies the line
f1 to follow the trajectory given by its forces and torques.
What do we get from all of our effort? This:
We’ve applied a ‘push’ to our rectangle and sent it spinning, but the actual spinning comes from the physics simulation. As typical, we can take this much further.
This gives us a more complex toy-box:
We see here behavior that would be incredibly tedious to plot out by hand: the fall of the red box inscribes a parabola (which is what falling / moving objects do in the ‘real world’), until it bounces then collides partially with the black box, and they both spin and slide convincingly. The motion is detailed and believable. This is parametric motion — if we want to move the box over a little, we just move the box over a little and the computer computes the new motion.
There is a range of things that you can call on these
physics objects that attach themselves to
FLines once you have called
applyTorque(x)— adds a rotational force
applyForce(vec(x,y))— adds a translational force
setLinearVelocity(vec(x,y))— sets the velocity (that’s direction and speed) of an object.
setRotationalVelocity(x)— sets how fast the object is spinning/
setDensity(x)— sets the density of objects, how much they ‘weight’ per how big they are.
setFixed()— makes the object not move under any circumstances (although it still can participate in collisions).
setRestitution(0.3)— determines how ‘bouncy’ or elastic an object is (note, this is ultimately a combination of both things that collide). Default to 1 (like a rubber ball)
setFriction(0.8)— determines how quickly touching objects grind to a halt if they keep touching. Defaults to 0 (like wet ice on wet ice…).
Finally, two things that you can do with the physics system itself:
p.setGravity(vec(x,y))— sets a gravity direction (where things fall). Remember ‘y’ points down in Field, therefore you’ll want a gravity like
p.distanceJoint(someFLine, someOtherFLine)— makes a joint between the centers of two
FLinethat tries to keep the distance between them fixed, like a spring.
p.distanceJoint(someFLine, someOtherFLine, dampingRatio, frequency)— like
distanceJointbut with more control over how much the physics system will try to maintain the joint. For example
p.distanceJoint(f1, f2, 0.01, 10)will produce a much stiffer (potentially to the point of instability) joint.
p.weldJoint(someFLine, someOtherFLine, dampingRatio, frequency)— like a distance joint, but also maintaining (or trying to maintain) rotational constraints as well.
Once (but not before) you’ve called
addPhysics on an
FLine you can also ask for contact events. This lets you arrange for pieces of code to be called when objects hit other objects. A collision has a start and (possibly) an end. Here’s how to change the color of that line
f2 above when it’s in contact with something else:
That will set
f2 to be bright red when it’s in contact with anything else, and then yellow when that contact stops:
If want to get extra fancy, you can use the parameter
other that’s passed into that function to change this behavior based on the other
FLine that this
FLine is hitting. Note, also, that there’s a parameter
points that’s passed into the onContactStart function. This contains a list of points where the contact is occuring.
And, two cautionary notes:
A full 2D physics interface reads longer than this — you can vary the coefficients of friction and the elasticity of objects for example and there are many, many more kinds of joints — but it isn’t more complex. We have the fundamentals here, and we always extend this out further if final projects demand. Finally, we should note that typical computer have enough computational power for around 100 potentially colliding objects. For example, if we add this to the above example:
(You can see some jumping about at the beginning because some of these random rectangles end up inside the larger rectangles so they are quickly pushed out).
FLines to the physics system without drawing them and, rather, use where
FLines end up after physics is updated to draw something else
The second “simulation” addition to Field this week is a Boids model. Boids is a ‘famous’ model that reproduces a range of flocking-like behavior that seems to mimic some of the properties of aggregations of animals (from birds to slime molds). There’s much information to be had out of Craig Reynolds’ website who was probably the first to formally realize the generative potential of the approach. Ultimately, we’ll let the example code speak for itself:
This yields something that’s eerily like a flock of birds:
Highlights from the code above:
vec(x,y)that tells you where the boid is. You can set this to something else if you like, for example
bb.at.x = 30and the boid will be teleported there
bb.rotation()— returns something that will rotate an
FLineto have the same heading as a boid
bb.tooFarcontrol the simulation parameters
boids.addBoid(startingPosition, startingHeading, speed)adds a boid to simulation
boids.allis a list of all of the boids in a simulation
boids.headBoid(index, position, heading)— nominates, for this single frame only, boid number
indexhas really important, teleporting it to
L-Systems are a “notational / computational” system that we saw briefly when we looked at notational strategies. Really, they straddle notational concerns (they build micro-languages for describing drawing processes), and simulation concerns (the arise out of trying to build geometries that seem organic and grow organically). Like ‘Boids’, you can do worse than simply Google them.
A general ‘production system’ (which is the fancy name for the family of languages that L-Systems are a part of) combines three things which act on “sentences” made up of letters. The first is the ‘axiom’ — which is simply the starting sentence. The second is a set of ‘production rules’ that, when they match parts of a sentence rewrite that part to be different. Finally, there are a set of rules that interpret what the letters mean or do — usually, in our case, to issue some instructions into an
Our rules say, wherever you see ‘0’ replace it with ‘10’ and wherever you see ‘1’ replace it with ‘0’. We don’t need to use ‘0’, ‘1’, ‘[’, or ‘]’, we are free to use any characters we want. We can also have rules that match more than one character, like ‘01’ -> ‘0AB1’.
Now we can run our production system a little and see what it grows:
This will print something like:
As you can see the ‘sentence’ grows quickly from ‘0’ (often you’ll find that you can run this loop with more than, say, 7 iterations).
So far we’ve played a ‘syntactical’ substitution game; let’s add some semantics - what the letters mean:
These ‘rules’ say, whenever you see ‘0’ do
f.lineForward(10) (which adds a line in the ‘forward’ direction). Calls like
f.right(4) rotate our ‘heading’ by 4 degrees to the right.
] save and reload the position and direction of the line respectively.
One final call applies these action rules to the sentence that we built:
Yielding this shape here:
As usual there are a number of routes in here.
_.wait() _.redraw()or a
_.stage.frame()and watch the tree draw out.
The last useful ‘simulation’ we’ll add to Field is a class of random number generators. True randomness is something that computers, the story goes, find particularly hard. In truth, for all of our purposes, computers have no trouble producing completely unpredictable number sequences. The trouble, rather, is that we don’t want completely unpredictable number sequences. Rather we go to “random sequences” when we want things to look less like a computer made them.
Partially in response to this desire to produce “organic” sequences of numbers — that is, variations that wobble in the ways that natural phenomenon wobble — Ken Perlin came up with a new approach to randomness in the early 80s. Fast to compute, easy to control, the approach has been used widely to ‘rough up’ almost everything that might otherwise seem sterile: textures (to make things look less computer generated), motion (to make things look less robotic), rhythms (to make things sound like a ‘real’ drummer) etc. It’s in Toy Story (the wood textures), it’s in Tron (the geometry of the rock / ice formations) and there’s nary a CGI tree that waves in the wind that isn’t waving to Perlin’s function and so on. Once you start to see it, it’s everywhere. So much so, in fact, that Perlin won a technical Oscar for what amounts to a single mathematical function.
Let’s motivate it this way. First of all, let’s begin with the noise function that many of you have been using to date
This plots something like this:
Math.random() returns a random number between 0 and 1. Other than the many many more numbers involved, this is a lot like tossing a coin: each number is completely independent from the one before. There are lots of ‘grade school math’ tricks to shape this randomness. For example:
This makes the randomness ‘spikey’ for example. (You might work through this in your head: raising numbers between 0 and 1 to large powers makes most of them all much closer to 0, leaving only a handful that happened to be very close to 1 spread out over the rest of the range).
But functions of a single random value like this are at best a crude way of manipulating how this change varies over time. And it’s not completely clear how to proceed. If we want something that’s “smoother”, we could start to play games like this:
This builds a sequence where the ‘next’ number is 90% the same as the previous number, 10% random. We can continue, for even less wiggle, even more ‘smoothness’ with a 99% and 1%:
As you can see this certainly is smoother, but it doesn’t really wobble at all. In fact the only real long term change is because we’ve picked the wrong first value for this function (
var lastValue = 0) if we change that to
var lastValue = 0.5 we’ll see that there’s hardly any variation to this line. We’ve tried to change the ‘frequency’ and the amplitude has changed as well.
We want something that still wiggles as much as ever, as much as the original pure
Math.random() function, but wiggles slower — not to fast, not too slowly. This is Perlin Noise:
This gives this, very different function:
First of all it’s between -1 and 1, but unlike our attempts at smoothing
Math.random() it really does fill this range. Secondly is moves much slower. That’s controlled by the parameter to
nextDouble(0.1). Setting that to
That parameter roughly controls the frequency of the noise — small numbers yield slow functions, numbers beyond and including
1 give us
Math.random() again. We can do things like this:
And watch our wobble ‘ramp up`:
Perlin noise is a very useful building block to build organic feeling things. In a loose sense, a Perlin noise function yields a random sequence that has frequencies within a single ‘octave’. The first move towards building more complex random sequences is usually to combine Perlin noise functions at different octaves and amplitudes together. We end up with more control because we can vary the amplitudes of each octave separately. This is such a useful approach that it’s also built into Field:
This has just a little more small, high-frequency wiggle, on top of lots of low-frequency wobble. Functions like these turn up everywhere in nature. For large chunks of ‘time’, musical instruments are like this, variations in the tides are like this, the stock market is like this (except when they aren’t).
RandomCascade is adding together 4
Randoms at 4 different ‘octaves’ in proportions 1, 1/2, 1/4, and 1/8. We can configure this:
This sets the amplitude of the 4th noise function
a to 0.2 and the frequency
h to 16x while setting the third noise function to
1 and it’s frequency to a very slow 0.1x. This gives:
Which recalls that stock market (on a normal day) or a mountainside.
Once we have a smoothly varying random function we can use that to drive other “random” processes. Here’s an important strategy:
This turns out random ‘events’ (white lines) that have random, but controllable frequency (they are generally never too close and never too far apart):
One final note: Randomness is sometimes exactly what we need to catch sight of something interesting. Don’t know how to make something move? wiggle it with a random function. Don’t know what color to make something? Add Perlin Noise. One trick, though, is to be able to reproduce your discoveries.
RandomCascade come with alternative constructors that let you seed your random functions. Given the same seeds, you’ll get the same random sequences.
Here’s the syntax:
If you change “seed” to something else, or that 2 to any other number you’ll get a completely different sequence of random numbers out, another (Perlin) set of random dice rolls. But if you don’t change it, your code will always reproduce what it did last time.