//<pre>
import java.awt.*;
import java.awt.event.*;

public class Animation3D extends BufferedApplet
{
   	int width = 0, height = 0;
   	Color shapeColor = Color.red;
   	
   	double parallax = 0.42;					//  separation between cubes
   	double offset = 0.01;
   	double c = 0.2;							//	size of cube, zoom
   	double zoom = 1.0;
   	
   	// creates cube from 25 points
   	double points[][][] = {	{{c,c,c},{c,-1*c,c},{-1*c,-1*c,c},{-1*c,c,c}},
   							{{c,c,-1*c},{c,-1*c,-1*c},{-1*c,-1*c,-1*c},{-1*c,c,-1*c}},
   							{{c,c,c},{c,-1*c,c},{c,-1*c,-1*c},{c,c,-1*c}},
   							{{-1*c,c,c},{-1*c,-1*c,c},{-1*c,-1*c,-1*c},{-1*c,c,-1*c}},
   							{{c,c,c},{-1*c,c,c},{-1*c,c,-1*c},{c,c,-1*c}},
   							{{c,-1*c,c},{-1*c,-1*c,c},{-1*c,-1*c,-1*c},{c,-1*c,-1*c}},
   							{{c,c,c},{c,c,-1*c}}};
   							
	double a[] = {0,0,0}, b[] = {0,0,0};

	
	Matrix3D t1Matrix;							// 	right eye perspective
	Matrix3D t2Matrix;							//  left eye perspective
	Matrix3D vMatrix;							//	"orientation matrix"
	Matrix3D aMatrix;							//	"animation matrix"
	Matrix3D vOldMatrix;
	
	boolean spin = true;						// 	controls continuous spinning -- "on/off"
		
	public Animation3D () {
		t1Matrix = new Matrix3D();
		t2Matrix = new Matrix3D();

		vMatrix = new Matrix3D();
		aMatrix = new Matrix3D();
		
		vOldMatrix = new Matrix3D();
		
		aMatrix.yRotationMatrix(0.1);
		
		t1Matrix.translationMatrix(0.1,0.0,0.0);
		t2Matrix.translationMatrix(-0.1,0.0,0.0);

		vMatrix.xRotationMatrix(0.3);
		t1Matrix.multiply(vMatrix);
		t2Matrix.multiply(vMatrix);
		
		vOldMatrix.copy(vMatrix);
		
		requestFocus();
		
		//tMatrix.print();
	}

	public void render(Graphics g) {

		int theta = 0;
		double time = System.currentTimeMillis() / 1000.0;

		if (width == 0) {
			width  = bounds().width;
			height = bounds().height;
		}

		g.setColor(Color.black);
		g.fillRect(0, 0, width, height);
		
		animate();
		
		g.setColor(Color.yellow);
		
		
		for (int i = 0 ; i < points.length ; i++) {				// LOOP THROUGH ALL THE SHAPES

			for (int j = 1 ; j < points[i].length ; j++) { 		// LOOP THROUGH ALL THE LINES IN THE SHAPE
				t1Matrix.transform(points[i][j-1], a);         	// TRANSFORM BOTH ENDPOINTS OF LINE
				t1Matrix.transform(points[i][j  ], b);
				g.drawLine(x(a[0]), y(a[1]), x(b[0]), y(b[1]));	// DRAW ONE LINE ON THE SCREEN
				t2Matrix.transform(points[i][j-1], a);         	// TRANSFORM BOTH ENDPOINTS OF LINE
				t2Matrix.transform(points[i][j  ], b);
				g.drawLine(x(a[0]), y(a[1]), x(b[0]), y(b[1]));	// DRAW ONE LINE ON THE SCREEN

      		}
      	}
      	
		animating = true;

	}
	
	

	public void animate( ) {

	
		// t1 -- right eye, t2 -- left eye
		t1Matrix.identityMatrix();
		t2Matrix.identityMatrix();
	
		// continuously rotate each object .01 rads
		if (spin == true) {
			aMatrix.rotateY(0.01);
		}
		
		t1Matrix.multiply(aMatrix);								
		t2Matrix.multiply(aMatrix);

		t1Matrix.multiply(vMatrix);
		t2Matrix.multiply(vMatrix);
		
		// zoom
		t1Matrix.scale(zoom, zoom, zoom);						
		t2Matrix.scale(zoom, zoom, zoom);

		t1Matrix.rotateY(offset);								// rotate right cube a little more
		t2Matrix.rotateY(-1*offset);							// rotate left cube a little less

		t1Matrix.translate(parallax,0.0,0.0);
		t2Matrix.translate(-1*parallax,0.0,0.0);


	}

   	int x(double t) {  // CONVERT X COORDINATE TO SCREEN PIXELS
   		return width/2 + (int)(t*width/4); 
   	}
   	
   	int y(double t) { // CONVERT Y COORDINATE TO SCREEN PIXELS
   		return height/2 + (int)(t*height/4); 
   	} 

	
   	public boolean mouseUp(Event e, int x, int y) {
		
		vOldMatrix.copy(vMatrix);
		spin = (spin != true);									// toggle spin on/off
		damage = true;
      	return true;
   	}
   
   	public boolean mouseDrag(Event e, int x, int y) {
		Matrix3D temp = new Matrix3D();
		
		vMatrix.copy(vOldMatrix);
		temp.yRotationMatrix(Math.PI * (x + width / 2) / (width / 2));
		vMatrix.multiply(temp);
		temp.xRotationMatrix(Math.PI * (y + height / 2) / (height / 2));
 		vMatrix.multiply(temp);
		
	 	damage = true;
	 	return true;
   	}
   	
   	// handle key events
   	public boolean keyDown(Event e, int key) {
      switch (key) {
      	case '\u03EC': zoom += 0.05; break;						// UP ARROW -- zoom in
      	case '\u03ED': zoom -= 0.05; break;						// DOWN ARROW -- zoom out
      	case '\u03EE': parallax = parallax - 0.02;	break;		// LEFT ARROW -- decrease parallax
      	case '\u03EF': parallax = parallax + 0.02;	break;		// RIGHT ARROW -- increase parallax
      	case '.': offset = offset - 0.001;	break;				// RIGHT ARROW -- increase parallax
      	case '/': offset = offset + 0.001;	break;				// RIGHT ARROW -- increase parallax

		}
		System.out.println("scale is " + c);
		System.out.println("parallax is " + parallax);
		System.out.println("offset is " + offset);

		return true;
	}

}
