text: /**
   * Math class library, utility functions and constants.
   * 
   * Copyright (C) Kevin Roast 2010
   * http://www.kevs3d.co.uk/dev
   * email: kevtoast at yahoo.com
   * twitter: @kevinroast
   * 
   * 30/04/09 First version
   * 
   * I place this code in the public domain - because it's not rocket science
   * and it won't make me any money, so do whatever you want with it, go crazy.
   * I would appreciate an email or tweet if you do anything fun with it!
   */
  
  var RAD = Math.PI/180.0;
  var PI = Math.PI;
  var TWOPI = Math.PI*2;
  var ONEOPI = 1.0 / Math.PI;
  var PIO2 = Math.PI/2;
  var PIO4 = Math.PI/4;
  var PIO8 = Math.PI/8;
  var PIO16 = Math.PI/16;
  var PIO32 = Math.PI/32;
  var Rnd = Math.random;
  var Sin = Math.sin;
  var Cos = Math.cos;
  var Sqrt = Math.sqrt;
  var Floor = Math.floor;
  var Atan2 = Math.atan2;
  var Ceil = Math.ceil;
  
  
  /**
   * Return a random integer value between low and high values
   */
  function randomInt(low, high)
  {
     return ~~(Rnd() * (high - low + 1) + low);
  }
  
  
  function weightedRandom(weight)
  {
     var input = Rnd();
     if (input < 0.5) return 1 - Math.pow(1 - input, weight !== undefined ? weight : 2) / 2; 
     return 0.5 + Math.pow((input - 0.5) * 2, weight !== undefined ? weight : 2) / 2; 
  }
  
  
  /**
   * Calculate normal vector.
   * 
   * First calculate vectors from 3 points on the poly:
   * Vector 1 = Vertex B - Vertex A
   * Vector 2 = Vertex C - Vertex A
   */
  function calcNormalVector(x1, y1, z1, x2, y2, z2)
  {
     return new Vector3D(
        (y1 * z2) - (z1 * y2),
        -((z2 * x1) - (x2 * z1)),
        (x1 * y2) - (y1 * x2) ).norm();
  }
  
  
  /**
   * Utility to set up the prototype, constructor and superclass properties to
   * support an inheritance strategy that can chain constructors and methods.
   * Static members will not be inherited.
   * 
   * @method extend
   * @static
   * @param {Function} subc   the object to modify
   * @param {Function} superc the object to inherit
   * @param {Object} overrides  additional properties/methods to add to the
   *                            subclass prototype.  These will override the
   *                            matching items obtained from the superclass.
   */
  function extend(subc, superc, overrides)
  {
     var F = function() {}, i;
     F.prototype = superc.prototype;
     subc.prototype = new F();
     subc.prototype.constructor = subc;
     subc.superclass = superc.prototype;
     if (superc.prototype.constructor == Object.prototype.constructor)
     {
        superc.prototype.constructor = superc;
     }
     
     if (overrides)
     {
        for (i in overrides)
        {
           if (overrides.hasOwnProperty(i))
           {
              subc.prototype[i] = overrides[i];
           }
        }
     }
  }
  
  
  function isArray(obj)
  {
     return (obj.constructor.toString().indexOf("Array") !== -1);
  }
  
  
  /**
   * Vector (or Point) structure class - all fields are public.
   * 
   * @class Vector
   */
  (function()
  {
     Vector = function(x, y)
     {
        this.x = x;
        this.y = y;
        
        return this;
     };
     
     Vector.prototype =
     {
        /**
         * X coordinate
         *
         * @property x
         * @type number
         */
        x: 0,
  
        /**
         * Y coordinate
         *
         * @property y
         * @type number
         */
        y: 0,
        
        clone: function()
        {
           return new Vector(this.x, this.y);
        },
        
        set: function(v)
        {
           this.x = v.x;
           this.y = v.y;
           return this;
        },
        
        add: function(v)
        {
           this.x += v.x;
           this.y += v.y;
           return this;
        },
  
        nadd: function(v)
        {
           return new Vector(this.x + v.x, this.y + v.y);
        },
        
        sub: function(v)
        {
           this.x -= v.x;
           this.y -= v.y;
           return this;
        },
        
        nsub: function(v)
        {
           return new Vector(this.x - v.x, this.y - v.y);
        },
        
        dot: function(v)
        {
           return this.x * v.x + this.y * v.y;
        },
        
        length: function()
        {
           return Sqrt(this.x * this.x + this.y * this.y); 
        },
        
        distance: function(v)
        {
           var xx = this.x - v.x;
           var yy = this.y - v.y;
           return Sqrt(xx * xx + yy * yy); 
        },
        
        theta: function()
        {
           return Atan2(this.y, this.x);
        },
        
        thetaTo: function(vec)
        {
           // calc angle between the two vectors
           var v = this.clone().norm();
           var w = vec.clone().norm();
           return Math.acos(v.dot(w));
        },
        
        thetaTo2: function(vec)
        {
           return Atan2(vec.y, vec.x) - Atan2(this.y, this.x);
        },
        
        norm: function()
        {
           var len = this.length();
           this.x /= len;
           this.y /= len;
           return this;
        },
        
        nnorm: function()
        {
           var len = this.length();
           return new Vector(this.x / len, this.y / len);
        },
        
        rotate: function(a)
        {
        	var ca = Cos(a);
        	var sa = Sin(a);
        	with (this)
        	{
        		var rx = x*ca - y*sa;
        		var ry = x*sa + y*ca;
        		x = rx;
        		y = ry;
        	}
        	return this;
        },
        
        nrotate: function(a)
        {
        	var ca = Cos(a),
        	    sa = Sin(a);
           return new Vector(this.x*ca - this.y*sa, this.x*sa + this.y*ca);
        },
        
        invert: function()
        {
           this.x = -this.x;
           this.y = -this.y;
           return this;
        },
        
        ninvert: function()
        {
           return new Vector(-this.x, -this.y);
        },
        
        scale: function(s)
        {
           this.x *= s; 
           this.y *= s;
           return this;
        },
        
        nscale: function(s)
        {
           return new Vector(this.x * s, this.y * s);
        },
        
        scaleTo: function(s)
        {
           var len = s / this.length();
           this.x *= len;
           this.y *= len;
           return this;
        },
        
        nscaleTo: function(s)
        {
           var len = s / this.length();
           return new Vector(this.x * len, this.y * len)
        }
     };
  })();
  
  
  /**
   * Vector3D structure class - all fields are public.
   * 
   * @class Vector3D
   */
  (function()
  {
     Vector3D = function(x, y, z)
     {
        this.x = x;
        this.y = y;
        this.z = z;
        
        return this;
     };
     
     Vector3D.prototype =
     {
        /**
         * X coordinate
         *
         * @property x
         * @type number
         */
        x: 0,
  
        /**
         * Y coordinate
         *
         * @property y
         * @type number
         */
        y: 0,
        
        /**
         * Z coordinate
         *
         * @property z
         * @type number
         */
        z: 0,
        
        clone: function()
        {
           return new Vector3D(this.x, this.y, this.z);
        },
        
        set: function(v)
        {
           this.x = v.x;
           this.y = v.y;
           this.z = v.z;
           return this;
        },
        
        add: function(v)
        {
           this.x += v.x;
           this.y += v.y;
           this.z += v.z;
           return this;
        },
        
        sub: function(v)
        {
           this.x -= v.x;
           this.y -= v.y;
           this.z -= v.z;
           return this;
        },
        
        dot: function(v)
        {
           return this.x * v.x + this.y * v.y + this.z * v.z;
        },
        
        cross: function(v)
        {
           return new Vector3D(this.y*v.z - this.z*v.y, this.z*v.x - this.x*v.z, this.x*v.y - this.y*v.x);
        },
        
        length: function()
        {
           return Sqrt(this.x * this.x + this.y * this.y + this.z * this.z); 
        },
        
        distance: function(v)
        {
           var xx = this.x - v.x;
           var yy = this.y - v.y;
           var zz = this.z - v.z;
           return Sqrt(xx * xx + yy * yy + zz * zz); 
        },
        
        thetaTo: function(v)
        {
           // Expanded version of: Atan2(this.cross(v).length(), this.dot(v));
           // to avoid intermediate object creation (about 30% faster..!)
           
           // calculate cross product
           var x = this.y*v.z - this.z*v.y, 
               y = this.z*v.x - this.x*v.z,
               z = this.x*v.y - this.y*v.x;
           // atan2 of length of cross product with dot product of supplied vector
           return Atan2(Sqrt(x * x + y * y + z * z), this.dot(v));
        },
        
        thetaTo2: function(v)
        {
           return Math.acos(this.dot(v) / (Sqrt(this.x * this.x + this.y * this.y + this.z * this.z) * Sqrt(v.x * v.x + v.y * v.y + v.z * v.z)));
        },
        
        norm: function()
        {
           var len = this.length();
           this.x /= len;
           this.y /= len;
           this.z /= len;
           return this;
        },
        
        scale: function(s)
        {
           this.x *= s; 
           this.y *= s;
           this.z *= s;
           return this;
        }
     };
  })();
  
  
  /**
   * Image Preloader class. Executes the supplied callback function once all
   * registered images are loaded by the browser.
   * 
   * @class Preloader
   */
  (function()
  {
     Preloader = function()
     {
        this.images = new Array();
        return this;
     };
     
     Preloader.prototype =
     {
        /**
         * Image list
         *
         * @property images
         * @type Array
         */
        images: null,
        
        /**
         * Callback function
         *
         * @property callback
         * @type Function
         */
        callback: null,
        
        /**
         * Images loaded so far counter
         */
        counter: 0,
        
        /**
         * Add an image to the list of images to wait for
         */
        addImage: function addImage(img, url)
        {
           var me = this;
           img.url = url;
           // attach closure to the image onload handler
           img.onload = function()
           {
              me.counter++;
              if (me.counter === me.images.length)
              {
                 // all images are loaded - execute callback function
                 me.callback.call(me);
              }
           };
           this.images.push(img);
        },
        
        /**
         * Load the images and call the supplied function when ready
         */
        onLoadCallback: function onLoadCallback(fn)
        {
           this.counter = 0;
           this.callback = fn;
           // load the images
           for (var i=0, j=this.images.length; i<j; i++)
           {
              this.images[i].src = this.images[i].url;
           }
        }
     };
  })();