Introduction - Normal Distribution of Random Numbers
This paragraph from the tutorial summarises what the section is about better than I could manage:
A distribution of values that cluster around an average (referred to as the “mean”) is known as a “normal” distribution. It is also called the Gaussian distribution (named for mathematician Carl Friedrich Gauss) or, if you are French, the Laplacian distribution (named for Pierre-Simon Laplace). Both mathematicians were working concurrently in the early nineteenth century on defining such a distribution.
In Processing
Here’s the first example for this section:
Random generator;
void setup() {
size(640,360);
generator = new Random();
}
void draw() {
float num = (float) generator.nextGaussian(); //Note that nextGaussian() returns a double.
float sd = 60;
float mean = 320;
float x = sd * num + mean; //Multiply by the standard deviation and add the mean.
noStroke();
fill(255,10);
ellipse(x,180,16,16);
}
Running this in Processing yielded an error:
Cannot find a class or type named “Random”
Google found this archived Processing forum thread and the tutorial linked to the JavaDocs for the Random class. Taken together, these lead to the addition of this first line, which solved the problem:
import java.util.Random
In JavaScript
To begin with, I decided to solve a minor, unimportant, invented, problem that had been nagging me: As I was building a framework to support the animations, it seemed odd to start and stop them using code external to the framework. Or, written another way: the framework code should allow for starting, continuing and stopping the animation, rather than the application code.
I refactored Frames
a little to support this, adding go()
and stop()
methods to the API.
// ...
//
// animation functions
//
function setup ( cb ) {
setupCallback = cb;
try {
setupCallback();
isSetUp = true;
} catch ( err ) {
console.error( 'Failed to setup drawing context' );
console.error( err );
}
}
function drawLoop () {
if( running ) {
drawCallback();
requestAnimationFrame( drawLoop );
}
}
function go ( setupCB, drawCB ) {
setup( setupCB );
drawCallback = drawCB;
running = true;
requestAnimationFrame( drawLoop );
}
function stop () {
running = false;
}
//
// Public API
//
return {
setupCanvas: setupCanvas,
clearCanvas: clearCanvas,
mouseX: function getMouseX () { return mouseX || 0; },
mouseY: function getMouseY () { return mouseY || 0; },
mousePressed: function getMousePressed () { return mousePressed; },
go: go,
stop: stop
};
// ...
Any variables that look like they’re globals in this snippet are defined with vars and held within the Frames
closure.
Note that go()
accepts the setup()
and draw()
functions from the application code, so these still need to be implemented and passed to the framework. I began to think it would be good to separate them, and started to do that (hence go()
calling setup()
), but realised this was premature optimisation and quit whilst I was ahead.
On to implementing the example.
The Processing version makes use of the Random
class from Java. There is no built-in equivalent in JavaScript, and Math.random()
returns uniformly distributed random numbers, rather than normal/Gaussian. I started out trying to roll my own Random
library, to contain methods for returning all sorts of random numbers. I got as far as moving the randomInt()
function created the other day into its own namespace when I realised others had probably done this work for me (and also that I don’t need to learn how to generate Gaussian numbers myself for the course).
- Chance.js is a rich library for creating all sorts of random data. It seems to be focussed primarily on data for tests. It creates random strings, Twitter handles and dates as well as numbers with various distribtuion characteristics. I don’t need all that for this example.
- random.js is part of a bigger suite called sim.js. It just generates random numbers, and has a method for returning numbers with normal distribution. Perfect!.
In other respects, the example is similar to the Processing version. One quirk I explored was creating a function which returns circle-drawing functions. I’m not entirely sure now why I did it. One convenience is provides is that now the draw()
function is much neater and more declarative.
(function( Frames, Random ){
var context;
function makeCircleOfRadius ( rad ) {
return function circle ( x, y ) {
context.beginPath();
context.arc( x, y, rad, 0, 2 * Math.PI );
context.fill();
}
}
var drawDisc = makeCircleOfRadius( 16 );
function setup () {
context = Frames.setupCanvas( 640, 360 );
context.fillStyle = 'rgba(255,255,255,0.1)';
}
function draw () {
// The normal generator from Random.js requires a mean and SD be passed in
// meaning the variables for these needed by Java/Processing's version
// are not needed here
var randomGaussian = Random.normal( 320, 60 );
drawDisc( randomGaussian, 180 );
}
Frames.go( setup, draw );
})( Frames, new Random() );
In the last line, I create an instance of the Random.js library object. This is possible because I also included this line in the HTML:
Exercise i.4 Paint splatters
Consider a simulation of paint splatter drawn as a collection of colored dots. Most of the paint clusters around a central location, but some dots do splatter out towards the edges. Can you use a normal distribution of random nbers to generate the locations of the dots? Can you also use a normal distribution of random numbers to generate a color palette?
I decided the paint wouldn’t drip, that it would be thicker than the white dots from the preceding example, but that it would mix when successive colours lay on top of one another. I’m not sure what kind of paint gun splatters this way. These decisions meant I could re-use the preceding example, but make it scatter in two dimensions, and in varying colours.
In Processing
I wrapped calls to generator.normal()
in another function, so I could easily call it with fixed parameters for the various uses made of it: screen width/height or palette size for the mean, a spreading factor for the standard deviation.
import java.util.Random;
Random generator;
void setup() {
size(640,360);
generator = new Random();
}
float gaussian( float mu, float sigma ) {
float num = (float) generator.nextGaussian();
float ret = sigma * num + mu;
return ret;
}
void draw() {
noStroke();
int red = (int) gaussian( 125, 60 );
int green = (int) gaussian( 125, 60 );
int blue = (int) gaussian( 125, 60 );
fill( red, green, blue, 50 );
float x = gaussian( 320, 60 );
float y = gaussian( 180, 60 );
ellipse( x, y, 8, 8 );
}
In JavaScript
For this, I felt the circle function factory was getting in the way, so I cut it out and put the whole circle drawing algorithm back into draw()
. This is, in hindsight, probably better for such a short example. The book overall teaches Object-oriented design approaches to its problems, so the use of functional patterns isn’t really in-keeping.
(function( Frames, Random ){
var canvasWidth = 640;
var canvasHeight = 360;
var context;
function setup () {
context = Frames.setupCanvas( canvasWidth, canvasHeight );
}
function draw () {
var x = parseInt( Random.normal( canvasWidth / 2, 60 ), 10 );
var y = parseInt( Random.normal( canvasHeight / 2, 60 ), 10 );
var red = parseInt( Random.normal( 125, 40 ), 10 );
var green = parseInt( Random.normal( 125, 40 ), 10 );
var blue = parseInt( Random.normal( 125, 40 ), 10 );
context.beginPath();
context.fillStyle = 'rgba('+ red +','+ green +','+ blue +',0.1 )';
context.arc( x, y, 8, 0, 2 * Math.PI );
context.fill();
}
Frames.go( setup, draw );
})( Frames, new Random() );
MDN suggests there is an ellipse()
on MDN method on the CanvasRenderingContext2D. It works in the live demos on the documentation page, in both Safari and Chrome. I couldn’t get this working. I’m not sure why. The other option is to use arc()
on MDN, which does just fine and takes fewer parameters - although for circles the start- and end- angle params are redundant.
If I’d given myself more time, I’d have found a nicer way to generalise getting Gaussian integers, rather than use this repeatedly:
var x = parseInt( Random.normal( canvasWidth / 2, 60 ), 10 );