/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 * 
 */

// =============================================================================
// FIELDKIT MAIN CLASS
// =============================================================================
fk = {
	// -- Inheritance Support --------------------------------------------------
	// this code was adapted from John Resig's Simple JavaScript inheritance article
	// http://ejohn.org/blog/simple-javascript-inheritance
	init: function() {
	  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
	  // The base Class implementation (does nothing)
	  this.Class = function(){};

	  // Create a new Class that inherits from this class
	  fk.Class.extend = function(prop) {
	    var _super = this.prototype;

	    // Instantiate a base class (but only create the instance,
	    // don't run the init constructor)
	    initializing = true;
	    var prototype = new this();
	    initializing = false;

	    // Copy the properties over onto the new prototype
	    for (var name in prop) {
	      // Check if we're overwriting an existing function
	      prototype[name] = typeof prop[name] == "function" && 
	        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
	        (function(name, fn){
	          return function() {
	            var tmp = this._super;

	            // Add a new ._super() method that is the same method
	            // but on the super-class
	            this._super = _super[name];

	            // The method only need to be bound temporarily, so we
	            // remove it when we're done executing
	            var ret = fn.apply(this, arguments);        
	            this._super = tmp;

	            return ret;
	          };
	        })(name, prop[name]) :
	        prop[name];
	    }

	    // The dummy class constructor
	    function Class() {
	      // All construction is actually done in the init method
	      if ( !initializing && this.init )
	        this.init.apply(this, arguments);
	    }

	    // Populate our constructed prototype object
	    Class.prototype = prototype;

	    // Enforce the constructor to be what we expect
	    Class.constructor = Class;

	    // And make this class extendable
	    Class.extend = arguments.callee;

	    return Class;
	  };
	},
	
	// -- Logging --------------------------------------------------------------
	info: function() {
		if(window.console) {
			var s = ""
			for(var i=0; i<arguments.length; i++)
				s += arguments[i] +' '
				
			window.console.info(s)
		}
	},
	
	warn: function() {
		if(window.console) {
			var s = "WARNING: "
			for(var i=0; i<arguments.length; i++)
				s += arguments[i] +' '
				
			window.console.warn(s)
		}
	},
	
	error: function() {
		if(window.console) {
			var s = "WARNING: "
			for(var i=0; i<arguments.length; i++)
				s += arguments[i] +' '
				
			window.console.error(s)
		}
	},
	
	// -- Utilities ------------------------------------------------------------
	
	// decimal to hex
	dec2hex: function(d) {
		return d.toString(16);
	},
	 
	// hex to decimal
	hex2dec: function(h) {
		return parseInt(h,16);
	},
}

// define subpackages
fk.particle = {}
fk.particle3d = {}

// initialize the kit
fk.init()
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: July 08, 2009
 */

// =============================================================================
// COLOUR CLASS
//
// For more info about HSV and HSL see
// http://en.wikipedia.org/wiki/HSL_and_HSV
//
// Uses RGB <> HSV, HSL conversion code from Michael Jackson
// http://www.mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
//
// =============================================================================
fk.Colour = fk.Class.extend({
	
	r:0, g:0, b: 0,

	init: function() {
		// parse arguments
		if(arguments.length == 3) {
			this.set(arguments[0], arguments[1], arguments[2])
			
		} else if(arguments.length == 1) {
			this.set(arguments[0])
		}
	},
	
	// -- Getters & Setters ------------------------------------------------------
	// Sets the R,G,B values of this Colour object
	// Can take either another Colour object, an RGB(r,g,b) String or three seperate R,G,B values as arguments
	set: function() {
		if(arguments.length == 1) {
			var arg = arguments[0]
			
			if(typeof(arg) == 'string') {
				// arg is a 6 character hex string
				if (arg.length == 6) {
					this.fromHex(arg);
				}
				// arg is in rgb(r,g,b) form
				else {
					var parts = arg.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/)
					this.red(parts[1]);
					this.green(parts[2]);
					this.blue(parts[3]);
				}
				
			} else if(typeof(arg) == 'object') {
				this.red(arg.r);
				this.green(arg.g);
				this.blue(arg.b)
				
			} else {
				fk.warn("fk.Colour.set: Invalid argument ("+ arg +" <"+ typeof(arg) +">)");
			}
		} else if(arguments.length == 3) {			
			this.red( arguments[0] );
			this.green( arguments[1] );
			this.blue( arguments[2] );
		}
		
		return this;
	},
	
	// sets/ gets the RED component
	red: function(r) {
		if(arguments.length == 1) this.r = this.parseComponent(arguments[0]);
		return this.r;
	},

	// sets/ gets the GREEN component	
	green: function(g) {
		if(arguments.length == 1) this.g = this.parseComponent(arguments[0]);
		return this.g;
	},
	
	// sets/ gets the BLUE component
	blue: function(b) {
		if(arguments.length == 1) this.b = this.parseComponent(arguments[0]);
		return this.b;
	},

	// sets/ gets the HSL HUE value
	hue: function() {
		var hsv = this.toHSV();
		if(arguments.length == 1) {
			hsv[0] = arguments[0];
			this.fromHSV(hsv[0],hsv[1],hsv[2]);
		}
		return hsv[0];
	},
	
	// sets/ gets the HSL SATURATION value
	saturation: function() {
		var hsv = this.toHSV()
		if(arguments.length == 1) {
			hsv[1] = arguments[0]
			this.fromHSV(hsv[0],hsv[1],hsv[2])
		}
		return hsv[1]
	},
	
	// sets/ gets the hsv VALUE/ BRIGHTNESS value
	value: function() {
		var hsv = this.toHSV()
		if(arguments.length == 1) {
			hsv[2] = arguments[0]
			this.fromHSV(hsv[0],hsv[1],hsv[2])
		}
		return hsv[2]
	},
	
	// -- Convert To ... Methods -------------------------------------------------
	// Converts this Colour to HSL
	toHSL: function() {
		var _r = this.r / 255
		var _g = this.g / 255
		var _b = this.b / 255
		
	    var max = Math.max(_r, _g, _b), min = Math.min(_r, _g, _b)
	    var h, s, l = (max + min) / 2
	
	    if(max == min){
	        h = s = 0 // ach_romatic
	    }else{
	        var d = max - min
	        s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
	        switch(max){
	            case _r: h = (_g - _b) / d + (_g < _b ? 6 : 0); break
	            case _g: h = (_b - _r) / d + 2; break
	            case _b: h = (_r - _g) / d + 4; break
	        }
	        h /= 6;
	    }
	
	    return [h, s, l];
	},
	
	// Converts this Colour to HSV
	toHSV: function() {
		var _r = this.r / 255
		var _g = this.g / 255
		var _b = this.b / 255
		
		var max = Math.max(_r, _g, _b), min = Math.min(_r, _g, _b)
		var h, s, v = max

		var d = max - min
		s = max == 0 ? 0 : d / max

		if(max == min) {
			h = 0; // ach_romatic
		} else {
			switch(max){
				case _r: h = (_g - _b) / d + (_g < _b ? 6 : 0); break
				case _g: h = (_b - _r) / d + 2; break
				case _b: h = (_r - _g) / d + 4; break
			}
			h /= 6;
		}

		return [h, s, v]
	},
	
	// returns this colour as a hexadecimal string
	toHex: function() {
		return fk.dec2hex(this.toInt());
	},
	
	// returns this colour as a single integer
	toInt: function() {
		return (this.r << 16) + (this.g << 8) + this.b;
	},
	
	// -- Convert From ... Methods -----------------------------------------------
	// Sets this Colour to the colour defined with the given HSL values.
	fromHSL: function(h, s, l) {
		if(s == 0) {
	        r = g = b = l // achromatic
	    } else {
	        function hue2rgb(p, q, t){
	            if(t < 0) t += 1
	            if(t > 1) t -= 1
	            if(t < 1/6) return p + (q - p) * 6 * t
	            if(t < 1/2) return q
	            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6
	            return p;
	        }
	
	        var q = l < 0.5 ? l * (1 + s) : l + s - l * s
	        var p = 2 * l - q
	        r = hue2rgb(p, q, h + 1/3)
	        g = hue2rgb(p, q, h)
	        b = hue2rgb(p, q, h - 1/3)
	    }
		return this
	},
	
	// Sets this Colour to the colour defined with the given HSV values.
	fromHSV: function(h,s,v) {
		
		var i = Math.floor(h * 6);
	    var f = h * 6 - i;
	    var p = v * (1 - s);
	    var q = v * (1 - f * s);
	    var t = v * (1 - (1 - f) * s);
	
	    switch(i % 6){
	        case 0: this.r = v, this.g = t, this.b = p; break
	        case 1: this.r = q, this.g = v, this.b = p; break
	        case 2: this.r = p, this.g = v, this.b = t; break
	        case 3: this.r = p, this.g = q, this.b = v; break
	        case 4: this.r = t, this.g = p, this.b = v; break
	        case 5: this.r = v, this.g = p, this.b = q; break
	    };

		this.r *= 255;
		this.g *= 255;
		this.b *= 255;
		
		return this;
	},
	
	// Sets this Colour to the colour defined with the given hex-string
	fromHex: function(hex) {
		return this.fromInt(fk.hex2dec(hex));
	},
	
	// Sets this Colour to the colour defined with the given integer
	fromInt: function(i) {
		this.r = (i >> 16) & 0xFF;
		this.g = (i >> 8) & 0xFF;
		this.b = i & 0xFF;
		return this;
	},
	
	// -- Misc Utilities -------------------------------------------------------
	randomise: function() {
		this.red(Math.random());
		this.green(Math.random());
		this.blue(Math.random());
		return this;
	},
	
	slerp: function(target, delta) {
		this.r = this.r * (1 - delta) + target.r * delta;
		this.g = this.g * (1 - delta) + target.g * delta;
		this.b = this.b * (1 - delta) + target.b * delta;
		return this;
	},
	
	interpolate: function(target, delta) {
		this.r += (target.r - this.r) * delta;
		this.g += (target.g - this.g) * delta;
		this.b += (target.b - this.b) * delta;
		return this;  
	},
	
	clone: function() {
		return new fk.Colour(this);
	},
	
	toString: function() {
		return 'Colour['+ this.r +', '+ this.g +', '+ this.b + ']';
	},
	
	
	// -- Private Utilities ------------------------------------------------------
	parseComponent: function(arg) {
		if(typeof(arg) == 'number') {
			var n = arg
			// make sure n is not negative
			if(n < 0) n *= -1
			// when n is smaller than 1 assume it's a float [0,1] and scale it up
			if(n < 1.0) n *= 255
			// make sure n doesnt exceed the rgb range [0,255]
			if(n > 255) n = 255
			return n
			
		} else if(typeof(arg) == 'string') {
			return this.parseComponent(parseFloat(arg))
			
		} else {
			fk.info("fk.Colour.parseComponent: unhandled type "+ typeof(arg) +" "+ arg)
			return arg	
		}
	},
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 */

// =============================================================================
// MATH UTILITIES & HELPERS
// =============================================================================
fk.math = {
	PI: Math.PI,
	HALF_PI: Math.PI * 0.5,
	TWO_PI: Math.PI * 2,
	
	DEG_TO_RAD: Math.PI / 180,
	RAD_TO_DEG: 180 / Math.PI,
	 
	// -- Classes ----------------------------------------------------------------
	Rect: function() {
		this.x1 = 0
		this.y1 = 0
		this.x2 = 0
		this.y2 = 0
		
		if(arguments.length==4) {
			this.x1 = arguments[0]
			this.y1 = arguments[1]
			this.x2 = arguments[2]
			this.y2 = arguments[3]
		}
		
		this.intersects = function(r) {
			return r.x2 > this.x1 && 
						 r.y2 > this.y1 &&
			    	 r.x1 < this.x2 && 
			    	 r.y1 < this.y2
		}
		
		this.contains = function(r) {
			return r.x1 >= this.x1 && 
						 r.y1 >= this.y1 &&
			    	 r.x2 <= this.x2 && 
			    	 r.y2 <= this.y2
		}
	},
	
	// -- Utilities --------------------------------------------------------------
	random: function(min, max) {
		return Math.random() * (max - min) + min
	},
	
	randomInt: function(min, max) { 
		return parseInt(Math.random() * (max - min) + min)
	},
	
	randomNormal: function() {
		return Math.random() > 0.5 ? 1 : -1;
	},
	
	flipCoin: function() {
		return Math.random() > 0.5 ? true : false;
	},

	slerp: function(cur, to, delta) {
		return cur * (1 - delta) + to * delta
	},
			
	slerpAngle: function(cur, to, delta) {
		if (cur < 0 && to > 0) {
			if(Math.abs(cur) > this.HALF_PI &&
				 Math.abs(to) > this.HALF_PI)
					cur += this.TWO_PI
				
		} else if (cur > 0 && to < 0) {
			if(Math.abs(cur) > this.HALF_PI && 
				 Math.abs(to) > this.HALF_PI)
					cur -= this.TWO_PI
		}
		return cur * (1 - delta) + to * delta
	},
}
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: July 05, 2009
 */

// =============================================================================
// Vector 2D class
// =============================================================================
fk.math.Vec2 = fk.Class.extend({
	x: 0, y: 0,

	init: function() {
		switch(arguments.length) {
			case 1:
				this.setS(arguments[0]);
				break;
			
			case 2:
				this.set(arguments[0], arguments[1]);
				break;
		};
	},
	
	// -- Setters --------------------------------------------------------------
	set: function(x, y) {
		this.x = x;
		this.y = y;
		return this;
	},
	
	setV: function(v) {
		this.x = v.x;
		this.y = v.y;
		return this;
	},

	setS: function(s) {
		this.x = s;
		this.y = s;
		return this;
	},

	// -- Vector Operations ----------------------------------------------------
	add: function(x, y) { 
		this.x += x;
		this.y += y;
		return this;
	},

	sub: function(x, y) { 
		this.x -= x;
		this.y -= y;
		return this;
	},

	mul: function(x, y) { 
		this.x *= x;
		this.y *= y;
		return this;
	},

	div: function(x, y) { 
		this.x /= x;
		this.y /= y;
		return this;
	},

	// -- Vector Object Operations ---------------------------------------------
	addV: function(v) { 
		this.x += v.x;
		this.y += v.y;
		return this;
	},

	subV: function(v) { 
		this.x -= v.x;
		this.y -= v.y;
		return this;		
	},

	mulV: function(v) { 
		this.x *= v.x;
		this.y *= v.y;
		return this;
	},

	divV: function(v) { 
		this.x /= v.x;
		this.y /= v.y;
		return this;		
	},

	// -- Scalar Operations ----------------------------------------------------
	addS: function(s) { 
		this.x += s;
		this.y += s;
		return this;
	},

	subS: function(s) { 
		this.x -= s;
		this.y -= s;
		return this;
	},

	mulS: function(s) { 
		this.x *= s;
		this.y *= s;
		return this;
	},

	divS: function(s) { 
		this.x /= s;
		this.y /= s;
		return this;
	},

	// -- Other Operations -----------------------------------------------------
	zero: function() { 
		this.x = 0; this.y = 0;
		return this;
	},

	normalize: function() {
		var l = this.length();
		return (l != 0) ? this.divS(l) : this;
	},

	lengthSquared: function() {
		return this.x * this.x + this.y * this.y;
	},

	length: function() { return Math.sqrt(this.lengthSquared()); },

	clamp: function(max) {
		var l = this.length();
		if(l > max) {
			this.divS(l);
			this.mulS(max);
		}
		return this;
	},

	// -- Distance Calculations ------------------------------------------------
	distance: function(x,y) {
		return Math.sqrt(this.distanceSquared(x,y));
	},
	
	distanceV: function(v) {
		return Math.sqrt(this.distanceSquared(v.x,v.y));
	},
	
	distanceSquared: function(x, y) {
		var dx = this.x - x;
    	var dy = this.y - y;
		return dx * dx + dy * dy;
	},
	
	// -- Helpers --------------------------------------------------------------
	toString: function() {
		return "Vec2("+ this.x +","+ this.y +")";
	},
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: July 05, 2009
 */

// =============================================================================
// Vector 3D class
// =============================================================================
fk.math.Vec3 = fk.Class.extend({
	x: 0, y: 0, z: 0,
	
	init: function() {
		switch(arguments.length) {
			case 1:
				this.setV(arguments[0]);
				break;
			
			case 3:
				this.set(arguments[0], arguments[1], arguments[2]);
				break;
		};
	},
	
	// -- Setters --------------------------------------------------------------
	set: function(x, y, z) {
		this.x = x;
		this.y = y;
		this.z = z;
		return this;
	},
	
	setV: function(v) {
		this.x = v.x;
		this.y = v.y;
		this.z = v.z;
		return this;
	},

	setS: function(s) {
		this.x = s;
		this.y = s;
		this.z = s;
		return this;
	},

	// -- Vector Operations ----------------------------------------------------
	add: function(x, y, z) { 
		this.x += x;
		this.y += y;
		this.z += z;
		return this;
	},

	sub: function(x, y, z) { 
		this.x -= x;
		this.y -= y;
		this.z -= z;
		return this;
	},

	mul: function(x, y, z) { 
		this.x *= x;
		this.y *= y;
		this.z *= z;
		return this;
	},

	div: function(x, y, z) { 
		this.x /= x;
		this.y /= y;
		this.z /= z;
		return this;
	},

	// -- Vector Object Operations ---------------------------------------------
	addV: function(v) { 
		this.x += v.x;
		this.y += v.y;
		this.z += v.z;
		return this;
	},

	subV: function(v) { 
		this.x -= v.x;
		this.y -= v.y;
		this.z -= v.z;
		return this;		
	},

	mulV: function(v) { 
		this.x *= v.x;
		this.y *= v.y;
		this.z *= v.z;		
		return this
	},

	divV: function(v) { 
		this.x /= v.x;
		this.y /= v.y;
		this.z /= v.z;
		return this;		
	},

	// -- Scalar Operations ----------------------------------------------------
	addS: function(s) { 
		this.x += s;
		this.y += s;
		this.z += s;
		return this;
	},

	subS: function(s) { 
		this.x -= s;
		this.y -= s;
		this.z -= s;
		return this;
	},

	mulS: function(s) { 
		this.x *= s;
		this.y *= s;
		this.z *= s;
		return this;
	},

	divS: function(s) { 
		this.x /= s;
		this.y /= s;
		this.z /= s;
		return this;
	},

	// -- Helpers --------------------------------------------------------------
	/** Resets this vectors components all to zero */
	zero: function() { 
		this.x = 0; this.y = 0; this.z = 0;
		return this;
	},

	normalize: function() {
		var l = this.length();
		return (l != 0) ? this.divS(l) : this;
	},

	length: function() { return Math.sqrt(this.lengthSquared()); },

	lengthSquared: function() {
		return this.x * this.x + this.y * this.y + this.z * this.z;
	},

	clamp: function(max) {
		var l = this.length();
		if(l > max) {
			this.divS(l);
			this.mulS(max);
		}
		return this;
	},
	
	/** Calculates the dot product of this vector a provided vector. */
	dot: function(v) {
		return this.x * v.x + this.y * v.y + this.z * v.z;
	},
	
	/** Calculates the cross product of this vector with a parameter vector v. */
	cross: function(v, result) {
    	var rx = (this.y * v.z) - (this.z * v.y);
    	var ry = (this.z * v.x) - (this.x * v.z);
    	var rz = (this.x * v.y) - (this.y * v.x);
		
		var r = (result==undefined) ? new fk.math.Vec3() : result;
		r.set(rx, ry, rz);
		return r;
	},
	
	// -- Distance Calculations ------------------------------------------------
	distance: function(x,y,z) {
		return Math.sqrt(this.distanceSquared(x,y,z));
	},
	
	distanceV: function(v) {
		return Math.sqrt(this.distanceSquared(v.x,v.y,v.z));
	},
	
	distanceSquared: function(x, y, z) {
		var dx = this.x - x;
    	var dy = this.y - y;
    	var dz = this.z - z;
		return dx * dx + dy * dy + dz * dz;
	},

	equals: function(x,y,z) {
		this.x == x && this.y == y && this.z == z;
	},
	
	toString: function() {
		return "Vec3("+ this.x +","+ this.y +","+ this.z +")";
	},
});

// unit vectors
fk.math.Vec3.ZERO = new fk.math.Vec3(0,0,0); 
fk.math.Vec3.UNIT_X = new fk.math.Vec3(1,0,0);
fk.math.Vec3.UNIT_Y = new fk.math.Vec3(0,1,0);
fk.math.Vec3.UNIT_Z = new fk.math.Vec3(0,0,1);

/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: July 05, 2009
 */

// =============================================================================
// 4x4 Matrix Class
// =============================================================================
fk.math.Mat4 = fk.Class.extend({
	m00: 0, m01: 0, m02: 0, m03: 3,
	m10: 0, m11: 0, m12: 0, m13: 3,
	m20: 0, m21: 0, m22: 0, m23: 3,
	m30: 0, m31: 0, m32: 0, m33: 3,
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: July 1, 2009
 */

// =============================================================================
// Simplex Noise in 2D, 3D and 4D
//
// Original code from ToxicLibs/SimplexNoise.java by Karsten Schmidt 
// http://www.toxiclibs.org
//
// Which was based based on the example code from Stefan Gustavson's paper: 
// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
//
// =============================================================================
fk.math.Simplex = function() {
	var SQRT3 = Math.sqrt(3.0)
	var SQRT5 = Math.sqrt(3.0)

	// Skewing and unskewing factors for 2D, 3D and 4D, some of them pre-multiplied
	var F2 = 0.5 * (SQRT3 - 1.0)
	var G2 = (3.0 - SQRT3) / 6.0
	var G22 = (3.0 - SQRT3) / 6.0

	var F3 = 1.0 / 3.0
	var G3 = 1.0 / 6.0

	var F4 = (SQRT5 - 1.0) / 4.0
	var G4 = (5.0 - SQRT5) / 20.0
	var G42 = G4 * 2.0
	var G43 = G4 * 3.0
	var G44 = G4 * 4.0 - 1.0

	// Permutation table
	var p = [ 151, 160, 137, 91, 90, 15, 131, 13, 201,
			95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37,
			240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62,
			94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56,
			87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139,
			48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133,
			230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25,
			63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200,
			196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3,
			64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255,
			82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
			223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153,
			101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79,
			113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242,
			193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249,
			14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204,
			176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222,
			114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
	]

	// To remove the need for index wrapping, double the permutation table length
	var perm = new Array(p.length * 2)
	for(var i=0; i<perm.length; i++) 
		perm[i] = p[i & 0xff]
		
	// Gradient vectors for 3D (pointing to mid points of all edges of a unit cube)
	var grad3 = [ 
		  [ 1, 1, 0 ], [ -1, 1, 0 ], [ 1, -1, 0 ], 
			[ -1, -1, 0 ], [ 1, 0, 1 ], [ -1, 0, 1 ],
			[ 1, 0, -1 ], [ -1, 0, -1 ], [ 0, 1, 1 ], 
			[ 0, -1, 1 ], [ 0, 1, -1 ], [ 0, -1, -1 ] 
	]
	
	// Gradient vectors for 4D (pointing to mid points of all edges of a unit 4D hypercube)
	var grad4 = [
		[ 0, 1, 1, 1 ], [ 0, 1, 1, -1 ], [ 0, 1, -1, 1 ], [ 0, 1, -1, -1 ],
		[ 0, -1, 1, 1 ], [ 0, -1, 1, -1 ], [ 0, -1, -1, 1 ], [ 0, -1, -1, -1 ],
		[ 1, 0, 1, 1 ], [ 1, 0, 1, -1 ], [ 1, 0, -1, 1 ], [ 1, 0, -1, -1 ],
		[ -1, 0, 1, 1 ], [ -1, 0, 1, -1 ], [ -1, 0, -1, 1 ], [ -1, 0, -1, -1 ],
		[ 1, 1, 0, 1 ], [ 1, 1, 0, -1 ], [ 1, -1, 0, 1 ], [ 1, -1, 0, -1 ], 
		[ -1, 1, 0, 1 ], [ -1, 1, 0, -1 ], [ -1, -1, 0, 1 ], [ -1, -1, 0, -1 ],
		[ 1, 1, 1, 0 ], [ 1, 1, -1, 0 ], [ 1, -1, 1, 0 ], [ 1, -1, -1, 0 ],
		[ -1, 1, 1, 0 ], [ -1, 1, -1, 0 ], [ -1, -1, 1, 0 ], [ -1, -1, -1, 0 ]
	]
	
	// A lookup table to traverse the simplex around a given point in 4D.
	// Details can be found where this table is used, in the 4D noise method.
	var simplex = [ 
		[ 0, 1, 2, 3 ], [ 0, 1, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 2, 3, 1 ],
		[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 2, 3, 0 ],
		[ 0, 2, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 3, 1, 2 ], [ 0, 3, 2, 1 ],
		[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 1, 3, 2, 0 ],
		[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
		[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
		[ 1, 2, 0, 3 ], [ 0, 0, 0, 0 ], [ 1, 3, 0, 2 ], [ 0, 0, 0, 0 ],
		[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 2, 3, 0, 1 ], [ 2, 3, 1, 0 ],
		[ 1, 0, 2, 3 ], [ 1, 0, 3, 2 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
		[ 0, 0, 0, 0 ], [ 2, 0, 3, 1 ], [ 0, 0, 0, 0 ], [ 2, 1, 3, 0 ],
		[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
		[ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
		[ 2, 0, 1, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
		[ 3, 0, 1, 2 ], [ 3, 0, 2, 1 ], [ 0, 0, 0, 0 ], [ 3, 1, 2, 0 ],
		[ 2, 1, 0, 3 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ],
		[ 3, 1, 0, 2 ], [ 0, 0, 0, 0 ], [ 3, 2, 0, 1 ], [ 3, 2, 1, 0 ] 
	]
	
	var fastfloor = function(x) { return x > 0 ? x : x - 1 }

	// Computes dot product in 2D.
	var dot2d = function(g, x, y) { return g[0] * x + g[1] * y }

	// Computes dot product in 3D.	
	var dot3d = function(g, x, y, z) { return g[0] * x + g[1] * y + g[2] * z }

	// Computes dot product in 4D.
	var dot4d = function(g, x, y, z, w) { return g[0] * x + g[1] * y + g[2] * z + g[3] * w }
	
	
	// -- Noise Functions ------------------------------------------------------
	
	// Computes 2D Simplex Noise.
	this.sample2d = function(x,y) {
		var n0 = 0, n1 = 0, n2 = 0 // Noise contributions from the three corners
	
		// fk.info('-------------------------------------------------')
	
		// Skew the input space to determine which simplex cell we're in
		var s = (x + y) * F2 // Hairy factor for 2D
		var i = Math.floor(x + s)
		var j = Math.floor(y + s)
 		var t = (i + j) * G2
 		var x0 = x - (i - t) // The x,y distances from the cell origin
 		var y0 = y - (j - t)
	
		// For the 2D case, the simplex shape is an equilateral triangle.
		// Determine which simplex we are in.
		var i1, j1 // Offsets for second (middle) corner of simplex in (i,j)
		if(x0 > y0) {
			i1 = 1
			j1 = 0
		} // lower triangle, XY order: (0,0)->(1,0)->(1,1)
		else {
			i1 = 0
			j1 = 1
		} // upper triangle, YX order: (0,0)->(0,1)->(1,1)

		// A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
		// a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
		// c = (3-sqrt(3))/6
		var x1 = x0 - i1 + G2 // Offsets for middle corner in (x,y) unskewed
		var y1 = y0 - j1 + G2
		var x2 = x0 + G22 // Offsets for last corner in (x,y) unskewed
		var y2 = y0 + G22
	
		// Work out the hashed gradient indices of the three simplex corners
		var ii = i & 0xff
		var jj = j & 0xff

		// Calculate the contribution from the three corners
		var t0 = 0.5 - x0 * x0 - y0 * y0
		if (t0 > 0) {
			t0 *= t0
			var gi0 = perm[ii + perm[jj]] % 12
			n0 = t0 * t0 * dot2d(grad3[gi0], x0, y0) // (x,y) of grad3 used for 2D gradient
		}
		var t1 = 0.5 - x1 * x1 - y1 * y1
		if (t1 > 0) {
			t1 *= t1
			var gi1 = perm[ii + i1 + perm[jj + j1]] % 12
			n1 = t1 * t1 * dot2d(grad3[gi1], x1, y1)
		}
		var t2 = 0.5 - x2 * x2 - y2 * y2
		if (t2 > 0) {
			t2 *= t2
			var gi2 = perm[ii + 1 + perm[jj + 1]] % 12
			n2 = t2 * t2 * dot2d(grad3[gi2], x2, y2)
		}
	
		// Add contributions from each corner to get the final noise value.
		// The result is scaled to return values in the interval [-1,1].
		return 70.0 * (n0 + n1 + n2)
	}

	// Computes 3D Simplex Noise.
	this.sample3d = function(x,y,z) {
		var n0 = 0, n1 = 0, n2 = 0, n3 = 0
		// Noise contributions from the
		// four corners
		// Skew the input space to determine which simplex cell we're in
		// final var F3 = 1.0 / 3.0
		var s = (x + y + z) * F3 // Very nice and simple skew factor
		// for 3D
		var i = fastfloor(x + s)
		var j = fastfloor(y + s)
		var k = fastfloor(z + s)
		// final var G3 = 1.0 / 6.0 // Very nice and simple unskew factor,
		// too
		var t = (i + j + k) * G3
		var x0 = x - (i - t) // The x,y,z distances from the cell origin
		var y0 = y - (j - t)
		var z0 = z - (k - t)
		// For the 3D case, the simplex shape is a slightly irregular
		// tetrahedron.
		// Determine which simplex we are in.
		var i1, j1, k1 // Offsets for second corner of simplex in (i,j,k)
		// coords
		var i2, j2, k2 // Offsets for third corner of simplex in (i,j,k) coords
		if (x0 >= y0) {
			if (y0 >= z0) {
				i1 = 1
				j1 = 0
				k1 = 0
				i2 = 1
				j2 = 1
				k2 = 0
			} // X Y Z order
			else if (x0 >= z0) {
				i1 = 1
				j1 = 0
				k1 = 0
				i2 = 1
				j2 = 0
				k2 = 1
			} // X Z Y order
			else {
				i1 = 0
				j1 = 0
				k1 = 1
				i2 = 1
				j2 = 0
				k2 = 1
			} // Z X Y order
		} else { // x0<y0
			if (y0 < z0) {
				i1 = 0
				j1 = 0
				k1 = 1
				i2 = 0
				j2 = 1
				k2 = 1
			} // Z Y X order
			else if (x0 < z0) {
				i1 = 0
				j1 = 1
				k1 = 0
				i2 = 0
				j2 = 1
				k2 = 1
			} // Y Z X order
			else {
				i1 = 0
				j1 = 1
				k1 = 0
				i2 = 1
				j2 = 1
				k2 = 0
			} // Y X Z order
		}
		// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
		// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z),
		// and
		// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z),
		// where
		// c = 1/6.
		var x1 = x0 - i1 + G3 // Offsets for second corner in (x,y,z) coords
		var y1 = y0 - j1 + G3
		var z1 = z0 - k1 + G3

		var x2 = x0 - i2 + F3 // Offsets for third corner in (x,y,z)
		var y2 = y0 - j2 + F3
		var z2 = z0 - k2 + F3

		var x3 = x0 - 0.5 // Offsets for last corner in (x,y,z)
		var y3 = y0 - 0.5
		var z3 = z0 - 0.5
		// Work out the hashed gradient indices of the four simplex corners
		var ii = i & 0xff
		var jj = j & 0xff
		var kk = k & 0xff

		// Calculate the contribution from the four corners
		var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0
		if (t0 > 0) {
			t0 *= t0
			var gi0 = perm[ii + perm[jj + perm[kk]]] % 12
			n0 = t0 * t0 * dot3d(grad3[gi0], x0, y0, z0)
		}
		var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1
		if (t1 > 0) {
			t1 *= t1
			var gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]] % 12
			n1 = t1 * t1 * dot3d(grad3[gi1], x1, y1, z1)
		}
		var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2
		if (t2 > 0) {
			t2 *= t2
			var gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]] % 12
			n2 = t2 * t2 * dot3d(grad3[gi2], x2, y2, z2)
		}
		var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3
		if (t3 > 0) {
			t3 *= t3
			var gi3 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]] % 12
			n3 = t3 * t3 * dot3d(grad3[gi3], x3, y3, z3)
		}
		// Add contributions from each corner to get the final noise value.
		// The result is scaled to stay just inside [-1,1]
		return 32.0 * (n0 + n1 + n2 + n3)
	}

	// Computes 4D Simplex Noise.
	this.sample4d = function(x,y,z,w) {
		// The skewing and unskewing factors are hairy again for the 4D case
		var n0 = 0, n1 = 0, n2 = 0, n3 = 0, n4 = 0 // Noise contributions
		// from the five corners
		// Skew the (x,y,z,w) space to determine which cell of 24 simplices
		var s = (x + y + z + w) * F4 // Factor for 4D skewing
		var i = fastfloor(x + s)
		var j = fastfloor(y + s)
		var k = fastfloor(z + s)
		var l = fastfloor(w + s)
		var t = (i + j + k + l) * G4 // Factor for 4D unskewing
		var x0 = x - (i - t) // The x,y,z,w distances from the cell origin
		var y0 = y - (j - t)
		var z0 = z - (k - t)
		var w0 = w - (l - t)
		// For the 4D case, the simplex is a 4D shape I won't even try to
		// describe.
		// To find out which of the 24 possible simplices we're in, we need to
		// determine the magnitude ordering of x0, y0, z0 and w0.
		// The method below is a good way of finding the ordering of x,y,z,w and
		// then find the correct traversal order for the simplex were in.
		// First, six pair-wise comparisons are performed between each possible
		// pair of the four coordinates, and the results are used to add up
		// binary bits for an vareger index.
		var c = 0
		if (x0 > y0) {
			c = 0x20
		}
		if (x0 > z0) {
			c |= 0x10
		}
		if (y0 > z0) {
			c |= 0x08
		}
		if (x0 > w0) {
			c |= 0x04
		}
		if (y0 > w0) {
			c |= 0x02
		}
		if (z0 > w0) {
			c |= 0x01
		}
		var i1, j1, k1, l1 // The vareger offsets for the second simplex corner
		var i2, j2, k2, l2 // The vareger offsets for the third simplex corner
		var i3, j3, k3, l3 // The vareger offsets for the fourth simplex corner
		// simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some
		// order. Many values of c will never occur, since e.g. x>y>z>w makes
		// x<z, y<w and x<w impossible. Only the 24 indices which have non-zero
		// entries make any sense. We use a thresholding to set the coordinates
		// in turn from the largest magnitude. The number 3 in the "simplex"
		// array is at the position of the largest coordinate.
		var sc = simplex[c]
		i1 = sc[0] >= 3 ? 1 : 0
		j1 = sc[1] >= 3 ? 1 : 0
		k1 = sc[2] >= 3 ? 1 : 0
		l1 = sc[3] >= 3 ? 1 : 0
		// The number 2 in the "simplex" array is at the second largest
		// coordinate.
		i2 = sc[0] >= 2 ? 1 : 0
		j2 = sc[1] >= 2 ? 1 : 0
		k2 = sc[2] >= 2 ? 1 : 0
		l2 = sc[3] >= 2 ? 1 : 0
		// The number 1 in the "simplex" array is at the second smallest
		// coordinate.
		i3 = sc[0] >= 1 ? 1 : 0
		j3 = sc[1] >= 1 ? 1 : 0
		k3 = sc[2] >= 1 ? 1 : 0
		l3 = sc[3] >= 1 ? 1 : 0
		// The fifth corner has all coordinate offsets = 1, so no need to look
		// that up.
		var x1 = x0 - i1 + G4 // Offsets for second corner in (x,y,z,w)
		var y1 = y0 - j1 + G4
		var z1 = z0 - k1 + G4
		var w1 = w0 - l1 + G4

		var x2 = x0 - i2 + G42 // Offsets for third corner in (x,y,z,w)
		var y2 = y0 - j2 + G42
		var z2 = z0 - k2 + G42
		var w2 = w0 - l2 + G42

		var x3 = x0 - i3 + G43 // Offsets for fourth corner in (x,y,z,w)
		var y3 = y0 - j3 + G43
		var z3 = z0 - k3 + G43
		var w3 = w0 - l3 + G43

		var x4 = x0 + G44 // Offsets for last corner in (x,y,z,w)
		var y4 = y0 + G44
		var z4 = z0 + G44
		var w4 = w0 + G44

		// Work out the hashed gradient indices of the five simplex corners
		var ii = i & 0xff
		var jj = j & 0xff
		var kk = k & 0xff
		var ll = l & 0xff

		// Calculate the contribution from the five corners
		var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0
		if (t0 > 0) {
			t0 *= t0
			var gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32
			n0 = t0 * t0 * dot4d(grad4[gi0], x0, y0, z0, w0)
		}
		var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1
		if (t1 > 0) {
			t1 *= t1
			var gi1 = perm[ii + i1
			+ perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32
			n1 = t1 * t1 * dot4d(grad4[gi1], x1, y1, z1, w1)
		}
		var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2
		if (t2 > 0) {
			t2 *= t2
			var gi2 = perm[ii + i2
			+ perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32
			n2 = t2 * t2 * dot4d(grad4[gi2], x2, y2, z2, w2)
		}
		var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3
		if (t3 > 0) {
			t3 *= t3
			var gi3 = perm[ii + i3
			+ perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32
			n3 = t3 * t3 * dot4d(grad4[gi3], x3, y3, z3, w3)
		}
		var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4
		if (t4 > 0) {
			t4 *= t4
			var gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32
			n4 = t4 * t4 * dot4d(grad4[gi4], x4, y4, z4, w4)
		}
		// Sum up and scale the result to cover the range [-1,1]
		return 27.0 * (n0 + n1 + n2 + n3 + n4)	
	}
}

fk.math.noise = function() {
	if(!this.simplex) this.simplex = new fk.math.Simplex()
	var n = this.simplex
	
	if(arguments.length == 2) 
		return n.sample2d( arguments[0], arguments[1] )
		
	else if(arguments.length == 3) 
		return n.sample3d( arguments[0], arguments[1], arguments[2] )
			
	else if(arguments.length == 4) 
		return n.sample4d( arguments[0], arguments[1], arguments[2], arguments[3] )
}
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: January 15, 2010
 */

// SUBSUMPTION PARTICLE SYSTEM
fk.particle.System = fk.Class.extend({

	init: function(updateTicks, logicTicks) {
		this.updateTicks = updateTicks;
		this.logicTicks = logicTicks;	
		
		this.flocks = new Array();
		this.friction = 0.97;
		
		this.width = 640;
		this.height = 480;
		this.depth = 480;
		
		this.timerSim = new Date();
		this.timerLogic = new Date();
	},
	
	reset: function() {
		this.timerSim = new Date();
		this.timerLogic = new Date();	
	},
	
	update: function() {
		var now = new Date();
		var dtSim = now - this.timerSim;
		this.timerSim = now;

		// update logic
		var dtLogic = now - this.timerLogic;
		if(dtLogic > 1000/this.logicTicks) {
			this.timerLogic = now;
			for(var i=0; i<this.flocks.length; i++)
				this.flocks[i].updateLogic(dtLogic);
		}
		
		// update simulation
		for(var i=0; i<this.flocks.length; i++)
			this.flocks[i].updateSimulation(dtSim);
	},

	add: function(flock) { 
		flock.ps = this;
		this.flocks.push(flock) ;
	},
	
	// -- HELPERS --------------------------------------------------------------
	toAbsolute: function(normalized, result) {
		result.setV(normalized).mul(this.width, this.height, this.depth);
		return result;
	}
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: January 15, 2010
 */

fk.particle.Flock = fk.Class.extend({

	init: function() {
		this.particles = new Array();
		this.behaviours = new Array();
		this.emitter = new fk.particle.Emitter(this);
	},
	
	updateLogic: function(dt) {
		// update emitter
		this.emitter.updateLogic(dt);

		// prepare behaviours
		for(var i=0; i<this.behaviours.length; i++)
			this.behaviours[i].prepare(dt);

		// update particles
		for(var i=0; i<this.particles.length; i++) {
			var p = this.particles[i];

			for(var j=0; j<this.behaviours.length; j++)
				this.behaviours[j].apply(p, dt);

			p.updateLogic(dt);
		}
	},
	
	updateSimulation: function(dt) {
		for(var i=0; i<this.particles.length; i++)
			this.particles[i].updateSimulation(dt);
	},

	add: function(behaviour) { this.behaviours.push(behaviour); },

	toString: function() { return 'Flock'; },
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: January 15, 2010
 */

/**
 * 2D Emitter
 */
fk.particle.Emitter = fk.Class.extend({

	init: function(flock) {
		this.flock = flock
		
		this.position = new fk.math.Vec2();
		this.behaviours = new Array();
		this.rate = 1;
		this.interval = 1000.0;
		this.particleMax = 100;

		// private
		this.time = 0;
	},

	updateLogic: function(dt) {
		this.time += dt;

		if(this.time >= this.interval) {
			this.time = 0;

			if(this.emissionCount() > 0) {
				// prepare behaviours
				for(var i=0; i<this.behaviours.length; i++)
					this.behaviours[i].prepare(dt);

				for(var i=0; i<this.rate; i++)
					if(this.emissionCount() > 0)
						this.emit();
			}
		}
	},

	emissionCount: function() {
		return this.particleMax - this.flock.particles.length;
	},

	emit: function() {
		var p = this.createParticle();
		p.position.setV(this.position);

		// apply behaviours
		for(var i=0; i<this.behaviours.length; i++)
			this.behaviours[i].apply(p, 0);

		// add particle to flock			
		this.flock.particles.push(p);
	},
	
	createParticle: function() {
		return new fk.particle.Particle(this.flock);
	},

	add: function(behaviour) { this.behaviours.push(behaviour) },
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 */


/**
 * 2D Particle
 */
fk.particle.Particle = fk.Class.extend({
	
	init: function(flock) {
		this.flock = flock;

		this.position = new fk.math.Vec2();
		this.velocity = new fk.math.Vec2();
		this.steer = new fk.math.Vec2();
		
		this.age = 0;
		this.steerMax = 1.0;
		this.velocityMax = 10;
		this.rotation = 0;
		this.turningSpeed = 0.1;
		this.size = 10;

		// temp fields
		this.theta = 0;
		this.absVelocity = new fk.math.Vec2();
				
		// reset to defaults
		this.reset();
	},

	reset: function() {
		this.velocity.zero();
		this.steer.zero();
		
		this.age = 0;
		this.rotation = 0;
		this.size = 10;
		
		this.theta = 0;
		this.absVelocity.zero();
	},

	updateLogic: function(dt) {
		this.age += dt;
		this.steer.clamp(this.steerMax);
		this.velocity.addV(this.steer).clamp(this.velocityMax);
		this.steer.zero();
		this.theta = -Math.atan2(this.velocity.x, this.velocity.y);
	},
		
	updateSimulation: function(dt) {
		this.absVelocity.setV(this.velocity).mulS(dt / this.flock.ps.updateTicks);
		this.position.addV(this.absVelocity);
		this.velocity.mulS(this.flock.ps.friction);
		this.rotation = fk.math.slerpAngle(this.rotation, this.theta, this.turningSpeed);
	},
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 */

// =============================================================================
// PARTICLE BEHAVIOURS
// =============================================================================
fk.particle.Behaviour = fk.Class.extend({
	prepare: function(dt) {},
	apply: function(particle, dt) {}
});

// -- Emitter Behaviours -------------------------------------------------------
/*
fk.particle.Initialiser = fk.particle.Behaviour.extend({
	
	apply: function(p, dt){
	
	}
});
*/
	
/**
 * Sets the particles position to a random point within the area defined by min, max
 */
fk.particle.RandomEmit = fk.particle.Behaviour.extend({
	
	// reference to the particle system
	ps: null,
	
	// normalized minimum and maximum positions [0,1]
	min: null, max: null,
	
	init: function(ps) {
		this.ps = ps;
		
		this.min = new fk.math.Vec2(0,0);
		this.max = new fk.math.Vec2(1,1);
		
		this.minAbs = new fk.math.Vec2();
		this.rangeAbs = new fk.math.Vec2();
	},
	
	prepare: function(dt) {
		this.minAbs.setV(this.min).mul(this.ps.width, this.ps.height, this.ps.depth)
		this.rangeAbs.setV(this.max).subV(this.min).mul(this.ps.width, this.ps.height, this.ps.depth)
	},
	
	apply: function(p, dt) {
		// range * random + minimum
		p.position.setV(this.rangeAbs).mul(Math.random(), Math.random(), Math.random()).addV(this.minAbs)
	},
});


// -- Steering Behaviours ------------------------------------------------------

fk.particle.Gravity = fk.particle.Behaviour.extend({
	force: null,
	
	init: function() {
		this.force = new fk.math.Vec2(0,0.01);
	},
	
	apply: function(p, dt) {
		p.steer.addV(this.force);
	},
});

fk.particle.RandomSteer = fk.particle.Behaviour.extend({
	apply: function(p, dt) {
		p.steer.x += (Math.random() * 2.0 - 1.0) * 0.2;
		p.steer.y += (Math.random() * 2.0 - 1.0) * 0.2;
	}
});


// -- Simulation Space Behaviours ----------------------------------------------

/**
 * Makes sure the particle never leaves the area defined by min, max
 */
fk.particle.Wrap = fk.particle.Behaviour.extend({
	
	min: new fk.math.Vec2(),
	
	max: new fk.math.Vec2(),
	
	init: function(ps) {
		this.ps = ps;
	},
	
	prepare: function(dt) {
		this.min.set(0,0);
		this.max.set(this.ps.width, this.ps.height);
	},
	
	apply: function(p, dt) {
		var pos = p.position;
		if(pos.x < this.min.x) pos.x = this.max.x;
		if(pos.y < this.min.y) pos.y = this.max.y;
		if(pos.x > this.max.x) pos.x = this.min.x;
		if(pos.y > this.max.y) pos.y = this.min.y;
	}
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 */

/**
 * 3D Flock
 */
fk.particle3d.Flock = fk.particle.Flock.extend({

	init: function() {
		this._super();
		this.emitter = new fk.particle3d.Emitter(this);
	},
	
	toString: function() { return 'Flock3D'; },
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 */

/**
 * 3D Emitter
 */
fk.particle3d.Emitter = fk.particle.Emitter.extend({

	init: function(flock) {
		this._super(flock);
		this.position = new fk.math.Vec3();
	},
})
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 */

/**
 * 3D Particle
 */
fk.particle3d.Particle = fk.particle.Particle.extend({
	
	init: function(flock) {
		this._super(flock);
		
		this.position = new fk.math.Vec3();
		this.velocity = new fk.math.Vec3();
		this.steer = new fk.math.Vec3();
		
		this.absVelocity = new fk.math.Vec3();
	},
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 */

// =============================================================================
// 3D PARTICLE BEHAVIOURS
// =============================================================================

// -- Emitter Behaviours -------------------------------------------------------

/**
 * 3D random area emitter
 */
fk.particle3d.RandomEmit = fk.particle.Behaviour.extend({
	
	// reference to the particle system
	ps: null,
	
	// normalized minimum and maximum positions [0,1]
	min: null, max: null,
	
	init: function(ps) {
		this.ps = ps;
		
		this.min = new fk.math.Vec3(0,0,0);
		this.max = new fk.math.Vec3(1,1,1);
		
		this.minAbs = new fk.math.Vec3();
		
		this.range = new fk.math.Vec3();
		this.rangeAbs = new fk.math.Vec3();
	},
	
	prepare: function(dt) {
		this.ps.toAbsolute(this.min, this.minAbs);
		this.range.setV(this.max).subV(this.min);
		this.ps.toAbsolute(this.range, this.rangeAbs);
	},
	
	apply: function(p, dt) {
		// range * random + minimum
		p.position.setV(this.rangeAbs).mul(Math.random(), Math.random(), Math.random()).addV(this.minAbs)
	},
});


// -- Steering Behaviours ------------------------------------------------------

/**
 * Simple 3D Gravity
 */
fk.particle3d.Gravity = fk.particle.Behaviour.extend({
	force: null,
	
	init: function() {
		this.force = new fk.math.Vec3(0,0.01,0);
	},
	
	apply: function(p, dt) {
		p.steer.addV(this.force);
	},
});

/**
 * 3D Space Wrap
 */
fk.particle3d.Wrap = fk.particle.Behaviour.extend({
	min: new fk.math.Vec3(),
	max: new fk.math.Vec3(),
	
	init: function(ps) {
		this.ps = ps;
		
		this.minAbs = new fk.math.Vec3();
		this.maxAbs = new fk.math.Vec3();
	},
	
	prepare: function(dt) {
		this.ps.toAbsolute(this.min, this.minAbs);
		this.ps.toAbsolute(this.max, this.maxAbs);
	},
	
	apply: function(p, dt) {
		var pos = p.position;
		
		var min = this.minAbs;
		var max = this.maxAbs;

		if(pos.x < min.x) pos.x = max.x;
		if(pos.x > max.x) pos.x = min.x;
		
		if(pos.y < min.y) pos.y = max.y;
		if(pos.y > max.y) pos.y = min.y;
		
		if(pos.z < min.z) pos.z = max.z;
		if(pos.z > max.z) pos.z = min.z;
	}
});
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2010 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 */

// =============================================================================
// WORDPRESS RELATED UTILITIES
// =============================================================================
fk.wp = {

	// private static fields
	_baseURL: null,
	_templateURL: null,

	// -- Wordpress related ----------------------------------------------------
	baseURL: function(){
		if (this._baseURL == null) {
			var url = null
			var script = $("head").children("script:last")
			var src = script.attr('src')
			this._baseURL = src.substring(0, src.lastIndexOf('/') + 1)
		}
		return this._baseURL
	},
	
	templateURL: function(){
		if (this._templateURL == null) {
			var url = null
			$("head").children("link").each(function(i){
				if ($(this).attr('rel') == 'stylesheet' && url == null) {
					var href = $(this).attr('href')
					url = href.substring(0, href.lastIndexOf('/') + 1)
					return
				}
			})
			this._templateURL = url
		}
		return this._templateURL
	},
};
/*!
 * FieldKit JavaScript Library
 * http://code.google.com/p/fieldkit
 *
 * Copyright (c) 2009 Marcus Wendt
 * Licensed under the LGPL license.
 * http://www.gnu.org/licenses/lgpl.html
 *
 * Created: June 20, 2009
 */

// =============================================================================
// JQUERY RELATED UTILITIES
// =============================================================================
fk.jquery = {
	
	// -- Helpers --------------------------------------------------------------
	load: function(url) {
		fk.info('load', url);
		var suffix = url.substring(url.lastIndexOf('.') + 1)
		
		// load javascript 
		if(suffix == 'js') {
			var head = document.getElementsByTagName('head')[0]; 
			$(document.createElement('script')).attr({
				type: 'text/javascript', 
				src: url
			}).appendTo(head);
		
		// load stylesheet	
		} else if(suffix == 'css') {
			var head = document.getElementsByTagName('head')[0]; 
			$(document.createElement('link')).attr({
				type: 'text/css', 
				href: url, 
				rel: 'stylesheet',
				media: 'screen'
			}).appendTo(head);
		} else {
			fk.warn('load: unknown suffix', suffix);
		}
	},
};
/*                                                                           
 *      _____  __  _____  __     ____                                   
 *     / ___/ / / /____/ / /    /    \   Craig Armstrong Animations
 *    / ___/ /_/ /____/ / /__  /  /  /   (c) 2010, FIELD. All rights reserved.              
 *   /_/        /____/ /____/ /_____/    http://www.field.io           
 *   
 *	 Created by Marcus Wendt on 26/05/2010.
 */

// init base package
fd = {}

fd.ca = {
	renderer: null,
	
	// assumes jQuery is present and page is loaded
	init: function() {
		fk.info("init animations");
		
		//var animationClass = fd.animation.Traversing
		var animationClass = fd.ca.Rising
		renderer = new fd.animation.Renderer('background', animationClass, 30)
		renderer.start()

		//$(window).bind('resize', function() { renderer.onResize() })
	},
	
	pause: function() {
		fk.info("fd.ca.pause " + renderer.isStarted);
		
		if(renderer.isStarted) {
			renderer.stop();
		} else {
			renderer.start();
		}
	}
}
/*                                                                           
 *      _____  __  _____  __     ____                                   
 *     / ___/ / / /____/ / /    /    \   Craig Armstrong Animations
 *    / ___/ /_/ /____/ / /__  /  /  /   (c) 2010, FIELD. All rights reserved.              
 *   /_/        /____/ /____/ /_____/    http://www.field.io           
 *   
 *	 Created by Marcus Wendt on 26/05/2010.
 */
fd.animation = {}

fd.animation.Animation = fk.Class.extend({
	init: function(width, height, fps) {
		//fk.info("Animation: ", width, height, fps)
		
		this.ps = new fk.particle.System(fps, fps);
		this.flock = new fk.particle.Flock();
		this.ps.add(this.flock);
		this.ps.width = width;
		this.ps.height = height;
		this.ps.friction = 0.975;
		
		this.flock.emitter.particleMax = 50;
		 this.flock.emitter.interval = 0
		 this.flock.emitter.rate = 5
		
		// make recycling emitter
		this.flock.emitter.emissionCount = function() {
			var activeParticles = 0
			for(var i=0; i<this.flock.particles.length; i++)
				if(this.flock.particles[i].stage == 1)
					activeParticles ++
				
			return this.particleMax - activeParticles
		}
	
		this.flock.emitter.emit = function() {
			var p = null
		
			// try to find a reuseable particle
			for(var i=0; i<this.flock.particles.length; i++) {
				var tmp = this.flock.particles[i]
				if(tmp.state == 0) {
					p = tmp
					break;
				}
			}
		
			// couldnt find an existing particle, reuse the old one
			if(p == null) {
				p = new fk.particle.Particle(this.flock)
				// add particle to flock			
				this.flock.particles.push(p)	
			}
			
			// reset particle
			p.state = 1;
			p.position.setV(this.position);
			p.reset();
		
			// apply behaviours
			for(var i=0; i<this.behaviours.length; i++)
				this.behaviours[i].apply(p, 0)
		}
	
		this.flock.updateLogic = function(dt) {
			// update emitter
			this.emitter.updateLogic(dt)

			// prepare behaviours
			for(var i=0; i<this.behaviours.length; i++)
				this.behaviours[i].prepare(dt)

			// update particles
			for(var i=0; i<this.particles.length; i++) {
				var p = this.particles[i]
				if(p.state == 0) continue
			
				for(var j=0; j<this.behaviours.length; j++)
					this.behaviours[j].apply(p, dt)

				p.updateLogic(dt)
			}
		}
	},
	
	resize: function(width, height) {
		this.ps.width = width;
		this.ps.height = height;
	},
	
	update: function() { 
		this.ps.update() 
	}
})
/*                                                                           
 *      _____  __  _____  __     ____                                   
 *     / ___/ / / /____/ / /    /    \   Craig Armstrong Animations
 *    / ___/ /_/ /____/ / /__  /  /  /   (c) 2010, FIELD. All rights reserved.              
 *   /_/        /____/ /____/ /_____/    http://www.field.io           
 *   
 *	 Created by Marcus Wendt on 26/05/2010.
 */

fd.animation.Renderer = function(parent, animationClass, fps) {

	var stage = document.getElementById(parent)
	
	// create canvas if it didnt exist
	if(stage.childNodes.length == 0) {
		var canvas = document.createElement('canvas')
		canvas.width = stage.offsetWidth
		canvas.height = stage.offsetHeight
		stage.appendChild(canvas)
	} else {
		canvas = stage.childNodes[0]
	}
	
	//fk.info('Renderer: width', canvas.width, 'height', canvas.height, 'stage', stage, 'fillColor', fillColor)
	
 	var context = canvas.getContext('2d')
	var interval = null
	
	// tmp vars used with boundaries
	var min = new fk.math.Vec2()
	var max = new fk.math.Vec3()
	
	// initialize animation
	var animation = new animationClass(canvas.width, canvas.height, fps)

	this.isStarted = false
	
	this.onResize = function() {
		canvas.width = stage.offsetWidth
		canvas.height = stage.offsetHeight
		animation.resize(canvas.width, canvas.height)
		//fk.info('Renderer: width', canvas.width, 'height', canvas.height, 'stage', stage, 'fillColor', fillColor)
	}
	
	this.start = function() {
		this.stop()
		
		min.zero()
		max.set(canvas.width, canvas.height)
		
		var context = this
		this.interval = setInterval(function() {
			animation.ps.update()
			context.render()
		}, 1000.0/fps)
		
		animation.ps.reset();
		this.isStarted = true;
	}
	
	this.stop = function() {
		clearInterval(this.interval);
		this.isStarted = false;
	}
	
	this.render = function() {
		var g = context;
		
		// clearing 
		g.clearRect(0, 0, canvas.width, canvas.height);
		
		// debug rect
		// g.fillStyle = '#CCCCCC'
		// g.strokeWeight = 10
		// g.strokeRect(0,0,canvas.width, canvas.height)
		
		//g.fillStyle = fillColor;
		g.fillStyle = '#edf5ee'
		g.strokeStyle = '#edf5ee'
		
		//g.globalAlpha = 0.5
		//g.globalCompositeOperation = 'lighter'
		
		g.lineCap = 'round'
		g.lineJoin = 'round'
		g.lineWidth = 8;
		
		// create gradients
/*
		var gradSize = 10
		
		var grad0 = g.createLinearGradient(0, -gradSize, 0, gradSize)
		grad0.addColorStop(0,'#FFFFFF')
		grad0.addColorStop(1,'#FFFFFF')
		
		var grad1 = g.createLinearGradient(0, -gradSize, 0, gradSize)
		grad1.addColorStop(1,'#f4f4f4')
		grad1.addColorStop(0,'#f4f4f4')
		
		var grad2 = g.createLinearGradient(0, -gradSize, 0, gradSize)
		grad2.addColorStop(1,'#e4e4e4')
		grad2.addColorStop(0,'#e4e4e4')
*/
		
		var x = 0, y = 0, s = 0, r = 0;
		var p = null;
		
		for(var i=0; i< animation.ps.flocks.length; i++) {
			var flock = animation.ps.flocks[i]
			
			for(var j=0; j<flock.particles.length; j++) {
				p = flock.particles[j]
				if(p.state == 0) continue;
				
				g.save();
				g.translate(p.position.x,p.position.y);
				g.rotate(p.rotation);
				
				// a) Paths
				g.lineWidth = p.size
				
				if(p.style == 0) {
					g.strokeStyle = '#FFFFFF'
				} else if(p.style == 1) {
					g.strokeStyle = '#f4f4f4'					
				} else {
					g.strokeStyle = '#e4e4e4'
				}
				
				g.beginPath()
				g.moveTo(0, -p.size * 2)
				g.lineTo(0, p.size * 4)
				g.stroke()
				g.restore()
								
				// b) Rects
				// g.fillRect(0, 0, p.lineWidth, p.size);
				// g.restore();
				
				// c) Ellipses
				// g.beginPath();
				// g.arc(p.position.x, p.position.y, p.size, 0, twoPi, true);
				// g.stroke();
			}
		}
	}
}
/*                                                                           
 *      _____  __  _____  __     ____                                   
 *     / ___/ / / /____/ / /    /    \   Craig Armstrong Animations
 *    / ___/ /_/ /____/ / /__  /  /  /   (c) 2010, FIELD. All rights reserved.              
 *   /_/        /____/ /____/ /_____/    http://www.field.io           
 *   
 *	 Created by Marcus Wendt on 26/05/2010.
 */

fd.animation.Traversing = fd.animation.Animation.extend({
	init: function(width, height, fps) {
		this._super(width, height, fps)
		
		//fk.info("Traversing init ", width, height, fps)
		var animation = this
		this.initMilestones()
		
		// -- Emitter ----------------------------------------------------------
		//this.flock.emitter.position.setV(this.start)
		this.flock.emitter.rate = 1
		this.flock.emitter.interval = 250
	
		// Particle Initializer 
		// set state to alive
		this.flock.emitter.add(new function() {
			this.prepare = function(dt) {}
			this.apply = function(p, dt) {
				p.state = 1
				p.originalSize = Math.random() * 10 + 5
				p.originalWidth = Math.random() * 3

				p.size = p.originalSize
				p.lineWidth = p.originalWidth
				p.turningSpeed = 0.25
				
				p.steerMax = 10
				p.velocityMax = 20
				
				// p.lineScale = 0
				// p.lineScaleTarget = 0
			}
		})

		// -- Flock Behaviours -------------------------------------------------
	//	this.flock.add(new fk.particle.Wrap(ps))
	//	this.flock.add(new fk.particle.Gravity())
		this.flock.add(new fk.particle.RandomSteer())		
	
		// target attractor
		this.flock.add( new function() {
			var tmp = new fk.math.Vec2()
			var weight = 0.1
			this.prepare = function(dt) {} 
			this.apply = function(p, dt) {
				tmp.setV(animation.target)
				tmp.subV(p.position)
				tmp.normalize()
				tmp.mulS(weight)
				p.steer.addV(tmp)
			}
		})
	
		// target hitcheck, particle killer
		this.flock.add( new function() {
			var lifetime = 25000
			this.prepare = function(dt) {} 
			this.apply = function(p, dt) {
				if(p.position.distanceSquared(animation.target.x, animation.target.y) < 25 ||
					 p.age > lifetime) {
					p.state = 0
				}
			}
		})
	
		// mouse attractor
		// window.event.clientX
		this.flock.add( new function() {
			var tmp = new fk.math.Vec2()
			var mouse = new fk.math.Vec2()
			var weight = 0
			var minWeight = 0
			
			var rangeNorm = 0.2
			var range = 0
			var rangeSq = 0
			
			/*
			document.onmousedown = function (event) {
				isEnabled = true
				weight = 0.5
				//console.log('mousedown', event)
			}
			
			*/
			document.onmouseup = function (event) {
				//isEnabled = false
				animation.initMilestones()
				//console.log('onmouseup', event)
			}
		
			document.onmousemove = function (event) {
				// mouse.x = event.offsetX ? event.offsetX : event.clientX
				// mouse.y = event.offsetY ? event.offsetY : event.clientY
				
				if(event.pageX) {
					mouse.x = event.pageX
					mouse.y = event.pageY
				} else {
					mouse.x = event.clientX
					mouse.y = event.clientY
				}
				
				weight = 1.5
			}
		
			this.prepare = function(dt) {
				range = rangeNorm * width
				rangeSq = range * range
				
				weight -= 0.05
				if(weight < minWeight) weight = minWeight
			}
		
			this.apply = function(p, dt) {
				var distSq = p.position.distanceSquared(mouse.x, mouse.y)
				
				// when in range affect width & steering
				if(distSq < rangeSq) {				
					// var dist = Math.sqrt(distSq)
					// p.lineScaleTarget = (1.0 - (dist / range)) * 6
				
					// steering only when mouse is pressed
					tmp.setV(mouse)
					tmp.subV(p.position)
					tmp.normalize()
					tmp.mulS(weight)
					//tmp.setV(mouse).subV(p.position).normalize().mulS(weight)
					p.steer.addV(tmp)
					
				// } else {
				// 	p.lineScaleTarget = 1
				}
			}
		})
		
		// width setter
		this.flock.add( new function() {
			var totalDist = 0
			this.prepare = function(dt) {
				totalDist = animation.start.distanceSquared(animation.target.x, animation.target.y)
			} 
			this.apply = function(p, dt) {
				var distSq = p.position.distanceSquared(animation.target.x, animation.target.y)
				// var depthWidth = (1.0 - distSq/ totalDist) * (p.originalWidth * 5 + 1)
				// p.lineScale = fk.math.slerp(p.lineScale, p.lineScaleTarget, 0.25)
				// p.lineWidth = depthWidth * p.lineScale
				
				p.lineWidth = (1.0 - distSq/ totalDist) * (p.originalWidth * 5 + 1)
				
				// dont get fatter than long
				if(p.lineWidth > p.size) p.lineWidth = p.size
			
				//if(p.position.distanceSquared(target.x, target.y) < 25) {
				//	p.size = p.originalSize
				//	p.lineWidth = p.originalWidth
				//}
			}
		})
	},
	
	resize: function(width, height) {
		this._super(width, height);
		this.initMilestones();
	},
	
	initMilestones: function() {
			// Settings
			var offset = -25
			var ps = this.ps

			// Functions
			var getRandomSide = function() { return Math.ceil(Math.random() * 4) }

			var getRandomPoint = function(side, point) {
				switch(side) {
					// left
					case 1:
						point.x = offset
						point.y = Math.random() * ps.height
						break
					// right
					case 2:
						point.x = ps.width - offset
						point.y = Math.random() * ps.height
						break
					// top
					case 3:
						point.x = Math.random() * ps.width
						point.y = offset
						break
					// bottom
					case 4:
						point.x = Math.random() * ps.width
						point.y = ps.height - offset
						break
				}
				return point
			}

			// Choose Sides
			var startSide = getRandomSide()
			var targetSide = startSide == 4 ? 3 : startSide + 1

			this.start = getRandomPoint(startSide, new fk.math.Vec2())
			this.target = getRandomPoint(targetSide, new fk.math.Vec2())
			while(this.start.distanceV(this.target) < ps.width/4*3) {
				this.start = getRandomPoint(startSide, this.start)
				this.target = getRandomPoint(targetSide, this.target)
			}
	
			// Apply points
			this.flock.emitter.position.setV(this.start)
	}
})
/*                                                                           
 *      _____  __  _____  __     ____                                   
 *     / ___/ / / /____/ / /    /    \   Craig Armstrong Animations
 *    / ___/ /_/ /____/ / /__  /  /  /   (c) 2010, FIELD. All rights reserved.              
 *   /_/        /____/ /____/ /_____/    http://www.field.io           
 *   
 *	 Created by Marcus Wendt on 26/05/2010.
 */

fd.ca.Rising = fd.animation.Animation.extend({
	init: function(width, height, fps) {
		this._super(width, height, fps)
		
		fk.info("fd.ca.Rising", "init", width, height, fps)
		var animation = this
		this.initMilestones()
		
		// -- Emitter ----------------------------------------------------------
		//this.flock.emitter.position.setV(this.start)
		this.flock.emitter.rate = 1
		this.flock.emitter.interval = 250
	
		// Particle Initializer 
		// set state to alive
		this.flock.emitter.add(new function() {
			this.prepare = function(dt) {}
			this.apply = function(p, dt) {
				p.state = 1
				//p.originalSize = Math.random() * 10 + 5
				p.originalSize = Math.random() * 5 + 1
				p.originalWidth = Math.random() * 3

				p.size = p.originalSize
				p.lineWidth = p.originalWidth
				p.turningSpeed = 0.25
				
				p.steerMax = 5
				p.velocityMax = 10
				
				p.style = Math.round(Math.random() * 3)
				
				// p.lineScale = 0
				// p.lineScaleTarget = 0
			}
		})

		// -- Flock Behaviours -------------------------------------------------
	//	this.flock.add(new fk.particle.Wrap(ps))
	//	this.flock.add(new fk.particle.Gravity())
		this.flock.add(new fk.particle.RandomSteer())		
	
		// target attractor
		this.flock.add( new function() {
			var tmp = new fk.math.Vec2()
			var weight = 0.05
			this.prepare = function(dt) {} 
			this.apply = function(p, dt) {
				tmp.setV(animation.target)
				tmp.subV(p.position)
				tmp.normalize()
				tmp.mulS(weight)
				p.steer.addV(tmp)
			}
		})
	
		// target hitcheck, particle killer
		this.flock.add( new function() {
			var lifetime = 25000
			this.prepare = function(dt) {} 
			this.apply = function(p, dt) {
				if(p.position.distanceSquared(animation.target.x, animation.target.y) < 25 ||
					 p.age > lifetime) {
					p.state = 0
				}
			}
		})
	
		// mouse attractor
		// window.event.clientX
		this.flock.add( new function() {
			var tmp = new fk.math.Vec2()
			var mouse = new fk.math.Vec2()
			var weight = 0
			var minWeight = 0
			
			var rangeNorm = 0.2
			var range = 0
			var rangeSq = 0
			
			/*
			document.onmousedown = function (event) {
				isEnabled = true
				weight = 0.5
				//console.log('mousedown', event)
			}
			
			*/
			document.onmouseup = function (event) {
				//isEnabled = false
				animation.initMilestones()
				//console.log('onmouseup', event)
			}
		
			document.onmousemove = function (event) {
				// mouse.x = event.offsetX ? event.offsetX : event.clientX
				// mouse.y = event.offsetY ? event.offsetY : event.clientY
				
				if(event.pageX) {
					mouse.x = event.pageX
					mouse.y = event.pageY
				} else {
					mouse.x = event.clientX
					mouse.y = event.clientY
				}
				
				weight = 0.5
			}
		
			this.prepare = function(dt) {
				range = rangeNorm * width
				rangeSq = range * range
				
				weight -= 0.05
				if(weight < minWeight) weight = minWeight
			}
		
			this.apply = function(p, dt) {
				var distSq = p.position.distanceSquared(mouse.x, mouse.y)
				
				// when in range affect width & steering
				if(distSq < rangeSq) {				
					// var dist = Math.sqrt(distSq)
					// p.lineScaleTarget = (1.0 - (dist / range)) * 6
				
					// steering only when mouse is pressed
					tmp.setV(mouse)
					tmp.subV(p.position)
					tmp.normalize()
					tmp.mulS(weight)
					//tmp.setV(mouse).subV(p.position).normalize().mulS(weight)
					p.steer.addV(tmp)
					
				// } else {
				// 	p.lineScaleTarget = 1
				}
			}
		})
		
		// width setter
		this.flock.add( new function() {
			var totalDist = 0
			this.prepare = function(dt) {
				totalDist = animation.start.distanceSquared(animation.target.x, animation.target.y)
			} 
			this.apply = function(p, dt) {
				var distSq = p.position.distanceSquared(animation.target.x, animation.target.y)
				// var depthWidth = (1.0 - distSq/ totalDist) * (p.originalWidth * 5 + 1)
				// p.lineScale = fk.math.slerp(p.lineScale, p.lineScaleTarget, 0.25)
				// p.lineWidth = depthWidth * p.lineScale
				
				p.lineWidth = (1.0 - distSq/ totalDist) * (p.originalWidth * 5 + 1)
				
				// dont get fatter than long
				if(p.lineWidth > p.size) p.lineWidth = p.size
			
				//if(p.position.distanceSquared(target.x, target.y) < 25) {
				//	p.size = p.originalSize
				//	p.lineWidth = p.originalWidth
				//}
			}
		})
	},
	
	resize: function(width, height) {
		this._super(width, height);
		this.initMilestones();
	},
	
	initMilestones: function() {
			// Settings
			var offset = -25
			var ps = this.ps

			// Functions
			var getRandomSide = function() { return Math.ceil(Math.random() * 4) }

			var getRandomPoint = function(side, point) {
				switch(side) {
					// left
					case 1:
						point.x = offset
						point.y = Math.random() * ps.height
						break
					// right
					case 2:
						point.x = ps.width - offset
						point.y = Math.random() * ps.height
						break
					// top
					case 3:
						point.x = Math.random() * ps.width
						point.y = offset
						break
					// bottom
					case 4:
						point.x = Math.random() * ps.width
						point.y = ps.height - offset
						break
				}
				return point
			}

			// Choose Sides
			var startSide = 4; //getRandomSide()
			var targetSide = 3; //startSide == 4 ? 3 : startSide + 1

			this.start = getRandomPoint(startSide, new fk.math.Vec2())
			this.target = getRandomPoint(targetSide, new fk.math.Vec2())
			while(this.start.distanceV(this.target) < ps.width/4*3) {
				this.start = getRandomPoint(startSide, this.start)
				this.target = getRandomPoint(targetSide, this.target)
			}
	
			// Apply points
			this.flock.emitter.position.setV(this.start)
	}
})

