
var ACTION_NONE = 0, /* Nothing happens. */
    ACTION_MOVE = 1,  /* Move to the specific location doing nothing else. */
	ACTION_SPAWN = 2, /* Spawning new things. */
	ACTION_GENERATE = 3, /* Generate yourself. */
	ACTION_DEGENERATE = 4, /* Degenerate yourself. */
	ACTION_FADE = 5, /* Fade in or out. */
	ACTION_APPEAR = 6, /* Instantly appear or disappear. */
	ACTION_SLAVEGENERATE = 8; /* Generate but change action to ACTION_NONE when generated so that another thing can control us. */
	
var STATE_NONE = 0,
    STATE_GROW = 1,
	STATE_SPAWN = 2,
	STATE_SHRINK = 3,
	STATE_IDLE = 4,
	STATE_DEAD = 5;
	
var STYLE_PLAIN = 0x00000000,
    STYLE_REDCIRCLE = 0x00000001,
	STYLE_CROSSHAIR = 0x00000002,
	STYLE_BLUECIRCLE = 0x00000004,
	STYLE_ROTATOR = 0x00000008,
	STYLE_GREYCIRCLE = 0x00000010;

var GROWSTYLE_PLAIN = 0x00000000,
	GROWSTYLE_GROWIN = 0x00000001,
	GROWSTYLE_FADEOUT = 0x00000002,
	GROWSTYLE_FADEIN = 0x00000004,
	GROWSTYLE_GROWOUT = 0x00000008,
	GROWSTYLE_GROWIN2 = 0x00000010,
	GROWSTYLE_GROWOUT2 = 0x00000020;
	
var CP = window.CanvasRenderingContext2D && CanvasRenderingContext2D.prototype;
if (CP.lineTo) {
    CP.dashedLine = function(x, y, x2, y2, da) {
        if (!da) da = [10,5];
        this.save();
        var dx = (x2-x), dy = (y2-y);
        var len = Math.sqrt(dx*dx + dy*dy);
        var rot = Math.atan2(dy, dx);
        this.translate(x, y);
        this.moveTo(0, 0);
        this.rotate(rot);       
        var dc = da.length;
        var di = 0, draw = true;
        x = 0;
        while (len > x) {
            x += da[di++ % dc];
            if (x > len) x = len;
            draw ? this.lineTo(x, 0): this.moveTo(x, 0);
            draw = !draw;
        }       
        this.restore();
    }
}
		
/*
 * Get "pointers" to common math functions to speed up the calls. This avoids
 * having to resove the Math object member before calling the function ans is
 * suppsoed to be faster.
 */
var floor = Math.floor;
var random = Math.random;
var sin = Math.sin;
var cos = Math.cos;
var atan2 = Math.atan2;
var PI = Math.PI;
var sqrt = Math.sqrt;
var min = Math.min;
var max = Math.max;
var abs = Math.abs;

var things = [];
var activeThings = 0;
var lines = [];
var globalStyleCounter = 0;
var globalStyle = 0;

/*
 * Generate random integers in the range. This ensures that each possible
 * result has the same likelyhood of being returned by adding the .9999999
 * to the floating point multiplier before we take the floor of the random
 * result.
 */
function randomInt( minimum, maximum )
{
	if( minimum == undefined || maximum == undefined )
		return 0;
	var temp = maximum - minimum;
	temp += 0.9999999;
	return minimum + floor( random() * temp );
}

var Test = new function()
{
	var canvas;
	var context;
	
	var lastTime = new Date().getTime();
	var timeScale = 1;
	activeThings = 0;
	
	world = { width: 0, height: 0 };
	
	this.init = function()
	{
		canvas = document.getElementById( 'world' );
		
		if( canvas && canvas.getContext ) 
		{
			context = canvas.getContext( '2d' );
			window.addEventListener('resize', windowResizeHandler, false);
			windowResizeHandler();
			animate();
		}
	};
	
	function getParam(name) 
	{
		name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
		var regexS = "[\\?&]" + name + "=([^&#]*)";
		var regex = new RegExp(regexS);
		var results = regex.exec( window.location.href );
		if (results == null)
			return "";
		else
			return results[1];
	}

	function windowResizeHandler() 
	{
		world.width = canvas.offsetWidth;
		world.height = canvas.offsetHeight;
		
		canvas.width = world.width;
		canvas.height = world.height;
	}
	
	function msleep(milliseconds) 
	{
		var start = new Date().getTime();
		for (var i = 0; i < 1e7; i++) 
		{
			if ((new Date().getTime() - start) > milliseconds)
			{
				break;
			}
		}
	}
	
	function getRingPositions( thing, ring )
	{
		var startPosition = 0;
		var endPosition = 0;
		if( thing.growStyle & ( GROWSTYLE_GROWIN2 | GROWSTYLE_GROWOUT2 ) )
		{
			var lastRing = thing.rings[thing.rings.length-1];
			var temp = lastRing.position + lastRing.size - 1;
			startPosition = ring.position - temp + ( temp * thing.scale );
			endPosition = ring.position + ring.size - 1 - temp + ( temp * thing.scale );
			if( startPosition < 0 )
				startPosition = 0;
			if( endPosition < 0 )
				endPosition = 0;
		}
		else
		{
			startPosition = ring.position * thing.scale;
			endPosition = ( ring.position + ring.size - 1 ) * thing.scale;
		}
		ring.scaledRingEdge = endPosition;
		
		return { start: startPosition, end: endPosition };
	}
	
	function drawThing( thing, part )
	{
		var counter;
		
		if( part == 1 )
		{
			if( ( thing.style & STYLE_CROSSHAIR ) != 0 )
			{
				if( thing.rings.length >= 2 )
				{
					if( thing.rings[1].position - thing.rings[0].position < 7 )
					{
						var positions0 = getRingPositions( thing, thing.rings[0] );
						var positions1 = getRingPositions( thing, thing.rings[1] );
						
						context.strokeStyle = makeRGBA( SHADE_COLOR + thing.shade, SHADE_COLOR + thing.shade, SHADE_COLOR + thing.shade, thing.alpha );
						context.lineWidth = 0.8;
						context.beginPath();
						context.moveTo( thing.x, thing.y + positions0.end );
						context.lineTo( thing.x, thing.y + positions1.start );
						context.moveTo( thing.x, thing.y - positions0.end );
						context.lineTo( thing.x, thing.y - positions1.start );
						context.moveTo( thing.x + positions0.end, thing.y );
						context.lineTo( thing.x + positions1.start, thing.y );
						context.moveTo( thing.x - positions0.end, thing.y );
						context.lineTo( thing.x - positions1.start,thing.y );
						context.stroke();
					}
				}
			}
		
			for( counter = 0; counter < thing.rings.length; ++counter )
			{
				var positions = getRingPositions( thing, thing.rings[counter] );

				context.fillStyle = makeRGBA( SHADE_COLOR + thing.shade, SHADE_COLOR + thing.shade, SHADE_COLOR + thing.shade, thing.alpha );
				context.strokeStyle = makeRGBA( SHADE_COLOR + thing.shade, SHADE_COLOR + thing.shade, SHADE_COLOR + thing.shade, thing.alpha );
				context.lineWidth = 1;
				
				context.beginPath();
				if( positions.start != positions.end )
					context.arc( thing.x, thing.y, positions.end, 0, Math.PI*2, false ); // outer (filled)
				context.arc( thing.x, thing.y, positions.start, 0, Math.PI*2, true ); // inner (unfills it)					
				if( positions.start != positions.end )
					context.fill();
				context.stroke();
			}
		}
		else if( part == 2 )
		{
			if( ( thing.style & ( STYLE_REDCIRCLE | STYLE_BLUECIRCLE | STYLE_GREYCIRCLE ) ) )
			{
				var colorAdjust = 20 + SHADE_COLOR / 3;
				if( thing.style & STYLE_REDCIRCLE )
				{
					context.fillStyle = makeRGBA( 220, colorAdjust, colorAdjust, min( thing.alpha, thing.scale ) );
					context.strokeStyle = makeRGBA( 220, colorAdjust, colorAdjust, min( thing.alpha, thing.scale ) );
					context.lineWidth = SHADE_COLOR > 128 ? 1 : 2;
				}
				else if( thing.style & STYLE_BLUECIRCLE )
				{
					context.fillStyle = makeRGBA( colorAdjust, colorAdjust, 255, min( thing.alpha, thing.scale ) );
					context.strokeStyle = makeRGBA( colorAdjust, colorAdjust, 255, min( thing.alpha, thing.scale ) );
					context.lineWidth = SHADE_COLOR > 128 ? 1 : 2;
				}
				else if( thing.style & STYLE_GREYCIRCLE )
				{
					// Use a dark circle if out shading is light on top of an assumed light background.
					// Use a white circle on a dark background.
					var realColor = SHADE_COLOR < 128 ? 200 : 90;
					context.fillStyle = makeRGBA( realColor, realColor, realColor, min( thing.alpha, thing.scale ) );
					context.strokeStyle = makeRGBA( realColor, realColor, realColor, min( thing.alpha, thing.scale ) );
					context.lineWidth = SHADE_COLOR > 128 ? 1 : 2;
				}
				context.beginPath();
				context.arc( thing.x, thing.y, 6, 0, PI*2, true );
				context.stroke();
			}
		}
		else if( part == 3 )
		{
			for( counter = 0; counter < thing.spawns.length; ++counter )
			{
				var spawner = thing.spawns[counter];
				if( spawner.length == 0 )
					continue;
					
				context.strokeStyle = makeRGBA( SHADE_COLOR, SHADE_COLOR, SHADE_COLOR, thing.alpha );
				context.lineWidth = 1.0;
				context.beginPath();
				var offsetStart = getOffset( spawner.angle, spawner.startDistance );
				var offsetEnd = getOffset( spawner.angle, spawner.endDistance );
				context.moveTo( thing.x + offsetStart.x, thing.y + offsetStart.y );
				context.lineTo( thing.x + offsetEnd.x, thing.y + offsetEnd.y );
				context.stroke();
			}

			if( thing.style & STYLE_ROTATOR )
			{
				var positions = getRingPositions( thing, thing.rings[thing.rings.length-1] );
				var position = ( positions.start + positions.end ) / 2;
				var x = thing.x + cos( thing.rotate ) * position;
				var y = thing.y + sin( thing.rotate ) * position;
				context.fillStyle = makeRGBA( 255, 255, 255, min( thing.alpha, thing.scale ) );
				context.strokeStyle = makeRGBA( SHADE_COLOR + thing.shade, SHADE_COLOR + thing.shade, SHADE_COLOR + thing.shade, thing.alpha );
				context.lineWidth = .8;
				context.beginPath();
				var size = 5 * ( thing.growStyle & GROWSTYLE_GROWIN2 ? 1 : thing.scale );
				context.arc( x, y, size, 0, Math.PI*2, true );
				context.stroke();
				context.fill();
			}
		}
	}
	
	function animate() 
	{
		// Fetch the current time for this frame
		var frameTime = new Date().getTime();
		var elapsedTime = frameTime - lastTime;
		lastTime = frameTime;		

		// Get a scale factor that can be used to scale all movement.
		// First calc 60FPS in milliseconds, get the floor, then devide the actual time by that.
		timeScale = elapsedTime / ( floor( 1000 / 60 ) );
		
		// if the timeScale is way off then assume that we are debugging and make it 1.
		// 1/2 second is the threshold since I can't stop at breakpoints and then proceed
		// any quicker than that.
		if( elapsedTime > 500 )
			timeScale = 1;
			
		globalStyleCounter -= elapsedTime;
		if( globalStyleCounter <= 0 )
		{
			globalStyleCounter = 60000 + random() * 60000;
			var newStyle = pickRandomStyle( globalStyle );
			globalStyle = newStyle;
		}
			
		context.clearRect( 0, 0, canvas.width, canvas.height );
		
		for( part = 0; part < 4; ++part )
		{
			for( index = 0; index < things.length; index++ ) 
				drawThing( things[index], part );
		}
		
		for( index = 0; index < things.length; index++ ) 
		{
			thing = things[index];
			thing.takeAction( timeScale );

			if( thing.dead ) 
			{
				things.splice( index, 1 );
				index--;
			}
		}

		// Ensure that there is never an empty canvas.
		if( things.length == 0 )
			createThing( pickRandomStyle( -1 ) );
			
		requestAnimFrame( animate );
	}
};

function Ring()
{
	this.style = 0;
	this.position = 0;
	this.scaledRingEdge = 0;
	this.size = 0;
}

function Spawner()
{
	this.angle = 0;
	this.length = 0;
	this.speed = 0;
	this.xDestination = 0;
	this.yDestination = 0;
	this.destinationLength = 0;
	this.angleChange = -0.004 + random() * 0.008;
	this.ring = 0;
	this.startDistance = 0;
	this.endDistance = 0;
	this.thing = null;
	this.thingRing = 0;
	this.style = 0;
	this.state = STATE_NONE;
}

function Thing()
{
	this.dead = false;
	this.x = 0;
	this.y = 0;
	this.xDestination = 0;
	this.yDestination = 0;
	this.direction = 0;
	this.speed = 0;
	this.maxSpeed = 0;
	this.growStyle = GROWSTYLE_GROWIN2 | GROWSTYLE_FADEOUT;
	this.style = 0;
	this.rings = [];
	this.spawns = [];
	this.action = ACTION_NONE;
	this.scale = 0;
	this.alpha = 0;
	this.lastDistance = 99999999;
	this.shade = random() * 20;
	this.rotate = random() * PI * 2;
	
	var count = randomInt( 1, 5 );
	var counter;
	var size = 0;
	for( counter = 0; counter < count; ++counter )
	{
		var ring = new Ring();
		ring.style = 0;
		ring.position = size + random() * 20;
		ring.size = random() * 40
		if( ring.size > 15 )
			ring.size = 1;
			
		this.rings.push( ring );
		
		size = ring.position + ring.size + 1;

		if( size >= 25 )
			break;
	}
	this.size = size;
}

Thing.prototype.startAction = function( x, y, xDestination, yDestination, speed, action, maxDistance )
{
	this.x = x;
	this.y = y;
	
	this.angle = atan2( yDestination - y, xDestination - x );
	if( this.angle < 0 )
		this.angle += PI * 2;
		
	var distance = getDistance( x, y, xDestination, yDestination );
	if( distance > maxDistance )
		distance = maxDistance;

	this.xDestination = x + cos( this.angle ) * distance;
	this.yDestination = y + sin( this.angle ) * distance;
		
	this.maxSpeed = speed;
	this.speed = speed * 0.03;
	this.action = action;
}

Thing.prototype.startSpawn = function()
{
	var spawnStyle = randomInt( 0, 1 );
	var count = randomInt( 1, 3 );
	for( var counter = 0; counter < count; ++counter )
	{
		var spawner = new Spawner();
		var angle = 0;
		
		spawner.xDestination = 15 + random() * ( world.width - 30 );
		spawner.yDestination = 15 + random() * ( world.height - 30 );
		
		var dx = spawner.xDestination - this.x;
		var dy = spawner.yDestination - this.y;
		angle = atan2( dy, dx );
		if( angle < 0 )
			angle += PI * 2;

		var distance = getDistance( 0, 0, dx, dy );
		distance = randomInt( 30, min( 150, distance ) );
			
		spawner.angle = angle;
		spawner.length = 0;
		spawner.state = STATE_GROW;
		spawner.speed = 0.16 + random() * 0.5;
		spawner.destinationLength = distance;
		spawner.ring = randomInt( 0, this.rings.length - 1 );
		if( spawner.ring >= this.rings.length )
			spawner.ring = -1;
			
		spawner.style = spawnStyle;
		if( spawner.style == 1 )
		{
			var style = this.style;
			if( random() < 0.1 )
				style = pickRandomStyle( this.style );
			spawner.thing = createThing( style );
			spawner.thing.action = ACTION_SLAVEGENERATE;
			spawner.thingRing = randomInt( 0, spawner.thing.rings.length - 1 );
			if( spawner.thingRing > 5 )
				spawner.thingRing = -1;
		}
		
		this.spawns.push( spawner );
	}
}

Thing.prototype.moving = function( timeScale )
{
	var xVelocity = cos( this.angle ) * this.speed;
	var yVelocity = sin( this.angle ) * this.speed;
	
	this.x += xVelocity * timeScale * SPEED_ADJUST;
	this.y += yVelocity * timeScale * SPEED_ADJUST;
	
	var distance = this.distanceToDestination();
	if( distance < 8 || distance > this.lastDistance )
	{
		this.speed = max( this.speed - .001 * timeScale, 0 );
		if( this.action == ACTION_MOVE )
		{
			if( activeThings >= 7 )
			{
				this.action = ACTION_DEGENERATE;
				--activeThings;
			}
			else
			{
				this.startSpawn();
				this.action = ACTION_SPAWN;
			}
		}
	}
	else
	{
		this.speed = min( this.speed + .002 * timeScale, this.maxSpeed );
	}
	
	this.lastDistance = distance;
}

Thing.prototype.spawning = function( timeScale )
{
	var finished = 0;
	var counter = 0;
	for( counter = 0; counter < this.spawns.length; ++counter )
	{
		var spawner = this.spawns[counter];
		
		if( spawner.state == STATE_GROW )
			spawner.length += spawner.speed * timeScale * SPEED_ADJUST;
		else if( spawner.state == STATE_SHRINK )
			spawner.length -= spawner.speed * timeScale * SPEED_ADJUST;
			
		var position = 0;
		if( spawner.ring >= 0 )
		{
			var ring = this.rings[spawner.ring];
			position = ring.scaledRingEdge;
		}
		spawner.startDistance = position;
		spawner.endDistance = spawner.startDistance + spawner.length;
		spawner.angle = atan2( spawner.yDestination - this.y, spawner.xDestination - this.x );
		if( spawner.angle < 0 )
			spawner.angle += PI * 2;
		
		if( spawner.state == STATE_GROW && spawner.length >= spawner.destinationLength )
		{
			spawner.length = spawner.destinationLength;
			spawner.speed *= 2.0;
			if( spawner.thing == undefined )
			{
				var offset = getOffset( spawner.angle, spawner.endDistance );
				var style = this.style;
				if( random() < 0.1 )
					style = pickRandomStyle( this.style );
				spawner.thing = createThing( style, this.x + offset.x, this.y + offset.y );
				spawner.thing.action = ACTION_SLAVEGENERATE;
				spawner.state = STATE_SPAWN;
			}
		}
		
		if( spawner.thing != undefined )
		{
			// Compute the end of the line using the selected ring of the slave thing.
			position = 0;
			if( spawner.thingRing >= 0 && spawner.thingRing < spawner.thing.rings.length )
			{
				var ring = spawner.thing.rings[spawner.thingRing];
				position = ring.scaledRingEdge;
			}
			spawner.endDistance = spawner.startDistance + spawner.length - position;
			var offset = getOffset( spawner.angle, spawner.startDistance + spawner.length );
			spawner.thing.x = this.x + offset.x;
			spawner.thing.y = this.y + offset.y;
			
			// Limit the generation to the line length ratio if the line is still growing.
			if( spawner.state == STATE_GROW )
			{
				if( spawner.destinationLength == 0 )
					spawner.thing.scale = 0;
				else
					spawner.thing.scale = min( spawner.length / spawner.destinationLength, 1 );
			}
			
			if( spawner.thing.action == ACTION_NONE )
			{
				// Cut it off and let it go. It is no longer being generated.
				spawner.thing.setMovement( spawner.thing.x, spawner.thing.y, spawner.xDestination, spawner.yDestination );
				spawner.thing.startAction( spawner.thing.x, spawner.thing.y, spawner.xDestination, spawner.yDestination, spawner.thing.maxSpeed, ACTION_MOVE, 350 );
				spawner.thing = undefined;
				
				// Save away the actual length since the slave thing won't be available after this.
				spawner.length = spawner.endDistance - spawner.startDistance;
				spawner.destinationLength = 0;
				
				spawner.state = STATE_SHRINK;
			}
		}
		
		if( spawner.state == STATE_DEAD 
		    || ( spawner.state == STATE_SHRINK && spawner.length <= 0 ) )
		{
			spawner.speed = 0;
			spawner.length = 0;
			spawner.state = STATE_DEAD;
			++finished;
		}
	}
	
	if( finished == this.spawns.length )
	{
		this.action = ACTION_DEGENERATE;
		--activeThings;
	}
}

Thing.prototype.setMovement = function( xSet, ySet, xDest, yDest )
{
	var x = 20 + ( random() * ( world.width - 40 ) );
	var y = 20 + ( random() * ( world.height - 40 ) );
	if( xSet != undefined && ySet != undefined )
	{
		x = xSet;
		y = ySet;
	}

	var xDestination = 20 + random() * ( world.width - 40 );
	var yDestination = 20 + random() * ( world.height - 40 );
	
	if( xDest != undefined && yDest != undefined )
	{
		xDestination = xDest;
		yDestination = yDest;
	}
	
	if( random() >= .9 )
	{
		xDestination = x;
		yDestination = y;
	}
	
	this.startAction( x, y, xDestination, yDestination, this.maxSpeed, this.action, 350 );
}

Thing.prototype.takeAction = function( timeScale )
{	
	// Do things that should happen regardless of the action.
	this.rotate += .01 * timeScale;

	switch( this.action )
	{
		case ACTION_MOVE:
			this.moving( timeScale );
			break;
		case ACTION_SPAWN:
			this.moving( timeScale );
			this.spawning( timeScale );
			break;
		case ACTION_GENERATE:
		case ACTION_SLAVEGENERATE:
			if( this.growStyle & GROWSTYLE_FADEIN )
			{
				this.scale = 1;
				if( this.alpha >= 1 )
					this.alpha = 1;
				else
				{
					this.alpha += 0.01 * timeScale * SPEED_ADJUST;
					if( this.alpha > 1 )
						this.alpha = 1;
				}
			}
			else if( this.growStyle & ( GROWSTYLE_GROWIN | GROWSTYLE_GROWIN2 ) )
			{
				this.alpha = 1;
				if( this.scale >= 1 )
					this.scale = 1;
				else
				{
					this.scale += 0.003 * timeScale * SPEED_ADJUST;
					if( this.scale > 1 )
						this.scale = 1;
				}
			}
			
			if( this.alpha == 1 && this.scale == 1 )
				this.action = ( this.action == ACTION_SLAVEGENERATE ? ACTION_NONE : ACTION_MOVE );
				
			break;
		case ACTION_DEGENERATE:
			if( this.alpha <= 0 || this.scale <= 0 )
			{
				this.scale = 0;
				this.alpha = 0;
				this.action = ACTION_NONE;
				this.dead = true;
			}
			else
			{
				if( this.growStyle & GROWSTYLE_FADEOUT )
				{
					this.alpha -= 0.01 * timeScale * SPEED_ADJUST;
					if( this.alpha < 0 )
						this.alpha = 0;
				}
				else if( this.growStyle & ( GROWSTYLE_GROWOUT | GROWSTYLE_GROWOUT2 ) )
				{
					this.scale -= 0.005 * timeScale * SPEED_ADJUST;
					if( this.scale < 0 )
						this.scale = 0;
				}
				for( counter = 0; counter < this.spawns.length; ++counter )
				{
					var spawner = this.spawns[counter];
					spawner.length -= abs( spawner.speed * timeScale * SPEED_ADJUST );
				}
			}
			break;
		case ACTION_FADE:
			break;
		case ACTION_APPEAR:
			break;
		case ACTION_NONE:
		default:
			break;
	}
}

Thing.prototype.distanceToDestination = function( x, y )
{
	var dx = this.x - this.xDestination;
	var dy = this.y - this.yDestination;
	return sqrt( dx * dx + dy * dy );
};

function getDistance( x1, y1, x2, y2 )
{
	var dx = x1 - x2;
	var dy = y1 - y2;
	return sqrt( dx * dx + dy * dy );
};

function makeRGBA( r, g, b, a )
{
	return "rgba( " + floor( r ) + "," + floor( g ) + "," + floor( b ) + "," + a.toFixed( 8 ) + " )";
}
	
function makeRGB( r, g, b )
{
	return "rgb( " + floor( r ) + "," + floor( g ) + "," + floor( b ) + " )";
}

function createThing( useStyle, xSet, ySet )
{
	++activeThings;
	
	var thing = new Thing();
	thing.scale = 0;
	thing.maxSpeed = 0.08 + random() * .25;
	thing.speed = thing.maxSpeed * 0.05;
	thing.action = ACTION_GENERATE;
	thing.style = useStyle;
	
	if( random() <= .2 )
	{
		if( thing.rings.length >= 2 )
			thing.style |= STYLE_CROSSHAIR;
	}
	thing.style &= ~STYLE_ROTATOR;
	if( random() < 0.025 )
		thing.style |= STYLE_ROTATOR;
	
	thing.setMovement( xSet, ySet );
	things.push( thing );
	
	return thing;
}

function pickRandomStyle( oldStyle )
{
	var newStyle = oldStyle;
	for( counter = 0; counter < 10 && newStyle == oldStyle; ++counter )
	{
		switch( randomInt( 1, 3 ) )
		{
			case 1:
				newStyle = STYLE_REDCIRCLE;
				break;
			case 2:
				newStyle = STYLE_BLUECIRCLE;
				break;
			case 3:
				newStyle = STYLE_GREYCIRCLE;
				break;
			case 0:
			default:
				newStyle = STYLE_GREYCIRCLE;
				break;
		}
	}
	return newStyle;
}

function getOffset( angle, distance )
{
	var cosine = cos( angle );
	var sine = sin( angle );
	return { x: cosine * distance, y: sine * distance };
}

// shim with setTimeout fallback from http://paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       || 
          window.webkitRequestAnimationFrame || 
          window.mozRequestAnimationFrame    || 
          window.oRequestAnimationFrame      || 
          window.msRequestAnimationFrame     || 
          function(/* function */ callback, /* DOMElement */ element){
            window.setTimeout(callback, 1000 / 60);
          };
})();

if( typeof( SPEED_ADJUST ) == "undefined" )
	SPEED_ADJUST = 4.0;

if( typeof( SHADE_COLOR ) == "undefined" )
	SHADE_COLOR = 50;

Test.init();

