// paths.js
// Gordon Buxton
// Oxford Internet Consultants
// June 2005

// Amended - TH 08-Jun-2005: Changed so that travel functions themselves determine when 
// the path has finished. This is to allow some paths to run indefinitely (such as travel_randomjiggle)

function pos(x,y) {
	this.x = x;
	this.y = y;
	
	this.xv = 0;
	this.yv = 0;

	this.distance = pos_calcdistance;
	this.accelerate = pos_accelerate;
	this.setvelocity = pos_setvelocity;
	this.posX = pos_posX;
	this.posY = pos_posY;
}

function calcfinalv(v1,a,t) {
	return new pos(v1.x + a.x * t,v1.y + a.y * t);
}

// this function accelerates the current point
// which was initially travelling at velocity 'v1'
// with an acceleration vector 'a' over a time interval 't'
// returns an array where [0] = new position, and [1]=new velocity
function pos_accelerate(v1,a,t) {
	var t2 = t*t;
	// new position calculated using s=ut + (at^2)/2
	var pn = new pos(this.x + v1.x * t + (a.x * t2)/2,this.y + v1.y * t + (a.y * t2)/2);
	// new velocity calculated with v=u+at;
	var v2 = new pos(v1.x + a.x * t,v1.y + a.y * t);
	
	pn.vx = v2.x;
	pn.vy = v2.y;
	return pn;
}

// sets a velocity for a point
function pos_setvelocity(v) {
	this.xv = v.x;
	this.yv = v.y;
}

function pos_calcdistance(p2) {

	var dx = (p2.x - this.x);
	var dy = (p2.y - this.y);
	
	return(Math.sqrt((dx * dx) + (dy * dy)));
}

function pos_posX() { return this.x; }
function pos_posY() { return this.y; }


function sprite(name,ob,p1,p2,ticks,nxtCalc,tickspan,delay) {

	this.object = ob;
	this.p1 = new pos(p1.x,p1.y);
	this.p2 = new pos(p2.x,p2.y);
	this.pc = new pos(p1.x,p1.y);
	this.v = new pos(0,0);
	this.pcr = new pos(Math.round(p1.x),Math.round(p1.y));
	this.ticks = ticks;
	this.tickcount = 0;
	this.tickspan = tickspan;
	this.calc = nxtCalc;
	this.active=1;
	this.object.style.position = 'absolute';
	this.object.style.left = Math.round(this.pc.x) + 'px';
	this.object.style.top = Math.round(this.pc.y) + 'px';
	this.evtHandler = sprite_evtHandle;
	this.spritename = name;
	this.repeat=0;

	this.evt = window.setTimeout(name + ".evtHandler()",delay);
}

function sprite_evtHandle() {
	var sprt = this;
	if(sprt.active==0) { return; }
	sprt.tickcount++;
	window.clearTimeout(sprt.evt);
	var p3 = this.calc(sprt.p1,sprt.p2,sprt.pc,sprt.v,sprt.tickcount,sprt.tickspan);
	sprt.v.x = p3.vx;
	sprt.v.y = p3.vy;
	sprt.pc=p3;
	
	if(((sprt.tickspan - sprt.tickcount) <= 0) && (sprt.tickspan > 0)) {
			sprt.active=0;
	}
	
	sprt.pcr.x = Math.round(sprt.pc.x);
	sprt.pcr.y = Math.round(sprt.pc.y);
	
	sprt.object.style.left = sprt.pcr.x + 'px';
	sprt.object.style.top = sprt.pcr.y + 'px';
    
	
	if(sprt.active) { sprt.evt = window.setTimeout(sprt.spritename + ".evtHandler()",sprt.ticks); }
}

//Randomly travels around the 'target' point p2, not going more than x_threshold or y_threshold away.
function travel_randomjiggle(p1, p2, pc, v, step, steptotal) {
	var xrnd = Math.round(Math.random()*3);
	var yrnd = Math.round(Math.random()*3);
	var x_threshold = 7;
	var y_threshold = 7;
	if (Math.round(Math.random()*3) > 1) 
		{
			xrnd = xrnd * -1;
		}
	if (Math.round(Math.random()*3) > 1) 
		{
			yrnd = yrnd * -1;
		}
	if (( pc.x + xrnd > p2.x + x_threshold) || ( pc.x + xrnd < p2.x - x_threshold))
		{
			xrnd = xrnd * -1;
		}
	if (( pc.y + yrnd > p2.y + y_threshold) || ( pc.y + yrnd < p2.y - y_threshold))
		{
			yrnd = yrnd * -1;
		}
	return(new pos((pc.x+xrnd), (pc.y+yrnd)));
}
function travel_randomjigglesmall(p1, p2, pc, v, step, steptotal) {
	var xrnd = Math.round(Math.random()*2);
	var yrnd = Math.round(Math.random()*2);
	var x_threshold = 7;
	var y_threshold = 7;
	if (Math.round(Math.random()*3) > 1) 
		{
			xrnd = xrnd * -1;
		}
	if (Math.round(Math.random()*3) > 1) 
		{
			yrnd = yrnd * -1;
		}
	if (( pc.x + xrnd > p2.x + x_threshold) || ( pc.x + xrnd < p2.x - x_threshold))
		{
			xrnd = xrnd * -1;
		}
	if (( pc.y + yrnd > p2.y + y_threshold) || ( pc.y + yrnd < p2.y - y_threshold))
		{
			yrnd = yrnd * -1;
		}
	return(new pos((pc.x+xrnd), (pc.y+yrnd)));
}

//Randomly travels around the 'target' point p2, not going more than x_threshold or y_threshold away.
//is attracted to the target point 
function travel_randomgravitysmall(p1, p2, pc, v, step, steptotal) {
	//This is a tiny random offset to give to the central point,
	//to make sure we don't always just go straight through and out the other side.
	var vlimit = 3;
	var plimit = 10;
	
	if (step % 80 == 0) {
		p2.vx = Math.random()*plimit - plimit/2
		p2.vy = Math.random()*plimit - plimit/2;
		//alert(p2.vx + ',' + p2.vy);
	}
	
	//var pr = new pos(p2.x + p2.vx,p2.y + p2.vy);
	var pr = new pos(p2.x,p2.y);

	//first calculate the distance from the 'sun'
	//with a fudge to make sure it never hits
	var dst = pc.distance(pr);
	//dst = dst < 0.1 ? 0.1 : dst;

	// now we know the distance between the attractors we can calculate the force
	// based on some algorithm (gravity decreases with square of distance)
	//var f = 0.2/(dst * dst);
	// a spring gives a force which is propotional to the distance.
	var f = dst;

//	alert(f);

	//the "attraction vector"
	var xv = p2.x - pc.x;
	var yv = p2.y - pc.y;
	
//	alert(xv + ',' + yv);
	
//	yv = (xv >= 0 && xv < 0.1) ? 0.1 : (xv < 0 && xv > -0.1) ? -0.1 : xv;
//	yv = (yv >= 0 && yv < 0.1) ? 0.1 : (yv < 0 && yv > -0.1) ? -0.1 : yv;
	var xsq = Math.sqrt((xv*xv)+(yv*yv));
	yv = f * yv / xsq;
	xv = f * xv / xsq;
	
	// limit the velocity to some pre-set value so we don't get
	// silly resonant effects.
	xv = Math.abs(xv) < vlimit ? xv : (xv < 0) ? -1 * vlimit : vlimit;
	yv = Math.abs(yv) < vlimit ? yv : (yv < 0) ? -1 * vlimit : vlimit;
	
//	alert('sq: ' + xsq + 'vec: ' + xv + ',' + yv);

	// now we have a normalised vector describing the direction of the force as well as it's magnitude.
	var a = new pos(xv,yv)
	
	var pn = pc.accelerate(v,a,0.1);
	if(dst < plimit) {
		v.x=pn.vx;
		v.y=pn.vy;
	} else {
		v.x = ((p2.x - pc.x) * pn.vx) >= 0 ? pn.vx : pn.vx * (dst-plimit)/plimit;
		v.y = ((p2.y - pc.y) * pn.vy) >= 0 ? pn.vy : pn.vy * (dst-plimit)/plimit;
	}

	dst = pc.distance(p2);


//	alert('pos=' + pn.x + ',' + pn.y + ' v=' + v.x + ',' + v.y);

	return pn;
}


//Randomly travels around the 'target' point p2, not going more than x_threshold or y_threshold away.
function travel_randomwobble(p1, p2, pc, v, step, steptotal) {
	var xrnd = Math.round(Math.random()*3);
	var yrnd = Math.round(Math.random()*3);
	var x_threshold = 2;
	var y_threshold = 2;
	if (Math.round(Math.random()*3) > 1) 
		{
			xrnd = xrnd * -1;
		}
	if (Math.round(Math.random()*3) > 1) 
		{
			yrnd = yrnd * -1;
		}
	if (( p1.x + xrnd > p2.x + x_threshold) || ( p1.x + xrnd < p2.x - x_threshold))
		{
			xrnd = xrnd * -1;
		}
	if (( p1.y + yrnd > p2.y + y_threshold) || ( p1.y + yrnd < p2.y - y_threshold))
		{
			yrnd = yrnd * -1;
		}
	return(new pos((p1.x+xrnd), (p1.y+yrnd)));
}

function travel_simple(p1,p2,pc,v,step,steptotal) {
	if(step==0) { return pc; }
	if ((Math.round(pc.x) == p2.x) && (Math.round(pc.y) == p2.y)) 
	{
	
		this.active = false;
	}
	return(new pos(((p2.x - p1.x) * (step / steptotal)),((p2.y - p1.y) * (step / steptotal))));
}

function travel_sin(p1,p2,pc,step,steptotal) {
	if(step==0) { return pc; }
	var deg = Math.sin((step / steptotal) * (Math.PI / 2))
	if ((Math.round(pc.x) == p2.x) && (Math.round(pc.y) == p2.y)) 
	{
	
		this.active = false;
	}
	return(new pos(p1.x + ((p2.x - p1.x) * deg),p1.y + ((p2.y - p1.y) * deg)));
}


function travel_fadeinout(p1,p2,pc,v,step,steptotal) {
	if(step==0) { return pc; }
	var deg = Math.sin((step / steptotal) * Math.PI)
	deg = 1-deg;
	if ((Math.round(pc.x) == p2.x) && (Math.round(pc.y) == p2.y)) 
	{
	
		this.active = false;
	}
	return(new pos(p1.x + ((p2.x - p1.x) * deg),p1.y + ((p2.y - p1.y) * deg)));
}


function travel_cos(p1,p2,pc,v,step,steptotal) {
	if(step==0) { return pc; }
	var deg = (1 - Math.cos((step / steptotal) * Math.PI))/2
	if ((Math.round(pc.x) == p2.x) && (Math.round(pc.y) == p2.y)) 
	{
	
		this.active = false;
	}
	return(new pos(p1.x + ((p2.x - p1.x) * deg),p1.y + ((p2.y - p1.y) * deg)));
}


function travel_cosrnd(p1,p2,pc,v,step,steptotal) {
	if(step==0) { return pc; }
	var deg = (1 - Math.cos((step / steptotal) * Math.PI))/2
	var rdeg = 5*(p1.distance(p2) / steptotal)
	
	var pr = new pos((Math.random()-0.5)*rdeg,(Math.random()-0.5)*rdeg);
	if ((Math.round(pc.x) == p2.x) && (Math.round(pc.y) == p2.y)) 
	{
	
		this.active = false;
	}
	return(new pos(pr.x + p1.x + ((p2.x - p1.x) * deg),pr.y + p1.y + ((p2.y - p1.y) * deg)));
}

//The next function is only used for strangestuff.htm
function travel_randomjigglereturn(p1, p2, pc, v, step, steptotal) {
	//This is like random jiggle, except if the page changes, then the item tries to move to the correct relative place.
	//I hope...
	var el1;
	var el2;
	if (document.getElementById)
	{
	el1 = document.getElementById("mDiv");
	el2 = document.getElementById("mdiv2");
	} else
	{
	el1 = document.all("mDiv");
	el2 = document.all("mdiv2");
	}
	//alert(el1.offsetParent.offsetLeft);
	var x1 = (getBrowserWidth()*(0.40)) + 160;
	if (getBrowserWidth()-220 < x1)
	{
		x1 = getBrowserWidth()-220;
		//alert("pop");
	}
	var y1 = el2.offsetTop + 130;
	var xrnd = Math.round(Math.random()*3);
	var yrnd = Math.round(Math.random()*3);
	var x_threshold = 7;
	var y_threshold = 7;
	
	//if(x1 > rhThreshold-1) {
	//alert(x1 + ", " + getBrowserWidth());
	//x1 = rhThreshold -129;
	//}
	//alert(x1);
	var pos_target = new pos(x1,y1);
	//alert(pos_target.x);
	//alert(pos_target.y);
	//alert(pos_calcdistance(pos_target));
	//alert(pos_target.distance(p1));
	if (pos_target.distance(pc) > 7)
	{
	
		//we need to move back to base
		if ( pc.x  > pos_target.x + x_threshold)
		{
			xrnd = xrnd * -1;
		} else
		{
		
		}
	if ( pc.y  > pos_target.y + y_threshold)
		{
			yrnd = yrnd * -1;		
		} else 
		{
		
		
		}
		
	 //alert(getBrowserWidth());
	 //alert(x1);
	} else
	{ 
		//do our normal stuff
		if (Math.round(Math.random()*3) > 1) 
		{
			xrnd = xrnd * -1;
		}
	if (Math.round(Math.random()*3) > 1) 
		{
			yrnd = yrnd * -1;
		}
	if (( pc.x + xrnd > pos_target.x + x_threshold) || ( pc.x + xrnd < pos_target.x - x_threshold))
		{
			xrnd = xrnd * -1;
		}
	if (( pc.y + yrnd > pos_target.y + y_threshold) || ( pc.y + yrnd < pos_target.y - y_threshold))
		{
			yrnd = yrnd * -1;
		}
	}
	//alert(xrnd + ", " + yrnd);
	return(new pos((pc.x+xrnd), (pc.y+yrnd)));
}

function rhThreshold()
{
	var rht = getBrowserWidth;
	rht = 0.9 * rht;
	rht = rht - 129;
	return rht;
}

function getBrowserWidth()
{
var x,y;
if (self.innerHeight) // all except Explorer
{
	x = self.innerWidth;
	
}
else if (document.documentElement && document.documentElement.clientHeight)
	// Explorer 6 Strict Mode
{
	x = document.documentElement.clientWidth;
	
}
else if (document.body) // other Explorers
{
	x = document.body.clientWidth;
	
}
return x;
}


function getBrowserHeight()
{
	var x;
	if (self.innerHeight) {	// all except Explorer {
		x = self.innerHeight;	
	}
	else if (document.documentElement && document.documentElement.clientHeight)// Explorer 6 Strict Mode
	{
		x = document.documentElement.clientHeight;	
	}
	else if (document.body) {// other Explorers {
		x = document.body.clientHeight;	
	}
	return x;
}