Skip navigation.

exploreopera

| Help

Sign up | Help

The Roost

Programming, palaver, puffins!

Guys, I am telling you... Dragonland is the best band ever! Srsly!

, , , ...

Why haven't more people heard about this band? Dragonland is a power metal band which started out making music much like the epic (and sometimes cheesy) fantasy-related tracks of Rhapsody of Fire (formerly Rhapsody). However, Dragonland began moving away from that genre with their third album Starfall which contains a lot of great tracks. Starfall suffers a little because, with a few exceptions, most of the tracks contain a similar tempo, and the mainstay of the power metal genre - the blazing guitars - are downplayed in favour of additional synth.

However, their most recent album, Astronomy, rocks so hard! There is plenty of variety here folks, from ballad to death metal vocals; from epic "movie-soundtrack" instrumentals to straight power metal awesomeness. Who would have thought that a track titled "Supernova" would actually be about a star getting blown to smithereens? :D Friends, this album appeals not only to the power metal fan, but the astronomy nerd in us all!

Check out their site and buy their music!
http://www.dragonland.se/

I am definitely going to one of their concerts if they ever come to Canada, but they'll never do much touring unless they get the popularity they deserve. So come on, guys! Spread the word! Dragonland! :up: :yes:

PlanetWerks performance tweaks

, , ,

A great deal of work has gone into making the PlanetWerks widget run as smoothly as it possibly can. I just thought I'd share a few tweaks which have gone into the latest version (released later this week hopefully) and a few earlier versions.

Fully Matrix Driven
Up until version 2.1, the transformations performed on the orbit ellipses to orient them correctly in 3D space were done by three sets of discrete parametric equations. This simply means that once a planetoid was placed on the orbit ellipse, it was rotated once, then rotated again to incline it to the ecliptic, then rotated a third time to point the periapsis in the correct direction. That's three separate rotations before each planetoid can be displayed.

Using matrix multiplication, it is possible to combine all of these rotations into a single rotation; and since the orbit of each planetoid does not change within PlanetWerks (they do over time IRL), I can do all that preparation before the animation starts and then apply one rotation per animation frame.

Reducing Calculation Load
I spent a lot of time reviewing what actually gets executed during a single frame of animation, and trying to cut out any calculations which could be pre-calculated for speed, or algorithms which might be faster in a different form.

First of all, when I switched to the matrix driven rotation method, I had a nice function which performed the rotation from a matrix upon a vector (an [x, y, z] point on the ellipse):

Math.matrixVector = function(m, v) {
  for (var x = 0, output = []; x < m.length; x++) {
    output[x] = 0;
    for (var y = 0; y < m[0].length; y++)
      output[x] += m[x][y] * v[y];
  }
};

This slick function would handle matrices and vectors of any size, but it also has a lot of loop overhead. Since I was only ever multiplying 3x3 matrices with 3-coordinate vectors, I unrolled all the loops and calculated each term manually.

Math.matrixVector = function(m, v) {
  return [
    m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
    m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
    m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2]
  ];
};

This method turned out to be considerably faster, which is a good thing since this function is executed for every visible planetoid for every frame of animation in PlanetWerks!

Another thing I tried to remove was the creation of unnecessary variables. Creating a new variable using "var" can be handy, but it also entails a small overhead for both allocating new memory and garbage collection once it goes out of scope.

Since PlanetWerks 2.0, the user rotation of the system is driven by quaternions, which are simple 3 coordinate vectors with an added orientation coordinate that keeps them stable after many sequential rotations. Every time the user rotated the system, the rotation function would fire and update a global rotation matrix. When a key is held down, this rotation function can be called many times per second, so it is a good thing to optimise here. In version 2.1, the bottom section of this function looked like this:

var angle = 2 * this.math.acos(this.quat[0]), c = this.math.cos(angle), s = this.math.sin(angle), C = 1 - c;
var scale = this.math.sqrt(this.quat[1] * this.quat[1] + this.quat[2] * this.quat[2] + this.quat[3] * this.quat[3]);
var x = this.quat[1] / scale, y = this.quat[2] / scale, z = this.quat[3] / scale;

var xs = x * s, ys = y * s, zs = z * s;
var xC = x * C, yC = y * C, zC = z * C;
var xyC = x * yC, yzC = y * zC, zxC = z * xC;

this.rota = [
  x * xC + c, xyC - zs, zxC + ys,
  xyC + zs, y * yC + c, yzC - xs,
  zxC - ys, yzC + xs, z * zC + c
];
this.moved = true;

Look at all those created variables! It was surely reducing the number of duplicated calculations, but it was also causing memory allocation overhead. I did some benchmarking and found that it executed moderately faster once unrolled and calculated manually:

var angle = 2 * this.math.acos(this.quat[0]), c = this.math.cos(angle), s = this.math.sin(angle), C = 1 - c;
var scale = this.math.sqrt(this.quat[1] * this.quat[1] + this.quat[2] * this.quat[2] + this.quat[3] * this.quat[3]);
var x = this.quat[1] / scale, y = this.quat[2] / scale, z = this.quat[3] / scale;

this.rota = [
  [x * x * C + c, x * y * C - z * s, z * x * C + y * s],
  [x * y * C + z * s, y * y * C + c, y * z * C - x * s],
  [z * x * C - y * s, y * z * C + x * s, z * z * C + c]
];

More could probably be done here to reduce the number of new variables created, but readability is already getting strained to the limit.

Another thing I discovered is that cleverly combining assignments can slow execution speed by a very small amount, but still significantly. I benchmarked the following two groups of statements in Opera 9.5b2 and found that the latter, while not always the fastest, consistently executed about 5% faster than the former group:

Slower, compact, not very readable...
this.appHeight2 = (this.math.round(tbx.ratio * (this.appWidth = this.math.max(1, this.appWidth)))) / 2 + 10;
this.appWidth2 = this.math.round(this.appWidth) / 2 + 10;
this.appAbsX = this.math.abs(this.appX = this.math.atan2(tbx.x[1] / 2, tdtbxz) * this.factor);
this.appAbsY = this.math.abs(this.appY = this.math.atan2(tbx.y[1] / 2, tdtbxz) * this.factor);
this.appPosX = this.math.round(this.appX + this.width2);
this.appPosY = this.math.round(this.appY + this.height2);

Faster, longer, but also more readable...
this.appWidth = this.math.max(1, this.appWidth);
this.appHeight = this.math.round(tbx.ratio * this.appWidth);
this.appWidth = this.math.round(this.appWidth);
this.appHeight2 = this.appHeight / 2 + 10;
this.appWidth2 = this.appWidth / 2 + 10;
this.appX = this.math.atan2(tbx.x[1] / 2, tdtbxz) * this.factor;
this.appY = this.math.atan2(tbx.y[1] / 2, tdtbxz) * this.factor;
this.appAbsX = this.math.abs(this.appX);
this.appAbsY = this.math.abs(this.appY);
this.appPosX = this.math.round(this.appX + this.width2);
this.appPosY = this.math.round(this.appY + this.height2);

Binary Flags
Some functions in PlanetWerks called for systems of flags which directed the main animation function to perform in a certain way. Previously I was using a separate variable storing a Boolean for each flag. For example, there were separate flags set if the system had "moved", been "zoomed" or "rotated". In 2.2, these three flags have been combined into a single binary value with places set for each flag. 001 means the system has moved, 010 means the system has been zoomed, and 100 means the system has been rotated. Using Javascript's binary math & operator, it is easy to check whether one or multiple values have been set. If I want to know if the zoomed bit has been set, I simply "and" the set with 2:

if (this.flags & 2) { /* system has been zoomed */ }

Since 2 in binary is 010, the & operator means, if a bit is equal to 1 in both variables, set it in the result. So say the system had been rotated and zoomed: 110 & 010 = 010. And if the system had been rotated, moved, but not zoomed: 101 & 010 = 000. You can even get more complicated; suppose I wanted to know if the system had been zoomed or rotated. I would simply "and" the flag set with a combination of the zoomed and rotated flags, in other words: 011. Now if either the zoomed or rotated flag is set in the flag set, we will get a true result from the & operator.

I really don't know if this is faster than having separate variables for each, but having one entry in the lookup table instead of three has got to count for something :wink:


That's it for now. Hopefully I will get PlanetWerks 2.2 out there before I head off on holidays! :smile:

Coming soon... PlanetWerks 2.2 :)

, , , ...

Here is a sneak peek at the latest PlanetWerks version:




Should be uploaded sometime next week! :D Stay tuned... :spock:

Ajax - There's nothing to it!

, , ,

These days, when I have the pleasure to meet people who at first seem as enthusiastic about web application design as I, more often than not the question eventually arises: "So, what framework are you using?" Is everyone using some kind of Ajax framework these days?

Usually I reply that I don't use any framework; I deal with Ajax on a fundamental level. It's because of this level of intimacy that I am able to code a complete Ajax exchange application from scratch without looking up anything. Well, that's not entirely true :smile: I do use a pre-coded function to create the XMLHTTPRequest object for me, mostly because of the convoluted cross-browser rules required to do so. It's everything else I can code from scratch :wink:

Now by this post I am not aiming to bash frameworks. They are perfectly fine if you want to avoid the minutia of customising a request each time you need one. My purpose here is mainly to cut away the hype-fueled framework-veneer which implies Ajax is a complicated, arcane mess of which only an API can possibly make sense. The truth is, Ajax is dead simple.

The hardest part comes first: creating the XMLHTTPRequest object. It's only hard because you need to do it a different way in Internet Explorer than the standard way in Opera and Firefox. This is the only part of the code that I need to copy and paste, because it works just the way it is and never needs to change:

/* ********************************************************
 * Modified from Jim Ley's implementation at jibbering.com
 *   http://www.jibbering.com/2002/4/httprequest.html
 *
 */
function getHTTPObject() {
  var xmlhttp = false;
  /*@cc_on
  @if (@_jscript_version >= 5)
    try {
      xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e) { xmlhttp = false; }
    }
  @end @*/

  if (!xmlhttp) {
    try {
       xmlhttp = new XMLHttpRequest();
    } catch (e) {
      try {
        xmlhttp = window.createRequest();
      } catch (e) { xmlhttp = false; }
    }
  }
  return xmlhttp;
}

Examine the code if you will, but you don't need to know how it works. All you need to know is that now you can create the Ajax request object (XMLHTTPRequest or XHR) properly in virtually all modern browsers simply by calling the function.

var ajax = getHTTPObject();

So let's start with this and build on it, creating a valid Ajax application - at least the client side of things, anyway. Now that we have our XHR object, we need to give it a URI we want it to open.

var ajax = getHTTPObject();
ajax.open("GET", "/server-side-app.php", true);

The first argument of the open() method is the type of request we want to make. Usually GET is okay; it's the type of request your browser sends when you do a search on Google, for example. You'll notice you get sent to a location with a lot of variables in the address bar. This is how data gets passed using the GET method: via the URI. In our little example, we are going to assume the server-side script will send us info without us sending any special variables. We are just going to request the plain page and capture whatever output it sends back.

The second argument is the URI itself. It's relative to the document in which the script is included, and not to the javascript file if it's external.

The final argument tells the client whether or not this request should be asynchronous. Even though asynchronous is the A in Ajax, don't be alarmed at the ten-dollar terminology. It just means that the client should continue executing futher statements without waiting around for the request to finish. This keeps Ajax applications snappy, by keeping them from "freezing" while a request is taking place.

Now that we've done that, we need to put a function in place to handle the data we're going to get back:

var ajax = getHTTPObject();
ajax.open("GET", "/server-side-app.php", true);
ajax.onreadystatechange = function() {
  if (ajax.readyState == 4) {
    // Success
  }
};

During a request, an XHR object goes through several "ready state"s, finally ending up at 4, meaning the request has completed - but not necessarily successfully! Once we reach a readyState of 4, we know that there will be no futher modification to our XHR object so we can begin to extract data from it.

Now, even though the X in Ajax stands for XML, you can ignore that completely and just use the reponseText property. This returns a simple text string containing the entire reponse. For now, we'll just alert it.

var ajax = getHTTPObject();
ajax.open("GET", "/server-side-app.php", true);
ajax.onreadystatechange = function() {
  if (ajax.readyState == 4) {
    alert(ajax.responseText);
  }
};

So, we've set up the target and the response handler. All we need to do now is send it. We do this with one final statement:

var ajax = getHTTPObject();
ajax.open("GET", "/server-side-app.php", true);
ajax.onreadystatechange = function() {
  if (ajax.readyState == 4) {
    alert(ajax.responseText);
  }
};
ajax.send(null);

That's it! This script will request data from the /server-side-app.php page on your server and alert the reponse it recieves. It's up to you to take it further than this. For the most part, all you need to do is do something useful with the response, rather than just alerting it.

Another thing you'd likely do is put this code inside a function which is triggered by some user action, like clicking a checkbox, or making some other kind of selection.

One final thing to note concerns POST requests. If you encounter the situation where you need to send a great deal of data to the server, putting it all in the URI may not be feasible. Some browsers have a 1kB limit on URI sizes, and if your data is larger than that, you can kiss some of it goodbye. The solution is to use a POST request, which sends the data in a separate header which can be any length. We'll just modify our previous code like so:

var ajax = getHTTPObject();
ajax.open("POST", "/server-side-app.php", true);
ajax.onreadystatechange = function() {
  if (ajax.readyState == 4) {
    alert(ajax.responseText);
  }
};
ajax.send("myData");

So we changed "GET" to "POST" and put the data we're sending as the argument to the send() method. Easy right? Well, you'll be unpleasantly surprised when your keen-eyed swaps don't work at all. Sure, the request will get sent, but if you examine your server-side page, you'll find that it doesn't recieve any data. How odd!

The solution to this is an additional header, one which explicitly tells the remote server that there is POST data coming with the request and the server should look for it. Our code will now look like:

var ajax = getHTTPObject();
ajax.open("POST", "/server-side-app.php", true);
ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
ajax.onreadystatechange = function() {
  if (ajax.readyState == 4) {
    alert(ajax.responseText);
  }
};
ajax.send("myData");

With this header, your POST requests will now work as expected.

That's all there is to it; just build on those code seeds and you'll be forging your own Ajax applications in no time. Ajax, who needs a framework when there's just nothing to it? :D

I'm finishing this up at 3am, so if you find any errors, please let me know, eh?
May 2008
SMTWTFS
April 2008June 2008
123
45678910
11121314151617
18192021222324
25262728293031