Inzomia_Animator.prototype.MAXPOINTS = 100;		//maximum number of control points
Inzomia_Animator.prototype.MAXSTEPS = 300;
Inzomia_Animator.prototype.interval = 30;

//
// i - The current point
// j - the current stepping between the i and i+1 (from 0 to 1)
// step -  the stepping size for each j, the increment to j
// steps - the total number of steps between each i
//

//////////////
// Constructor
//             input parameters: IZobj ( Inzomia object)
//							   : point ( "b-dimensional"array of control points	
function Inzomia_Animator(IZobj,controlPts,steps)         
{
	this.dowarp = false;
	this.running = false;
	this.i           =  0;			       // starting point
	this.j           =  0;			       // starting
	if(!steps){
		this.steps   = 10;                 // number of subdivision between two control points
	}
	else this.steps = steps;
	this.step        =  1.0/this.steps;    // Steplength = 1/steps
	this.t           =  0;                 // parameter for the "parametric curve representation"
	if(!controlPts){
		this.point       =  new Array();	   // array of control points	
	}
	else this.point = controlPts;
	this.IZ          =  IZobj ;			   // the Inzomia object	
	this.interval_id =  null;              // interval identifier used for the animations 
	this.loop        =  false;			   // loop condition 	
	this.pingpong    =  false;             // true if we loop in a pingpong way 
	this.reverseP    =  false;		       // if the array of control points is reversed
	this.loopType    =  String('null');

  // Offset the point with respect to this window size
  // change the coordinates from normal IZ coordinates
  // to window coordinates.
	this.COX = IZobj.width / 2;
	this.COY = IZobj.height / 2;
	for(k = 0 ;k < this.point.length;k++)
	{
		this.point[k][0] = this.point[k][0] * this.point[k][2];
		this.point[k][1] = this.point[k][1] * this.point[k][2];
	}
}

////////////////////////////////////////
//Just an is empty function for vectors
function isEmpty(vect){
	return(vect.length == 0);
}

///////////////////////////////////////////////////
// Notify the animator of the inzomia window size
//
Inzomia_Animator.prototype.updateSize = function ( Width, Height ) {
  IZ_Anim.COX = Width/2;
  IZ_Anim.COY = Height/2;
}			           			     				

///////////////////////////////////////////////////
// Set the position of the IZobj to x/z,y/z,z
//
Inzomia_Animator.prototype.draw = function (IZobj,x,y,z){

	x = (x - this.COX)/z;
	y = (y - this.COY)/z;

	IZobj.setposition(x, y, z); //this works better expecially for Netscape!
}			           			     				

////////////////////////////////////////////////////
// Draw the IZ object at the coordinate specified 
// at the "i"-element of the array point
//
Inzomia_Animator.prototype.drawPoint_i = function (IZobj,point,i){ 
	this.draw(IZobj, point[i][0],point[i][1],point[i][2]);
}

///////////////////////////
// calculate the rigth bias
//
Inzomia_Animator.prototype.getBias = function(i){
	if(i == 0){
		if (this.dowarp)
			dZ_1 = Math.abs( this.point[p.length-1][2] - this.point[i][2]);
		else
			dZ_1 = 0;

		dZ_2 = Math.abs( this.point[i][2] - this.point[i+1][2]);
	}
	else if(i == (this.point.length -1)){
		dZ_1 = Math.abs(this.point[i-1][2] - this.point[i][2]);
		if (this.dowarp)
			dZ_2 = Math.abs(this.point[i][2] - this.point[0][2]);
		else
			dZ_2 = 0;
	}
		else if ((i >=1 ) && (i <=(this.point.length - 2))){
			dZ_1 = Math.abs(this.point[i-1][2] - this.point[i][2]);
			dZ_2 = Math.abs(this.point[i][2] - this.point[i+1][2]);
	}
	
	if(dZ_1 < dZ_2){
			if (Math.abs(dZ_2 - dZ_1) > (dZ_1 * dZ_1 ) ){ //if it doesn't grow linearly then.. 
				return  1.0;//we consider just the first two points
			}
		}
	if(dZ_1 > dZ_2){ 
			if (Math.abs(dZ_2 - dZ_1) > (dZ_2 * dZ_2 ) ){ //if it doesn't grow linearly then.. 
				return  -1.0; //we consider just the last two points
			}
	}
	
	return 0.0;
}

//////////////////////////////////////////////////////
// Returns the points interpolated with Hermite curves
//
Inzomia_Animator.prototype.getHermPoint = function(i,t){

		nextindex = (i+1)%this.point.length;
		afternextindex = (i+2)%this.point.length;
		p   =  this.point;     //copy of the control-point array 
		a   =   0.0;           // tension 
		b1  = this.getBias( i );  // bias considering the (i-1,i,i+1) points
		b2  = this.getBias(nextindex );  // bias considering the (i,i+1,i+2) points
		
		//b =   0.99; //small,small,big 
		//b = -	0.99; // big,small.small	
		k1  =  (1-a)*(1+b1)/2;
		k2  =  (1-a)*(1-b1)/2;

		k3  =  (1-a)*(1+b2)/2;
		k4  =  (1-a)*(1-b2)/2;


		p1x =  this.point[i][0]; 
		p1y =  this.point[i][1];      
		p1z =  this.point[i][2];

		p2x =  this.point[nextindex][0];
		p2y =  this.point[nextindex][1];
		p2z =  this.point[nextindex][2];
 
		if( (i >= 1) && (i< (this.point.length -2) )){ 
			//mi = (1-a)(1+b)/2*(p[i] - p[i-1]) + (1-a)(1-b)/2*(p[i+1]-p[i])
			dp1x =  k1*(p[i][0] - p[i-1][0]) + k2*(p[nextindex][0] - p[i][0]); 
			dp1y =  k1*(p[i][1] - p[i-1][1]) + k2*(p[nextindex][1] - p[i][1]);
			dp1z =  k1*(p[i][2] - p[i-1][2]) + k2*(p[nextindex][2] - p[i][2]);
			
			dp2x =  k3*(p[nextindex][0] - p[i][0]) + k4*(p[afternextindex][0] - p[nextindex][0]);
			dp2y =  k3*(p[nextindex][1] - p[i][1]) + k4*(p[afternextindex][1] - p[nextindex][1]);
			dp2z =  k3*(p[nextindex][2] - p[i][2]) + k4*(p[afternextindex][2] - p[nextindex][2]);
		}

		else { // boundary cases
			if (this.point.length == 2){	
				// simply drop first coord if we are considering the first control point
				dp1x =  k2*(p[nextindex][0] - p[i][0]); 
				dp1y =  k2*(p[nextindex][1] - p[i][1]); 
				dp1z =  k2*(p[nextindex][2] - p[i][2]);
		
				//simply drop last coord if we are considering the last control point
				dp2x =  k3*(p[nextindex][0] - p[i][0]);
				dp2y =  k3*(p[nextindex][1] - p[i][1]);
				dp2z =  k3*(p[nextindex][2] - p[i][2]);
			}
			else if ( i == 0 ) { 
				// simply drop first coord if we are considering the first control point
				
				if (this.dowarp)
				{
					warp = this.point.length-1;
					dp1x =  k1*(p[i][0] - p[warp][0]) + k2*(p[nextindex][0] - p[i][0]); 
					dp1y =  k1*(p[i][1] - p[warp][1]) + k2*(p[nextindex][1] - p[i][1]);
					dp1z =  k1*(p[i][2] - p[warp][2]) + k2*(p[nextindex][2] - p[i][2]);
				}
				else
				{
					dp1x =  k2*(p[i+1][0] - p[i][0]); 
					dp1y =  k2*(p[i+1][1] - p[i][1]); 
					dp1z =  k2*(p[i+1][2] - p[i][2]); 
				}
								
				dp2x =  k3*(p[nextindex][0] - p[i][0]) + k4*(p[afternextindex][0] - p[nextindex][0]);
				dp2y =  k3*(p[nextindex][1] - p[i][1]) + k4*(p[afternextindex][1] - p[nextindex][1]);
				dp2z =  k3*(p[nextindex][2] - p[i][2]) + k4*(p[afternextindex][2] - p[nextindex][2]);
			}
			else if (i >= (this.point.length - 2)){
				
				dp1x =  k1*(p[i][0] - p[i-1][0]) + k2*(p[nextindex][0] - p[i][0]); 
				dp1y =  k1*(p[i][1] - p[i-1][1]) + k2*(p[nextindex][1] - p[i][1]);
				dp1z =  k1*(p[i][2] - p[i-1][2]) + k2*(p[nextindex][2] - p[i][2]);


				if (this.dowarp)
				{
					warp = (i+2)%this.point.length;
					dp2x =  k3*(p[nextindex][0] - p[i][0]) + k4*(p[warp][0] - p[nextindex][0]);
					dp2y =  k3*(p[nextindex][1] - p[i][1]) + k4*(p[warp][1] - p[nextindex][1]);
					dp2z =  k3*(p[nextindex][2] - p[i][2]) + k4*(p[warp][2] - p[nextindex][2]);
				}
				else
				{
					//simply drop last coord if we are considering the last control point
					dp2x =  k3*(p[nextindex][0] - p[i][0]);
					dp2y =  k3*(p[nextindex][1] - p[i][1]);
					dp2z =  k3*(p[nextindex][2] - p[i][2]);
				}
			}
		}
	
		// 0<=t<=1 initially =0
	    t2=t*t;             
	    t3=t*t*t;           
		// Calculate the first x,y Hermit coords 
		px =  p1x*(2*t3-3*t2+1)
			  + p2x*(-2*t3+3*t2)
			  + dp1x*(t3-2*t2+t)
			  + dp2x*(t3-t2); 		     
		py =  p1y*(2*t3-3*t2+1)
			  + p2y*(-2*t3+3*t2)
			  + dp1y*(t3-2*t2+t)
			  + dp2y*(t3-t2);		

		pz =  p1z*(2*t3-3*t2+1)
			  + p2z*(-2*t3+3*t2)
			  + dp1z*(t3-2*t2+t)
			  + dp2z*(t3-t2);

		return new Array([px,py,pz]);
}

////////////////////////
// Returns the IZ object
//
Inzomia_Animator.prototype.getIZ = function(){return this.IZ;}

/////////////////////////////////////////////////////
// SET PRECISION : 
//			set the the number of steps(/subdivisions)
Inzomia_Animator.prototype.setPrecision = function(value){
	if ((value > 0) && (value <= Inzomia_Animator.prototype.MAXSTEPS)){  
		this.steps = value;         // new subdivisions between 2 control point
		this.step = 1.0/this.steps; // calculate new increment 
	}
	else {
		alert('The precision value is Out of Range!\n 0 < Precision <= '+ Inzomia_Animator.MAXSTEPS);
	}	
}

/////////////////////////////////////////////////////
// RWD Move the picture to the previous control point
//
Inzomia_Animator.prototype.rwd = function(){
	if(! isEmpty(this.point)){
		if (this.i > 0){
			this.drawPoint_i(this.IZ,this.point,--this.i);//!Note this.i is decremented here!
		}
	}
}

////////////////////////////////////////////////////////
// MOVE the object according to the interpolating  curve
//
Inzomia_Animator.prototype.move = function(){
	if (this.running == false)
	  return;
	this.domove();
}

// Do the actual move regardless of running state
Inzomia_Animator.prototype.domove = function(){
	// if we have at least 2 control points we can 
	// have Hermit curves

	lastpoint = this.point.length - 2;
	if (this.dowarp)
		lastpoint++;

	if (this.j>this.steps ){ // we start with the next 2 points
				this.j = 0;  // reset j
				this.i++;    // next starting point
				this.t = 0;  // reset the parameter
	}
	if ( this.i <= (lastpoint)){

		if (this.t == 0 && this.i != 0){
				// we don't want to draw twice in the same position
				this.t = this.t + this.step;
				this.j ++;
		}	
		// Calculate the  Hermit coords
		this.p =  this.getHermPoint(this.i,this.t);
		// draw the IZ picture
		this.draw(this.IZ,this.p[0][0],this.p[0][1],this.p[0][2]);		
		// update the parameters for the next iteration
		this.t = this.t + this.step;
		this.j++;
	}
	else {
		// Finish! ( we don't have enough control points left to interpolate) 		
		if (! this.loop){
			this.stop(); // simply stop if we are not looping
		}
		else{
			if (this.dowarp)
			{
				this.j = 0;  // reset j
				this.i = 0;    // next starting point
				this.t = 0;  // reset the parameter
			}
			else
			{
				this.init();
				if (this.pingpong){//set the loop in the opposite direction
					this.point.reverse();
					this.reverseP = !this.reverseP;
				}
			}
		}
	}
}

/////////////////////////////////////////////////
// FWD Move the picture to the next control point
//
Inzomia_Animator.prototype.fwd = function(){
	if (! isEmpty(this.point)){ //if it's not empty
		if (this.i < this.point.length - 1){
			this.drawPoint_i(this.IZ,this.point,++this.i);//!Note this.i is incremented here!
		}
	}
}

/////////////////////////////////////////////////////////////////////////
//END OF SEQUENCE :
//	Returns true if the current control point is the last of the sequence 
//
Inzomia_Animator.prototype.endOfSequence= function(){ 
	return (this.i == (this.point.length-1));
}

////////////////////////////////////////////////
//INIT initialize the indeces for the animation
//
Inzomia_Animator.prototype.init = function(){
	this.i = 0;
	this.j = 0;
	this.t = 0;
}

/////////////////////////////////////////////
// STOP the animation in the current position
//
Inzomia_Animator.prototype.stop = function(){
	this.loop     = false;          // quit any possible loop 
	this.pingpong = false;
	if (this.reverseP){				// restore the initial control points vector
		this.point.reverse();
		this.reverseP = false;
	}
	this.running = false;
}

Inzomia_Animator.prototype.play = function(){
	this.running = true;
}

//////////////////////////////////////////////////////////
// SET LOOP POS : set the conditions to loop 
//				  from the first to the last control point
//
Inzomia_Animator.prototype.setLoopPos = function( warp ){
	this.dowarp = warp;
	this.stop();
	if (this.reverseP) {	//reverse back if we were playing in the opposite direction
		this.point.reverse();
		this.reverseP = false;
	}
	this.loopType = "Pos";
	this.loop     = true;      //set the loop condition
	this.pingpong = false;
}

//////////////////////////////////////////////////////////
// SET LOOP NEG : set the conditions to loop 
//				  from the first to the last control point
//
Inzomia_Animator.prototype.setLoopNeg = function( warp ){
	this.dowarp = warp;
	this.stop();
	if (!this.reverseP) {	//reverse back if we were playing in the opposite  direction
		this.point.reverse();
		this.reverseP = true;
	}
	this.loopType = "Neg";
	this.loop     = true;      //set the loop condition
	this.pingpong = false;
}

/////////////////////////////////////////////////////////////
// SET LOOP PING PONG : set the conditions for ping pong loop 
//
Inzomia_Animator.prototype.setLoopPingPong = function(){
	this.dowarp = false;
	this.stop();
	this.loopType = "PingPong"; 
	this.pingpong = true;  //set the ping pong condition
	this.loop     = true;  //set the loop condition
}

////////////////////////////////////////////////
//RESET put the picture in the starting position
//
Inzomia_Animator.prototype.reset = function(){
	if (! isEmpty(this.point)){
		this.stop();
		this.init();
		this.domove();		
	}
}

