/**
 * Geometry.pool
 * A set of functions to assist in geometric calculations (optimized for the Pool)
 *
 * @author john.bell@REMOVEME.umit.maine.edu
 * @version 1.1
 * @extends native Math object
 * @uses DivTools.js
 * @uses defined.js
 */

/**
 * Math.arc function
 * Given parameters describing an arc, returns an array of
 * points that make up that arc
 *
 * @argument	center	2 value relational array contain the x,y coordinates of
 *						the center of the arc
 * @argument	radius	radius of the arc in pixels
 * @argument	begin	beginning of the arc sweep, in degrees
 * @argument	end		end of the arc sweep, in degrees
 * @argument	steps	number of points in the arc to calculate
 *
 * @returns an array with [steps] points that describes the arc.
 *          Each point is a relational array containing x, y, and index of the point
 *
 * @example myArcArray = Math.arc({center:{x:200,y:200}, radius:50, begin:90, end:270, steps:30});
 */
Math.arc = function(args){
	args.begin = (args.begin * Math.PI) / 180;
	args.end = (args.end * Math.PI) / 180;
	var arcLength = (args.end - args.begin) / (args.steps - 1);
	var pointArray = new Array();
	for(var i=0; i<args.steps; i++){
		var point = new Array();
		var angle = (arcLength * i) + args.begin;

		point[0] = args.radius * Math.cos(angle);
		point[1] = -1 * args.radius * Math.sin(angle);

		var out = {x:Math.round(point[0] + args.center.x), y:Math.round(point[1] + args.center.y), idx:i};

		pointArray[i] = out;
	}
	return pointArray;
}

/**
 * Math.circle function
 * Given the parameters of a circle, returns an
 * array of points that make up the circle
 *
 * @argument	center		2-value relational array containing the x,y coordinates
 *							of the center of the circle
 * @argument	radius		Radius of the circle
 * @argument	begin		Beginning of the circle, in degrees.  Defaults to 0.
 * @argument	direction	Direction to draw the circle:  "Clockwise" or "CounterClockwise"
 *							Defaults to counterclockwise
 * @argument	steps		Number of points in the circle to calculate
 *
 * @returns	an array with [steps] points that describes the outline of the circle
 *          Each point is a relational array containing x, y, and index of the point
 *
 * @example myCircleArray = Math.circle({center:{x:200,y:200}, radius:50, begin: 30, steps:30, direction:"Clockwise"});
 */
Math.circle = function(args){
	var dir = args.direction == "Clockwise" ? -360 : 360;
	var begin = defined(args.begin) ? args.begin : 0;
	var pointArray = Math.arc({center:args.center, radius:args.radius, begin:begin, end:begin + dir, steps:args.steps + 1});
	pointArray.pop();
	return pointArray;
}

/**
 * Math.line function
 * Interpolates points on a line
 *
 * @argument	begin	2-value relational array containing the x,y coordinates
 *						of the beginning point of the line
 * @argument	end		2-value array containing the x,y coordinates
 *						of the end point of the line
 * @argument	steps	number of points to plot on the line
 *
 * @returns an array with [steps] points that fall along the line
 *          Each point is a relational array containing x, y, and index of the point
 *
 * @example myLineArray = Math.line({begin:{x:200,y:100}, end:{x:200,y:300}, steps:10});
 */
Math.line = function(args){
	var pointArray = new Array();
	for(var i=0; i<args.steps; i++){
		var point = new Array();
		point.x = Math.round(args.begin.x + (((args.end.x - args.begin.x) / (args.steps-1)) * i));
		point.y = Math.round(args.begin.y + (((args.end.y - args.begin.y) / (args.steps-1)) * i));
		point.idx = i;
		pointArray[i] = point;
	}
	return pointArray;
}

/**
 * Math.heading function
 * Draws a line from an initial point and a heading
 *
 * @argument	begin	2-value relations array containing the x,y coordinates
 *						of the beginning point of the line
 * @argument	heading	direction the line moves from its beginning point, in degrees
 * @argument	len		length of the line, in pixels
 * @argument	steps	number of points to plot on the line
 *
 * @returns an array with [steps] points that fall along the line
 *          Each point is a relational array containing x, y, and index of the point
 *
 * @example myHeadingArray = Math.heading({begin:{x:100,y:200}, heading:0, steps:10, len:200});
 */
Math.heading = function(args){
	var end = new Array();
	args.heading = (args.heading * Math.PI)/180;

	end.x = Math.round((args.len * Math.cos(args.heading)) + args.begin.x);
	end.y = Math.round((-1 * args.len * Math.sin(args.heading)) + args.begin.y);

	return Math.line({begin: args.begin, end:end, steps:args.steps});
}

/**
 * Math.polygon function
 * Given the parameters of a regular polygon, returns an array of points
 * that make up the boundaries of the polygon.
 *
 * @argument	center		2 value relational array containing the x,y coordinates
 *							of the center of the polygon
 * @argument	radius		radius of the circle in which the polygon is inscribed
 * @argument	sides		number of sides of the polygon
 * @argument	begin		degrees to rotate the first vertex of the polygon.  Defaults to 0.
 * @argument	direction	direction to draw the polygon: "Clockwise" or "CounterClockwise"
 *							defaults to "CounterClockwise"
 * @argument	steps		number of points to plot on each side of the polygon
 *
 * @returns an array with [steps] points that fall along the edges of the polygon
 *          Each point is a relational array containing x, y, and index of the point
 *
 * @example myPolygonArray = Math.polygon({center:{x:500,y:200}, radius:100, steps:8, direction: "Clockwise", begin:90, sides:6});
 */
Math.polygon = function(args){
	var steps = args.steps;
	var circleArray  = args;
	args.steps = args.sides;
	var verticies = Math.circle(args);
	var pointArray = new Array();
	for(var s=0; s<args.sides; s++){
		var side = new Array();
		var beginpoint = verticies[s];
		var endpoint = s == args.sides - 1 ? verticies[0] : verticies[s+1];
		side = Math.line({begin:beginpoint, end:endpoint, steps:steps});
		for(var j=0; j<side.length; j++){
			side[j].idx = pointArray.length;
			pointArray.push(side[j]);
		}
		pointArray.pop();
	}
	return pointArray;
}

/**
 * Math.bezier function
 * Given an array with 3 or 4 control points, plots a bezier curve
 *
 * @argument	controls	array containing 3 or 4 control points.  Each point	is
 *							a 2 value relational array containing the x,y coordinates of the point
 * @argument	steps		number of points to plot along the curve
 *
 * @returns an array with [steps] points that fall along the path of the curve
 *          Each point is a relational array containing x, y, and index of the point
 *
 * @example myBezierArray = Math.bezier({controls: [{x:300,y:400},{x:500,y:400},{x:500,y:500}], steps:25});
 */
Math.bezier = function(args){
	var muSteps = 1 / (args.steps-1);
	var pointArray = new Array();
	if(args.controls.length==3){
		for(var i=0; i<args.steps; i++){
			var mu = muSteps * i;
			var mum1 = 1 - mu;
			var mu2 = mu * mu;
			var mum12 = mum1 * mum1;

			var point = new Array();
			point.x = Math.round((args.controls[0].x*mum12) + (2*args.controls[1].x*mum1*mu) + (args.controls[2].x*mu2));
			point.y = Math.round((args.controls[0].y*mum12) + (2*args.controls[1].y*mum1*mu) + (args.controls[2].y*mu2));
			point.idx = i;

			pointArray.push(point);
		}
	return pointArray;
	}
	if(args.controls.length==4){
		for(var j=0; j<args.steps; j++){
			var mu = muSteps * j;
			var mum1 = 1 - mu;
			var mu3 = mu * mu * mu;
			var mum13 = mum1 * mum1 * mum1;

			var point = new Array();
			point.x = Math.round((args.controls[0].x*mum13) + (3*mum1*mum1*mu*args.controls[1].x) + (3*mu*mu*mum1*args.controls[2].x) + (mu3*args.controls[3].x));
			point.y = Math.round((args.controls[0].y*mum13) + (3*mum1*mum1*mu*args.controls[1].y) + (3*mu*mu*mum1*args.controls[2].y) + (mu3*args.controls[3].y));
			point.idx = j;

			pointArray.push(point);
		}
	return pointArray;
	}
	alert("Incorrect number of control points when calling Math.bezier");
	return 0;
}
