School productivity

Our Software Design lessons at school have always been competitive, classmates competing for the shortest code, the most interesting project ideas etc. The fruits of one of these unofficial ‘competitions’ though, I think is worth a write-up; the JavaScript Challenge. The challenge was to create animated, interesting graphics in JavaScript using only ASCII characters. Classmates probably wouldn’t be too happy if I stuck their submissions online, hence I’ll only supply my own. With all due modesty though, the nearest competitor was a white spiral generator, which didn’t animate – so you’re not missing out on much :P

take 1. a sine wave

View it here. Quite simple (in comparison), the page consists of a mono-space grid of spaces and digits iterating horizontally using the sine function and a mutated delta value for animation.

Take 2. A Cube

View it here. A big step up, this demonstration uses projection transformation, 3D mathematics and matrices to draw the illusion of a rotating cube.

Basically, the way it works is that each of the cube’s vertices is defined in a stationary position (x, y, z), all stored in a larger array. Each iteration, an angular transform is performed on every point, followed by a distance transformation, then the result displayed on the screen and vertices interpolated between using Bresenham’s line algorithm.

// Perform an angular matrix transform (aint that fun to say :D ), angles in radians.
function AngularTransform( result, original, heading, pitch, bank ) {
 
    for( var i = 0; i != result.length; ++i ) {
 
        // Yes, this is the simplest possible way of doing it ;) -- besides maybe a massive lookup table
 
        result[i].x = (Math.cos(heading)*Math.cos(bank)+Math.sin(heading)*Math.sin(pitch)*Math.sin(bank))*original[i].x +
 
                      (-Math.cos(heading)*Math.sin(bank)+Math.sin(heading)*Math.sin(pitch)*Math.cos(bank))*original[i].y +
 
                      (Math.sin(heading)*Math.cos(pitch))*original[i].z;
 
        result[i].y = (Math.sin(bank)*Math.cos(pitch))*original[i].x +
 
                      (Math.cos(bank)*Math.cos(pitch))*original[i].y +
 
                      (-Math.sin(pitch))*original[i].z;
 
        result[i].z = (-Math.sin(heading)*Math.cos(bank)+Math.cos(heading)*Math.sin(pitch)*Math.sin(bank))*original[i].x +
 
                      (Math.sin(bank)*Math.sin(heading)+Math.cos(heading)*Math.sin(pitch)*Math.cos(bank))*original[i].y +
 
                      (Math.cos(heading)*Math.cos(pitch))*original[i].z;
 
    }
 
    return result;
 
}

Above is the code for the angular transform. It accepts 3 angles in radians, transforming an input vector around the origin by these angles, and then returning the result. The core of the function looks complex, but it is a simply the result of the input vector multiplied by the equation for Euler angles in matrix form.

var px = Math.floor((shape[i].x + offset.x) / (shape[i].z+Z_OFF)*V_DIST) + SIZE_Y/2;
var py = Math.floor((shape[i].y + offset.y) / (shape[i].z+Z_OFF)*V_DIST) + SIZE_X/2;

And those 2 lines perform the distance transformation (generate the view projection), which basically means that objects with a greater Z coordinate look smaller, as they do in reality – A simple inverse relationship between Z and the size of X and Y in relation to zero.

Take 3. A cubefield

View it here. The last piece of graphic JS I worked on, it’s basically a simple extension of the code for the display of a single cube. An array of cubes randomly spawn, Z coordinates decreasing towards the screen until zero and then the cubes respawning. To save processing time, all of the cubes use the same angular transform; hence they are all rotated by the same angle at the same time – but they use a different position transformation and so appear at different positions.

The end?

The code in each of these examples is reasonably commented, and I invite anyone who’s curious to look at all of the page sources and maybe even alter them into something new.. An in-browser 3D space-invaders clone using only ASCII characters would be interesting *nudge nudge.