//========================================================
// Transforms - a simple applet demonstrating transformations
// Written by Chris DeCoro - cdecoro@cat.nyu.edu
// Sept. 9, 2002
//========================================================

import java.util.*;
import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import Geometry.*;

class Vertex
{
	Vector3 pos=null;
	Vector3 relPos=null;
	Vector3 nowPos=null;
	Vector3 normal=null;
	Color color=null;
	double[] texCoord = new double[2];
	int bone=-1;
}

class Triangle
{
	int texID;
	Vertex vtx[] = new Vertex[3];
}

class Texture
{
	int id;
	String name;
	
}


class Bitmap
{
	int fileSize;
	int width;
	int height;
	int bitsPerPixel;
	int compression;
	int[] colorTable=null;
	int[] indices=null;
	int[] colors=null;
}

public class Kinematics extends ThreeDimensionalApplet
{
	//vertices[] and faces[] contain the currently loaded geometry
	private Vector3[] vertices;
	//private Face[] faces;

	//Given a URL, opens an Internet connection and returns a stream
	InputStream openModel(String fileURL)
	{
		try
		{
			URL url = new URL(fileURL);
			return url.openStream();
		}
		catch(Exception e)
		{
			System.out.println(e);
			throw new RuntimeException("Error in reading file");
		}
	}

	double degree(double radians)
	{
		return radians * 180 / Math.PI;
	}

	//function readShortLE
	//will read a short value in little endian format, and will then
	//convert into Java bigendian format
	int readShortLE(DataInput in) throws IOException 
	{
		byte a = in.readByte(); //least significant
		byte b = in.readByte(); //most significant

		return (b << 8) | a;
	}

	int readIntLE(DataInput in) throws IOException 
	{
		byte a = in.readByte(); //least significant
		byte b = in.readByte(); 
		byte c = in.readByte(); 
		byte d = in.readByte(); //most significant

		//return (d << 24) &255 | (c << 16) &255 | (b << 8) &255 | a &255;
		return ((d&255) << 24)  | ((c&255) << 16)  | ((b&255) << 8)  | a &255;
	}

	Bitmap readTexture(InputStream stream)
	{
		try
		{
			DataInputStream in = new DataInputStream(stream);
			Bitmap bmp = new Bitmap();
			int datum;

			//Read the file signature (size 2, off 0);
			datum = readShortLE(in);
			if( datum != 19778 ) //BM
				throw new RuntimeException("Bitmpa file signature not found");


			//Read the file size; (size 4, off 2)
			readIntLE(in);

			//Read both reserved fields (size 4, off 6)
			readShortLE(in);
			readShortLE(in);

			//Read data offset (size 4, off 10)
			readIntLE(in);

			//Read INFOHEADER size (size 4, off 14, start of BITMAPINFO header)
			//Color table will be found at 14 + headerSize;
			int headerSize = readIntLE(in);

			//Read width, height (size 8, off 18)
			bmp.width = readIntLE(in);
			bmp.height = readIntLE(in);

			//Read image planes (size 2, off 26)
			readShortLE(in);

			//Read bits per pixel (size 2, off 28)
			bmp.bitsPerPixel = readShortLE(in);
			if( bmp.bitsPerPixel == 8 )
				bmp.colorTable = new int[256];
			else if( bmp.bitsPerPixel == 24 )
				bmp.colorTable = null;
			else
				throw new RuntimeException("Unknown number of colors in pallete");

			//Read image compression, image size (size 8, off 30)
			readIntLE(in);
			readIntLE(in);

			//Read 4 junk fields (size 16, off 38)
			readIntLE(in);
			readIntLE(in);
			readIntLE(in);
			readIntLE(in);

			//We are now at offset 54 , read over the rest of the header
			for(int i=0; i<(14+headerSize)-54; i++)
				in.readByte();

		

			//Finally, we have the image data itself
			//We do different things for palleted vs. full color
			int dataSize = bmp.width*bmp.height;
			bmp.colors = new int[dataSize];
			if( bmp.bitsPerPixel == 8 )
			{
				//Next is the color table - only read if 8bpp, thus 256 in size
				for(int i=0; i<256; i++)
				{
					byte red, green, blue;
					blue = in.readByte();
					green = in.readByte();
					red = in.readByte();
					in.readByte();
					bmp.colorTable[i] = pack(red, green, blue);
				}
		
				bmp.indices = new int[dataSize];
				bmp.colors = new int[dataSize];
				for(int i=0; i<dataSize; i++)
				{
					bmp.indices[i] = in.readByte() & 255;
					bmp.colors[i] = bmp.colorTable[bmp.indices[i]];
				}

			}
			else if( bmp.bitsPerPixel == 24 )
			{
				for(int i=0; i<dataSize; i++)
				{
					byte red, green, blue;
					blue = in.readByte();
					green = in.readByte();
					red = in.readByte();
					in.readByte();
					bmp.colors[i] = pack(red, green, blue);
				}
			}
			else
			{
				throw new RuntimeException("Oops, how did we get here?");
			}

			return bmp;
		} 
		catch( IOException e )
		{
			throw new RuntimeException("Could not read bitmap");		
		}
	}

	//Given a stream, downloads the model, parses it, and fills in vertices[]/faces[]

	//This will traverse through the bone structure, and for each vertex connected to the current bone,
	//  it will determine the relative position of that vertex in the bone's coordinate system
	//We currently do an exhaustive search, while we should really sort all the stuff
	int tris = 0;
	
	private boolean initialized = false;
	

	//Changes the current model
	//Called from outside of the Java applet (by the scripting in the page)
	public void setModel(String model)
	{
		System.out.println(model);
		//readModel(openModel(model), theModel, 0);
		setDamage();
	}
	

	Bitmap bmp;

	double[][] bezier = 
		{             // Bezier basis matrix
			{-1, 3,-3, 1},
			{ 3,-6, 3, 0},
			{-3, 3, 0, 0},
			{ 1, 0, 0, 0}
		};

	double[][] hermite   = 
		{             // Hermite basis matrix
						   { 2,-2, 1, 1},
						   {-3, 3,-2,-1},
						   { 0, 0, 1, 0},
						   { 1, 0, 0, 0}
					   };

	double[][] bspline = 
		{               // B-spline basis matrix
						 {-1/6., 1/2.,-1/2., 1/6.},
						 { 1/2.,-1.  , 1/2., 0   },
						 {-1/2., 0   , 1/2., 0   },
						 { 1/6., 2/3., 1/6., 0   }
					 };

	double[][] catmull = 
		{             // Catmull-Rom basis matrix
						   {-0.5,  1.5, -1.5, 0.5},
						   { 1  , -2.5,  2  ,-0.5},
						   {-0.5,  0  ,  0.5, 0  },
						   { 0  ,  1  ,  0  , 0  }
					   };

	Matrix4x4 charecter = new Matrix4x4(bezier);

	double teapot_data[][] = {
{
	-80.00,   0.00,  30.00,      -80.00, -44.80,  30.00,      -44.80, -80.00,  30.00,        0.00, -80.00,  30.00,
	-80.00,   0.00,  12.00,      -80.00, -44.80,  12.00,      -44.80, -80.00,  12.00,        0.00, -80.00,  12.00,
	-60.00,   0.00,   3.00,      -60.00, -33.60,   3.00,      -33.60, -60.00,   3.00,        0.00, -60.00,   3.00,
	-60.00,   0.00,   0.00,      -60.00, -33.60,   0.00,      -33.60, -60.00,   0.00,        0.00, -60.00,   0.00,
},
{
	0.00, -80.00,  30.00,       44.80, -80.00,  30.00,       80.00, -44.80,  30.00,       80.00,   0.00,  30.00,
	0.00, -80.00,  12.00,       44.80, -80.00,  12.00,       80.00, -44.80,  12.00,       80.00,   0.00,  12.00,
	0.00, -60.00,   3.00,       33.60, -60.00,   3.00,       60.00, -33.60,   3.00,       60.00,   0.00,   3.00,
	0.00, -60.00,   0.00,       33.60, -60.00,   0.00,       60.00, -33.60,   0.00,       60.00,   0.00,   0.00,
},
{
	-60.00,   0.00,  90.00,      -60.00, -33.60,  90.00,      -33.60, -60.00,  90.00,        0.00, -60.00,  90.00,
	-70.00,   0.00,  69.00,      -70.00, -39.20,  69.00,      -39.20, -70.00,  69.00,        0.00, -70.00,  69.00,
	-80.00,   0.00,  48.00,      -80.00, -44.80,  48.00,      -44.80, -80.00,  48.00,        0.00, -80.00,  48.00,
	-80.00,   0.00,  30.00,      -80.00, -44.80,  30.00,      -44.80, -80.00,  30.00,        0.00, -80.00,  30.00,
},
{
	0.00, -60.00,  90.00,       33.60, -60.00,  90.00,       60.00, -33.60,  90.00,       60.00,   0.00,  90.00,
	0.00, -70.00,  69.00,       39.20, -70.00,  69.00,       70.00, -39.20,  69.00,       70.00,   0.00,  69.00,
	0.00, -80.00,  48.00,       44.80, -80.00,  48.00,       80.00, -44.80,  48.00,       80.00,   0.00,  48.00,
	0.00, -80.00,  30.00,       44.80, -80.00,  30.00,       80.00, -44.80,  30.00,       80.00,   0.00,  30.00,
},
{
	-56.00,   0.00,  90.00,      -56.00, -31.36,  90.00,      -31.36, -56.00,  90.00,        0.00, -56.00,  90.00,
	-53.50,   0.00,  95.25,      -53.50, -29.96,  95.25,      -29.96, -53.50,  95.25,        0.00, -53.50,  95.25,
	-57.50,   0.00,  95.25,      -57.50, -32.20,  95.25,      -32.20, -57.50,  95.25,        0.00, -57.50,  95.25,
	-60.00,   0.00,  90.00,      -60.00, -33.60,  90.00,      -33.60, -60.00,  90.00,        0.00, -60.00,  90.00,
},
{
	0.00, -56.00,  90.00,       31.36, -56.00,  90.00,       56.00, -31.36,  90.00,       56.00,   0.00,  90.00,
	0.00, -53.50,  95.25,       29.96, -53.50,  95.25,       53.50, -29.96,  95.25,       53.50,   0.00,  95.25,
	0.00, -57.50,  95.25,       32.20, -57.50,  95.25,       57.50, -32.20,  95.25,       57.50,   0.00,  95.25,
	0.00, -60.00,  90.00,       33.60, -60.00,  90.00,       60.00, -33.60,  90.00,       60.00,   0.00,  90.00,
},
{
	80.00,   0.00,  30.00,       80.00,  44.80,  30.00,       44.80,  80.00,  30.00,        0.00,  80.00,  30.00,
	80.00,   0.00,  12.00,       80.00,  44.80,  12.00,       44.80,  80.00,  12.00,        0.00,  80.00,  12.00,
	60.00,   0.00,   3.00,       60.00,  33.60,   3.00,       33.60,  60.00,   3.00,        0.00,  60.00,   3.00,
	60.00,   0.00,   0.00,       60.00,  33.60,   0.00,       33.60,  60.00,   0.00,        0.00,  60.00,   0.00,
},
{
	0.00,  80.00,  30.00,      -44.80,  80.00,  30.00,      -80.00,  44.80,  30.00,      -80.00,   0.00,  30.00,
	0.00,  80.00,  12.00,      -44.80,  80.00,  12.00,      -80.00,  44.80,  12.00,      -80.00,   0.00,  12.00,
	0.00,  60.00,   3.00,      -33.60,  60.00,   3.00,      -60.00,  33.60,   3.00,      -60.00,   0.00,   3.00,
	0.00,  60.00,   0.00,      -33.60,  60.00,   0.00,      -60.00,  33.60,   0.00,      -60.00,   0.00,   0.00,
},
{
	60.00,   0.00,  90.00,       60.00,  33.60,  90.00,       33.60,  60.00,  90.00,        0.00,  60.00,  90.00,
	70.00,   0.00,  69.00,       70.00,  39.20,  69.00,       39.20,  70.00,  69.00,        0.00,  70.00,  69.00,
	80.00,   0.00,  48.00,       80.00,  44.80,  48.00,       44.80,  80.00,  48.00,        0.00,  80.00,  48.00,
	80.00,   0.00,  30.00,       80.00,  44.80,  30.00,       44.80,  80.00,  30.00,        0.00,  80.00,  30.00,
},
{
	0.00,  60.00,  90.00,      -33.60,  60.00,  90.00,      -60.00,  33.60,  90.00,      -60.00,   0.00,  90.00,
	0.00,  70.00,  69.00,      -39.20,  70.00,  69.00,      -70.00,  39.20,  69.00,      -70.00,   0.00,  69.00,
	0.00,  80.00,  48.00,      -44.80,  80.00,  48.00,      -80.00,  44.80,  48.00,      -80.00,   0.00,  48.00,
	0.00,  80.00,  30.00,      -44.80,  80.00,  30.00,      -80.00,  44.80,  30.00,      -80.00,   0.00,  30.00,
},
{
	56.00,   0.00,  90.00,       56.00,  31.36,  90.00,       31.36,  56.00,  90.00,        0.00,  56.00,  90.00,
	53.50,   0.00,  95.25,       53.50,  29.96,  95.25,       29.96,  53.50,  95.25,        0.00,  53.50,  95.25,
	57.50,   0.00,  95.25,       57.50,  32.20,  95.25,       32.20,  57.50,  95.25,        0.00,  57.50,  95.25,
	60.00,   0.00,  90.00,       60.00,  33.60,  90.00,       33.60,  60.00,  90.00,        0.00,  60.00,  90.00,
},
{
	0.00,  56.00,  90.00,      -31.36,  56.00,  90.00,      -56.00,  31.36,  90.00,      -56.00,   0.00,  90.00,
	0.00,  53.50,  95.25,      -29.96,  53.50,  95.25,      -53.50,  29.96,  95.25,      -53.50,   0.00,  95.25,
	0.00,  57.50,  95.25,      -32.20,  57.50,  95.25,      -57.50,  32.20,  95.25,      -57.50,   0.00,  95.25,
	0.00,  60.00,  90.00,      -33.60,  60.00,  90.00,      -60.00,  33.60,  90.00,      -60.00,   0.00,  90.00,
},
{
	-64.00,   0.00,  75.00,      -64.00,  12.00,  75.00,      -60.00,  12.00,  84.00,      -60.00,   0.00,  84.00,
	-92.00,   0.00,  75.00,      -92.00,  12.00,  75.00,     -100.00,  12.00,  84.00,     -100.00,   0.00,  84.00,
	-108.00,   0.00,  75.00,     -108.00,  12.00,  75.00,     -120.00,  12.00,  84.00,     -120.00,   0.00,  84.00,
	-108.00,   0.00,  66.00,     -108.00,  12.00,  66.00,     -120.00,  12.00,  66.00,     -120.00,   0.00,  66.00,
},
{
	-60.00,   0.00,  84.00,      -60.00, -12.00,  84.00,      -64.00, -12.00,  75.00,      -64.00,   0.00,  75.00,
	-100.00,   0.00,  84.00,     -100.00, -12.00,  84.00,      -92.00, -12.00,  75.00,      -92.00,   0.00,  75.00,
	-120.00,   0.00,  84.00,     -120.00, -12.00,  84.00,     -108.00, -12.00,  75.00,     -108.00,   0.00,  75.00,
	-120.00,   0.00,  66.00,     -120.00, -12.00,  66.00,     -108.00, -12.00,  66.00,     -108.00,   0.00,  66.00,
},
{
	-108.00,   0.00,  66.00,     -108.00,  12.00,  66.00,     -120.00,  12.00,  66.00,     -120.00,   0.00,  66.00,
	-108.00,   0.00,  57.00,     -108.00,  12.00,  57.00,     -120.00,  12.00,  48.00,     -120.00,   0.00,  48.00,
	-100.00,   0.00,  39.00,     -100.00,  12.00,  39.00,     -106.00,  12.00,  31.50,     -106.00,   0.00,  31.50,
	-80.00,   0.00,  30.00,      -80.00,  12.00,  30.00,      -76.00,  12.00,  18.00,      -76.00,   0.00,  18.00,
},
{
	-120.00,   0.00,  66.00,     -120.00, -12.00,  66.00,     -108.00, -12.00,  66.00,     -108.00,   0.00,  66.00,
	-120.00,   0.00,  48.00,     -120.00, -12.00,  48.00,     -108.00, -12.00,  57.00,     -108.00,   0.00,  57.00,
	-106.00,   0.00,  31.50,     -106.00, -12.00,  31.50,     -100.00, -12.00,  39.00,     -100.00,   0.00,  39.00,
	-76.00,   0.00,  18.00,      -76.00, -12.00,  18.00,      -80.00, -12.00,  30.00,      -80.00,   0.00,  30.00,
},
{
	68.00,   0.00,  51.00,       68.00,  26.40,  51.00,       68.00,  26.40,  18.00,       68.00,   0.00,  18.00,
	104.00,   0.00,  51.00,      104.00,  26.40,  51.00,      124.00,  26.40,  27.00,      124.00,   0.00,  27.00,
	92.00,   0.00,  78.00,       92.00,  10.00,  78.00,       96.00,  10.00,  75.00,       96.00,   0.00,  75.00,
	108.00,   0.00,  90.00,      108.00,  10.00,  90.00,      132.00,  10.00,  90.00,      132.00,   0.00,  90.00,
},
{
	68.00,   0.00,  18.00,       68.00, -26.40,  18.00,       68.00, -26.40,  51.00,       68.00,   0.00,  51.00,
	124.00,   0.00,  27.00,      124.00, -26.40,  27.00,      104.00, -26.40,  51.00,      104.00,   0.00,  51.00,
	96.00,   0.00,  75.00,       96.00, -10.00,  75.00,       92.00, -10.00,  78.00,       92.00,   0.00,  78.00,
	132.00,   0.00,  90.00,      132.00, -10.00,  90.00,      108.00, -10.00,  90.00,      108.00,   0.00,  90.00,
},
{
	108.00,   0.00,  90.00,      108.00,  10.00,  90.00,      132.00,  10.00,  90.00,      132.00,   0.00,  90.00,
	112.00,   0.00,  93.00,      112.00,  10.00,  93.00,      141.00,  10.00,  93.75,      141.00,   0.00,  93.75,
	116.00,   0.00,  93.00,      116.00,   6.00,  93.00,      138.00,   6.00,  94.50,      138.00,   0.00,  94.50,
	112.00,   0.00,  90.00,      112.00,   6.00,  90.00,      128.00,   6.00,  90.00,      128.00,   0.00,  90.00,
},
{
	132.00,   0.00,  90.00,      132.00, -10.00,  90.00,      108.00, -10.00,  90.00,      108.00,   0.00,  90.00,
	141.00,   0.00,  93.75,      141.00, -10.00,  93.75,      112.00, -10.00,  93.00,      112.00,   0.00,  93.00,
	138.00,   0.00,  94.50,      138.00,  -6.00,  94.50,      116.00,  -6.00,  93.00,      116.00,   0.00,  93.00,
	128.00,   0.00,  90.00,      128.00,  -6.00,  90.00,      112.00,  -6.00,  90.00,      112.00,   0.00,  90.00,
},
{
	50.00,   0.00,  90.00,       50.00,  28.00,  90.00,       28.00,  50.00,  90.00,        0.00,  50.00,  90.00,
	52.00,   0.00,  90.00,       52.00,  29.12,  90.00,       29.12,  52.00,  90.00,        0.00,  52.00,  90.00,
	54.00,   0.00,  90.00,       54.00,  30.24,  90.00,       30.24,  54.00,  90.00,        0.00,  54.00,  90.00,
	56.00,   0.00,  90.00,       56.00,  31.36,  90.00,       31.36,  56.00,  90.00,        0.00,  56.00,  90.00,
},
{
	0.00,  50.00,  90.00,      -28.00,  50.00,  90.00,      -50.00,  28.00,  90.00,      -50.00,   0.00,  90.00,
	0.00,  52.00,  90.00,      -29.12,  52.00,  90.00,      -52.00,  29.12,  90.00,      -52.00,   0.00,  90.00,
	0.00,  54.00,  90.00,      -30.24,  54.00,  90.00,      -54.00,  30.24,  90.00,      -54.00,   0.00,  90.00,
	0.00,  56.00,  90.00,      -31.36,  56.00,  90.00,      -56.00,  31.36,  90.00,      -56.00,   0.00,  90.00,
},
{
	-50.00,   0.00,  90.00,      -50.00, -28.00,  90.00,      -28.00, -50.00,  90.00,        0.00, -50.00,  90.00,
	-52.00,   0.00,  90.00,      -52.00, -29.12,  90.00,      -29.12, -52.00,  90.00,        0.00, -52.00,  90.00,
	-54.00,   0.00,  90.00,      -54.00, -30.24,  90.00,      -30.24, -54.00,  90.00,        0.00, -54.00,  90.00,
	-56.00,   0.00,  90.00,      -56.00, -31.36,  90.00,      -31.36, -56.00,  90.00,        0.00, -56.00,  90.00,
},
{
	0.00, -50.00,  90.00,       28.00, -50.00,  90.00,       50.00, -28.00,  90.00,       50.00,   0.00,  90.00,
	0.00, -52.00,  90.00,       29.12, -52.00,  90.00,       52.00, -29.12,  90.00,       52.00,   0.00,  90.00,
	0.00, -54.00,  90.00,       30.24, -54.00,  90.00,       54.00, -30.24,  90.00,       54.00,   0.00,  90.00,
	0.00, -56.00,  90.00,       31.36, -56.00,  90.00,       56.00, -31.36,  90.00,       56.00,   0.00,  90.00,
},
{
	8.00,   0.00, 102.00,        8.00,   4.48, 102.00,        4.48,   8.00, 102.00,        0.00,   8.00, 102.00,
	16.00,   0.00,  96.00,       16.00,   8.96,  96.00,        8.96,  16.00,  96.00,        0.00,  16.00,  96.00,
	52.00,   0.00,  96.00,       52.00,  29.12,  96.00,       29.12,  52.00,  96.00,        0.00,  52.00,  96.00,
	52.00,   0.00,  90.00,       52.00,  29.12,  90.00,       29.12,  52.00,  90.00,        0.00,  52.00,  90.00,
},
{
	0.00,   8.00, 102.00,       -4.48,   8.00, 102.00,       -8.00,   4.48, 102.00,       -8.00,   0.00, 102.00,
	0.00,  16.00,  96.00,       -8.96,  16.00,  96.00,      -16.00,   8.96,  96.00,      -16.00,   0.00,  96.00,
	0.00,  52.00,  96.00,      -29.12,  52.00,  96.00,      -52.00,  29.12,  96.00,      -52.00,   0.00,  96.00,
	0.00,  52.00,  90.00,      -29.12,  52.00,  90.00,      -52.00,  29.12,  90.00,      -52.00,   0.00,  90.00,
},
{
	-8.00,   0.00, 102.00,       -8.00,  -4.48, 102.00,       -4.48,  -8.00, 102.00,        0.00,  -8.00, 102.00,
	-16.00,   0.00,  96.00,      -16.00,  -8.96,  96.00,       -8.96, -16.00,  96.00,        0.00, -16.00,  96.00,
	-52.00,   0.00,  96.00,      -52.00, -29.12,  96.00,      -29.12, -52.00,  96.00,        0.00, -52.00,  96.00,
	-52.00,   0.00,  90.00,      -52.00, -29.12,  90.00,      -29.12, -52.00,  90.00,        0.00, -52.00,  90.00,
},
{
	0.00,  -8.00, 102.00,        4.48,  -8.00, 102.00,        8.00,  -4.48, 102.00,        8.00,   0.00, 102.00,
	0.00, -16.00,  96.00,        8.96, -16.00,  96.00,       16.00,  -8.96,  96.00,       16.00,   0.00,  96.00,
	0.00, -52.00,  96.00,       29.12, -52.00,  96.00,       52.00, -29.12,  96.00,       52.00,   0.00,  96.00,
	0.00, -52.00,  90.00,       29.12, -52.00,  90.00,       52.00, -29.12,  90.00,       52.00,   0.00,  90.00,
},
{
	0.00,   0.00, 120.00,        0.00,   0.00, 120.00,        0.00,   0.00, 120.00,        0.00,   0.00, 120.00,
	32.00,   0.00, 120.00,       32.00,  18.00, 120.00,       18.00,  32.00, 120.00,        0.00,  32.00, 120.00,
	0.00,   0.00, 108.00,        0.00,   0.00, 108.00,        0.00,   0.00, 108.00,        0.00,   0.00, 108.00,
	8.00,   0.00, 102.00,        8.00,   4.48, 102.00,        4.48,   8.00, 102.00,        0.00,   8.00, 102.00,
},
{
	0.00,   0.00, 120.00,        0.00,   0.00, 120.00,        0.00,   0.00, 120.00,        0.00,   0.00, 120.00,
	0.00,  32.00, 120.00,      -18.00,  32.00, 120.00,      -32.00,  18.00, 120.00,      -32.00,   0.00, 120.00,
	0.00,   0.00, 108.00,        0.00,   0.00, 108.00,        0.00,   0.00, 108.00,        0.00,   0.00, 108.00,
	0.00,   8.00, 102.00,       -4.48,   8.00, 102.00,       -8.00,   4.48, 102.00,       -8.00,   0.00, 102.00,
},
{
	0.00,   0.00, 120.00,        0.00,   0.00, 120.00,        0.00,   0.00, 120.00,        0.00,   0.00, 120.00,
	-32.00,   0.00, 120.00,      -32.00, -18.00, 120.00,      -18.00, -32.00, 120.00,        0.00, -32.00, 120.00,
	0.00,   0.00, 108.00,        0.00,   0.00, 108.00,        0.00,   0.00, 108.00,        0.00,   0.00, 108.00,
	-8.00,   0.00, 102.00,       -8.00,  -4.48, 102.00,       -4.48,  -8.00, 102.00,        0.00,  -8.00, 102.00,
},
{
	0.00,   0.00, 120.00,        0.00,   0.00, 120.00,        0.00,   0.00, 120.00,        0.00,   0.00, 120.00,
	0.00, -32.00, 120.00,       18.00, -32.00, 120.00,       32.00, -18.00, 120.00,       32.00,   0.00, 120.00,
	0.00,   0.00, 108.00,        0.00,   0.00, 108.00,        0.00,   0.00, 108.00,        0.00,   0.00, 108.00,
	0.00,  -8.00, 102.00,        4.48,  -8.00, 102.00,        8.00,  -4.48, 102.00,        8.00,   0.00, 102.00,
} };


	
	//function constructBicubicCoeff
	//Given a control point matrix G, and a charecteristic matrix M,
	// will compute the bicubic coefficient matrix C
	void constructBicubicCoefficients(double[][] G, double[][] M, double[][] C) 
	{
		double[][] tmp = new double[4][4];

		for (int i = 0 ; i < 4 ; i++)    // tmp = G • MT
			for (int j = 0 ; j < 4 ; j++)
				for (int k = 0 ; k < 4 ; k++)
					tmp[i][j] += G[i][k] * M[j][k];
      
		for (int i = 0 ; i < 4 ; i++)    // C = M • tmp
			for (int j = 0 ; j < 4 ; j++)
				for (int k = 0 ; k < 4 ; k++)
					C[i][j] += M[i][k] * tmp[k][j];
	}

	Matrix4x4 makeGeometryMatrix(double[][] data, int patch, int coord)
	{
		Matrix4x4 G = new Matrix4x4(
			data[patch][0+coord], data[patch][3+coord], data[patch][6+coord], data[patch][9+coord],
			data[patch][12+coord], data[patch][15+coord], data[patch][18+coord], data[patch][21+coord],
			data[patch][24+coord], data[patch][27+coord], data[patch][30+coord], data[patch][33+coord],
			data[patch][36+coord], data[patch][39+coord], data[patch][42+coord], data[patch][45+coord] );
		return G;
	}

	double evalBicubic(Matrix4x4 Cvar, double u, double v) 
	{
		double C[][] = Cvar.array();

		return u * (u * (u * (v * (v * (v * C[0][0] + C[0][1]) + C[0][2]) + C[0][3])
			+ (v * (v * (v * C[1][0] + C[1][1]) + C[1][2]) + C[1][3]))
			+ (v * (v * (v * C[2][0] + C[2][1]) + C[2][2]) + C[2][3]))
			+ (v * (v * (v * C[3][0] + C[3][1]) + C[3][2]) + C[3][3]);
	}

	double du(Matrix4x4 Cvar, double u, double v)
	{
		Vector4 vBasis = new Vector4(v*v*v, v*v, v, 1);
		Vector4 duBasis = new Vector4(3*u*u, 2*u, 1, 0);

		Vector4 vC = Cvar.leftMul(vBasis);
		double retval = vC.dot(duBasis);
		return retval;
	}

	double dv(Matrix4x4 Cvar, double u, double v)
	{
		Vector4 dvBasis = new Vector4(3*v*v, 2*v, 1, 0);
		Vector4 uBasis = new Vector4(u*u*u, u*u, u, 1);

		Vector4 vC = Cvar.leftMul(dvBasis);
		double retval = vC.dot(uBasis);
		return retval;
	}

	Vector3 uTangent(Matrix4x4[] Cxyz, double u, double v)
	{
		return new Vector3( du(Cxyz[0], u, v), du(Cxyz[1], u, v), du(Cxyz[2], u, v) );
	}

	Vector3 vTangent(Matrix4x4[] Cxyz, double u, double v)
	{
		return new Vector3( dv(Cxyz[0], u, v), dv(Cxyz[1], u, v), dv(Cxyz[2], u, v) );
	}

	Vector3 normal(Matrix4x4[] Cxyz, double u, double v)
	{
		Vector3 vecDu = uTangent(Cxyz, u, v);
		Vector3 vecDv = vTangent(Cxyz, u, v);

		Vector3 retval = vecDv.cross(vecDu).normalize();
		return retval;
	}

	void renderPatchAsPoints(Matrix4x4[] G)
	{
		//Compute the coefficient matrix
		Matrix4x4[] C = new Matrix4x4[3];
		C[0] = charecter.mul(G[0]).mul(charecter.transpose());
		C[1] = charecter.mul(G[1]).mul(charecter.transpose());
		C[2] = charecter.mul(G[2]).mul(charecter.transpose());

		//Evaluate the patch over the domain
		begin(RENDER_POINTS);
		pointSize(4);
		color(new Color(255,0,0));
		for(double u=0.0; u<=1.0; u+=INCREMENT)
		{
			for(double v=0.0; v<=1.0; v+=INCREMENT)
			{
				Vector3 xyz = new Vector3(
					evalBicubic(C[0], u, v),
					evalBicubic(C[1], u, v),
					evalBicubic(C[2], u, v)
					);
					
				vertex( xyz.div(30) );
			}
		}	
		end();	
	}

	int SEGMENTS = 4;
	Vertex[][] computePatch(Matrix4x4[] G)
	{
		Vertex[][] grid = new Vertex[SEGMENTS+1][SEGMENTS+1];
		Matrix4x4 cT = charecter.transpose();
		
		Matrix4x4[] C = new Matrix4x4[3];
		C[0] = charecter.mul(G[0]).mul(cT);
		C[1] = charecter.mul(G[1]).mul(cT);
		C[2] = charecter.mul(G[2]).mul(cT);

		for(int countU=0; countU<=SEGMENTS; countU++)
		{
			double u = (double)countU / SEGMENTS;
			for(int countV=0; countV<=SEGMENTS; countV++)
			{
				double v = (double)countV / SEGMENTS;
				Vector3 xyz = new Vector3(
					evalBicubic(C[0], u, v),
					evalBicubic(C[1], u, v),
					evalBicubic(C[2], u, v)
					);
				grid[countU][countV] = new Vertex();
				grid[countU][countV].coords = xyz;
				grid[countU][countV].normal = normal(C, v, u);
			}
		}
		
		return grid;
	}

	static final double INCREMENT = 0.2;
	class Vertex
	{
		Vector3 coords;
		Vector3 normal;
	}

	Vertex[][][] vertexCache;
	Matrix4x4[][] geometryMatrices;

	void makeAllPatches()
	{
		//Extract the geometry matrices and compute the vertices
		for(int patch=0; patch<teapot_data.length; patch++)
		{
			Matrix4x4[] G = new Matrix4x4[3];
			G[0] = makeGeometryMatrix(teapot_data, patch, 0);
			G[1] = makeGeometryMatrix(teapot_data, patch, 1);
			G[2] = makeGeometryMatrix(teapot_data, patch, 2);
				
			//renderPatchAsPoints(G);	
			geometryMatrices[patch] = G;
			vertexCache[patch] = computePatch(G);
		}
	}

	//Renders the scene
	public void render() 
	{
		//On the first run, initialize using the following code
		if(!initialized)
		{
			ambientCoeff(0.8);   //0.3 , 0.7 also looked good
			diffuseCoeff(0.75);
			ambientAndDiffuseMaterial(201, 125, 73);
			ambientAndDiffuseColor(255, 255, 255);
			vertexCache = new Vertex[teapot_data.length][][];
			geometryMatrices = new Matrix4x4[teapot_data.length][];

			double maxAbs=Double.MIN_VALUE;
			for(int i=0; i<teapot_data.length; i++)
				for(int j=0; j<teapot_data[0].length; j++)
					if( Math.abs(teapot_data[i][j]) > maxAbs )
						maxAbs = Math.abs(teapot_data[i][j]);
					
			for(int i=0; i<teapot_data.length; i++)
				for(int j=0; j<teapot_data[0].length; j++)
					teapot_data[i][j] /= maxAbs;
			
			initialized = true;
			makeAllPatches();
		}
		
		//Whenever the display is damaged, redraw using this code
		if (damage) 
		{
			//Clear off the screen by drawing a big white box (this is glClear())
			identity();

			if(cullFace)
				enable(RENDER_CULL_FACE);

			//Stop gap measure until we fix that
			clear();

			//Transform the model based on the current settings
			double hw=bounds().width/2, hh=bounds().height/2;

			identity();

			transform( (new Matrix4x4())
					.translate(translateX+hw, translateY+hh,0)
					.scale(scaleX, scaleY, 1)
					.mul(rotationMatrix)
					.scale(1000, 1000, /*-*/ 1000)    );
					
			//int anim = theAnimation;
			//long fr = (long)(System.currentTimeMillis() / 50) % theModel.frameCount[anim];

			if(cullFace)
				enable(RENDER_CULL_FACE);
			else
				disable(RENDER_CULL_FACE);

			if(doLighting)
				enable(RENDER_LIGHTING);
			else
				disable(RENDER_LIGHTING);

			//enable(RENDER_TEXTURE);

			/*
			//Extract the geometry matrices and compute the vertices
			for(int patch=0; patch<teapot_data.length; patch++)
			{
				Matrix4x4[] G = new Matrix4x4[3];
				G[0] = makeGeometryMatrix(teapot_data, patch, 0);
				G[1] = makeGeometryMatrix(teapot_data, patch, 1);
				G[2] = makeGeometryMatrix(teapot_data, patch, 2);
				
				//renderPatchAsPoints(G);	
				vertexCache[patch] = computePatch(G);
			}
			*/

			//Render the patch as a set of triangles (should optimize to quads)
			
			for(int k=0; k<teapot_data.length; k++)
			{
				if(cullFace)
					enable(RENDER_CULL_FACE);
				else
					disable(RENDER_CULL_FACE);

				if(doLighting)
					enable(RENDER_LIGHTING);
				else
					disable(RENDER_LIGHTING);
				
				begin(RENDER_QUAD);
				Vertex[][] vtxs = vertexCache[k];
				for(int u=0; u<vtxs.length-1; u++)
				{
					for(int v=0; v<vtxs.length-1; v++)
					{
						/*
						//Triangle 1
						normal( vtxs[u][v].normal );
						vertex( vtxs[u][v].coords );

						normal( vtxs[u+1][v].normal );
						vertex( vtxs[u+1][v].coords );
						
						normal( vtxs[u+1][v+1].normal );
						vertex( vtxs[u+1][v+1].coords );

						//Triangle 2
						normal( vtxs[u][v].normal );
						vertex( vtxs[u][v].coords );

						normal( vtxs[u+1][v+1].normal );
						vertex( vtxs[u+1][v+1].coords );
						
						normal( vtxs[u][v+1].normal );
						vertex( vtxs[u][v+1].coords );
						*/

						normal( vtxs[u][v].normal );
						vertex( vtxs[u][v].coords );

						normal( vtxs[u+1][v].normal );
						vertex( vtxs[u+1][v].coords );

						normal( vtxs[u+1][v+1].normal );
						vertex( vtxs[u+1][v+1].coords );
						
						normal( vtxs[u][v+1].normal );
						vertex( vtxs[u][v+1].coords );
					}
				}
				end();
				//done with a patch
			}
				
			//Render the control grid as a set of quads
			if(drawControl)
			{
				disable(RENDER_LIGHTING);
				disable(RENDER_CULL_FACE);

				color(new Color(0, 240, 40));
				begin(RENDER_OPEN_QUAD);
				for(int k=0; k<teapot_data.length; k++)
				{
					for(int i=0; i<3; i++)
					{
						for(int j=0; j<3; j++)
						{
							Matrix4x4 mX, mY, mZ;
							mX = geometryMatrices[k][0];
							mY = geometryMatrices[k][1];
							mZ = geometryMatrices[k][2];
						
							vertex( mX.get(i,j), mY.get(i,j), mZ.get(i,j) );
							vertex( mX.get(i+1,j), mY.get(i+1,j), mZ.get(i+1,j) );
							vertex( mX.get(i+1,j+1), mY.get(i+1,j+1), mZ.get(i+1,j+1) );
							vertex( mX.get(i,j+1), mY.get(i,j+1), mZ.get(i,j+1) );
						}
					}
				}
				end();
			}

			//Clear out the context, ending the frame
			setContext(null);
			damage = true;
		}
	}

	boolean modified = false;
	int selectedK = -1;
	int selectedI = -1;
	int selectedJ = -1;

	//Stores the "old" values for each of the transformation factors, and sets the mouse-down pos
	//These old values are used to allow for dynamic updates while we drag the mouse
	public boolean mouseDown(Event e, int x, int y) 
	{		 
		boolean leftButton, centerButton, rightButton;
		centerButton = ((e.modifiers & e.ALT_MASK) > 0);
		rightButton = ((e.modifiers & e.META_MASK) > 0);
		leftButton = !centerButton && !rightButton;

		if( !modified )
		{
			downX = x;
			downY = y;

			oldTranslateX = translateX;
			oldTranslateY = translateY;

			oldScaleX = scaleX;
			oldScaleY = scaleY;

			oldTheta = theta; //I dont think we use this anymore
	
			oldRotationMatrix = rotationMatrix;
		}
		else
		{
			for(int k=0; k<teapot_data.length; k++)
			{
				/*
				for(int i=0; i<4; i++)
				{
					for(int j=0; j<4; j++)
					{
						Vector3 pt = new Vector3(geometryMatrices[k][0].get(i,j), 
							geometryMatrices[k][1].get(i,j), 
							geometryMatrices[k][2].get(i,j));
						Vector3 proj = project(pt);
						Vector3 down = new Vector3(x, y, proj.z());
						
						if( down.distance(proj) < 5 )
						{
							System.out.println("Hit point " + k + " " + i + " " + j );
							selectedK = k;
							selectedI = i;
							selectedJ = j;
						}
					}
				}
				*/
				for(int i=0; i<16; i++)
				{
					Vector3 pt = new Vector3(teapot_data[k][i*3+0], 
						teapot_data[k][i*3+1],
						teapot_data[k][i*3+2]);
						
					Vector3 proj = project(pt);
					Vector3 down = new Vector3(x, y, proj.z());
						
					if( down.distance(proj) < 5 )
					{
						System.out.println("Hit point " + k + " " + i  );
						selectedK = k;
						selectedI = i;
						//selectedJ = j;
					}
				}
			}
		}

		setDamage();
		return true;
	}

	//Here is where we actually perform the transformations
	public boolean mouseDrag(Event e, int x, int y) 
	{ 
		//See what buttons are down
		//This is a real mess, but I think its JDK1.0 compilant
		boolean leftButton, centerButton, rightButton, shiftDown;
		centerButton = ((e.modifiers & e.ALT_MASK) > 0);
		rightButton = ((e.modifiers & e.META_MASK) > 0);
		leftButton = !centerButton && !rightButton;
		shiftDown = ((e.modifiers & e.SHIFT_MASK) > 0);

		double hw=bounds().width/2, hh=bounds().height/2;

		if( modified && selectedK != -1 )
		{
			/*
			Vector3 pt = new Vector3(geometryMatrices[selectedK][0].get(selectedI,selectedJ), 
				geometryMatrices[selectedK][1].get(selectedI,selectedJ), 
				geometryMatrices[selectedK][2].get(selectedI,selectedJ));
				*/
			Vector3 pt = new Vector3(teapot_data[selectedK][3*selectedI+0],
				teapot_data[selectedK][3*selectedI+1],
				teapot_data[selectedK][3*selectedI+2]);

			Vector3 proj = project(pt);
			Vector3 mousePos = new Vector3(x, y, proj.z());
			Vector3 newPt = unProject(mousePos);

			teapot_data[selectedK][selectedI*3+0] = newPt.x();
			teapot_data[selectedK][selectedI*3+1] = newPt.y();
			teapot_data[selectedK][selectedI*3+2] = newPt.z();

			/*
			geometryMatrices[selectedK][0].set(selectedI,selectedJ,newPt.x());
			geometryMatrices[selectedK][1].set(selectedI,selectedJ,newPt.y());
			geometryMatrices[selectedK][2].set(selectedI,selectedJ,newPt.z());
			*/
																		makeAllPatches();
			return true;
		}

		//Virtual trackball rotation
		if(leftButton && !shiftDown)
		{
			double radius=hw;

			//Determine the center of the object in screen coords
			//This will be the center of our sphere
			Vector3 center = new Vector3(translateX+hw,translateY+hh,0);
			Vector3 i1, i2;

			//Project a line from the mouse click locations through the sphere
			//i1 is the initial mouse down position, i2 is the current position
			//We could optimize by computing i1 once in mouseDown, but oh well
			i1 =isectLineSphere(
					new Vector3(downX,downY,radius), 
					new Vector3(downX,downY,0), 
					center,	
					radius);

			i2 =isectLineSphere(
					new Vector3(x,y,radius), 
					new Vector3(x,y,0), 
					center,
					radius);
			
			//If either are null (no collision) just return
			if( i1 != null && i2 != null )
			{
				//Determine the vectors from the center of the sphere
				// to the point on the surface
				//We can optimize by removing the normalize() with div(radius)
				Vector3 v1, v2;
				v1 = i1.sub(center).normalize();
				v2 = i2.sub(center).normalize();

				//Determine the axis of rotation and the angle
				Vector3 a = v1.cross(v2);
				double theta = Math.asin(a.norm());

				//We have problems when the angle is > 90
				//The following code computes the angle correctly, but will dont rotate right
				//So just stop rotating when we move too far away
				if(v1.dot(v2) < 0)
				{
					theta = Math.PI - theta; //gets right angle, but still problems
					//theta = theta + Math.PI/2; //this is wrong
					setDamage();
					return true;
				}
			
				//Make some practical adjustments to account for the flipped y axis
				rotationAxis = new Vector3(a.x(), -a.y(), a.z()).normalize();
				
				//TODO: Find out if we should comment this next line
				//rotationAxis = (new Matrix4x4()).rotateZ(theta).mul(rotationAxis);
				
				//Convert to degrees
				rotationTheta = theta*180/Math.PI;

				//System.out.println("Dot: " + v1.dot(v2) + " angle: " + rotationTheta);
				
				//Update the rotation matrix	
				//rotationMatrix = (oldRotationMatrix).rotate(rotationAxis, rotationTheta);
				rotationMatrix = (new Matrix4x4()).rotate(rotationAxis, rotationTheta).mul(oldRotationMatrix);
			}
		}
		else if(leftButton && shiftDown)
		{
			//We have the vector from the center to the mouse-down position
			//We can compute the vector to the 
			Vector3 center = new Vector3(translateX+hw,translateY+hh,0);
			Vector3 handle = (new Vector3(x, y, 0)).sub(center);
			Vector3 start = (new Vector3(downX,downY,0)).sub(center);

			//Get the angle from the dot product, dividing by norms and takign arccos
			//If we wanted to be clever, we could keep the cos as it is, find sin
			// using sin = sqrt(1-cos^2) and plug sin,cos into the rotatematrix directly
			//This is a 2d operation
			double dot = handle.dot(start);
			double angle = Math.acos(dot/handle.norm()/start.norm())*180/Math.PI;	
			
			//If we went left (i think its left), take the negative angle
			//We will know if the crossproduct is pointing back at us
			if( handle.cross(start).z() > 0 )
				angle = -angle;

			//Update the old rotation matrix
			//rotationMatrix = (oldRotationMatrix).rotateZ(angle);
			rotationMatrix = (new Matrix4x4()).rotateZ(angle).mul(oldRotationMatrix);

		}
		else if(centerButton)
		{
			//Translate the object based on mouse pos.
			translateX = oldTranslateX + (x - downX);
			translateY = oldTranslateY + (y - downY);
		}
		else if(rightButton)
		{
			//Rescale the object based on the mouse position
			scaleX = oldScaleX + (x - downX)/50.0;
			scaleY = oldScaleY + -(y - downY)/50.0;
		}	

		setDamage();
		return true;

	}

	//Just reset the mouse positions to their old values
	public boolean mouseUp(Event e, int x, int y) 
	{		 
		boolean leftButton, centerButton, rightButton;
		centerButton = ((e.modifiers & e.ALT_MASK) > 0);
		rightButton = ((e.modifiers & e.META_MASK) > 0);
		leftButton = !centerButton && !rightButton;
	   
		if( downX == -1 )
			return true;

		selectedK = -1;
		selectedI = -1;
		selectedJ = -1;

		downX = -1;
		downY = -1;
		setDamage();
		return true;
	}


	boolean cullFace=true;
	boolean drawAxis=true;
	boolean wire=false;
	boolean doLighting=true;
	int selectedMode = 0;
	int theAnimation = 1;
	boolean skeletonOnly = false;
	boolean drawControl = true;

	static final int ANIMATION_COUNT = 7;

	public boolean keyUp(Event evt, int key)
	{
		switch(key)
		{
		case Event.TAB:
			modified = false;
			selectedK = -1;
			selectedJ = -1;
			selectedI = -1;
			break;
		}

		return true;
	}

	//Toggle a couple of settings, based on input
	public boolean keyDown(Event evt, int key)
	{
		switch(key)
		{
		case 'b':
			cullFace = !cullFace;
			break;
		case 'x':
			drawAxis = !drawAxis;
			break;
		case 'w':
			wire = !wire;
			wireframe(wire);
			break;
		case 's':
			skeletonOnly = !skeletonOnly;
			break;
		case 'l':
			doLighting = !doLighting;
			break;
		case ' ':
			theAnimation = (theAnimation+1)	% ANIMATION_COUNT;
			break;
		case Event.ESCAPE:
			translateX = translateY = oldTranslateX = oldTranslateY = theta = oldTheta = rotationTheta = 0;	
			scaleX = scaleY = oldScaleX = oldScaleY = 1;
			rotationMatrix = oldRotationMatrix = new Matrix4x4();
			break;
		case '-':
			SEGMENTS--;
			if( SEGMENTS < 1 )
				SEGMENTS = 1;
			makeAllPatches();
			break;
		case '+':
		case '=':
			SEGMENTS++;
			makeAllPatches();
			break;
		case 'c':
			drawControl = !drawControl;
			break;
		case Event.TAB:
			modified = true;
			break;

		}

		setDamage();
		return true;
	}
	
	//Settings for transformations
	int downX=-1, downY=-1;
	double translateX=0, translateY=0;
	double oldTranslateX=0, oldTranslateY=0;

	//double scaleX=1, scaleY=1;
	//double scaleX=0.32, scaleY=0.32;
	double scaleX=0.2, scaleY=0.2;
	double oldScaleX=1, oldScaleY=1;

	double theta=0, oldTheta=0;

	Vector3 rotationAxis=new Vector3(1, 0, 0);
	double rotationTheta=0;

	//Matrix4x4 rotationMatrix=new Matrix4x4();
	Matrix4x4 rotationMatrix=new Matrix4x4(0.208, .997, -.009, 0,
											.0026, -.0101, -.999, 0,
											-.978, .208,  -.0046, 0,
											0, 0, 0, 1);
	Matrix4x4 oldRotationMatrix=new Matrix4x4();

	public static void main(String[] args)
	{
		Frame frame = new Frame("Kinematics");
		Kinematics pnl = new Kinematics();	
		pnl.setSize(900, 900);
		frame.setSize(900, 900);
		frame.add(pnl);
		frame.show();
		
		frame.addWindowListener(
			new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				e.getWindow().dispose();
				System.exit(0);
			}
		}
			);

		pnl.init();
		pnl.start();
	}
}

