import java.applet.*;
import java.awt.*;
import java.io.*;
import java.net.*;
import java.lang.*;
import Vertex3D;
import Point3D;
import Matrix3D;
import Light;
import Surface;
import ZRaster;

import symantec.itools.awt.RadioButtonGroupPanel;
import symantec.itools.awt.util.spinner.NumericSpinner;
import symantec.itools.awt.ImageListBox;
import symantec.itools.awt.LabelButton;

public class Pipeline extends Applet {
    final static int CHUNKSIZE = 100;
    ZRaster raster;
    Image screen;
    Vertex3D vertList[];
    Vertex3D worldList[];
    Vertex3D canonicalList[];
    int vertices;
    Triangle triList[];
    int triangles;
    Matrix3D view;
    Matrix3D model;
    Matrix3D project;
    boolean cull = true;
    ewaCanvas canvas1;

    Light lightList[];
    int lights;
    Surface surfaceList[];
    int surfaces;

    boolean useColor;
    boolean usePerspective;
    String filename;

    Point3D eye, lookat, up;
    Point3D initEye, initLookat;
    float fov;

    public void init( )
    {

		Debug.startError();
    	test();
    	useColor = true;
    	usePerspective = true;

    		//{{INIT_CONTROLS
		setLayout(null);
		setSize(454,495);
		radioButtonGroupPanelColor = new symantec.itools.awt.RadioButtonGroupPanel();
		try {
			radioButtonGroupPanelColor.setLabel("Color");
		}
		catch(java.beans.PropertyVetoException e) { }
		radioButtonGroupPanelColor.setLayout(null);
		radioButtonGroupPanelColor.setBounds(348,84,72,84);
		add(radioButtonGroupPanelColor);
		Group1 = new CheckboxGroup();
		radioColor = new java.awt.Checkbox("Color", Group1, false);
		radioColor.setBounds(8,10,48,24);
		radioButtonGroupPanelColor.add(radioColor);
		radioBW = new java.awt.Checkbox("B & W", Group1, false);
		radioBW.setBounds(8,34,48,22);
		radioButtonGroupPanelColor.add(radioBW);
		numericSpinnerRotationX = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerRotationX.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerRotationX.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerRotationX.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerRotationX.setBounds(48,36,60,24);
		add(numericSpinnerRotationX);
		numericSpinnerRotationY = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerRotationY.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerRotationY.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerRotationY.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerRotationY.setBounds(48,60,60,24);
		add(numericSpinnerRotationY);
		numericSpinnerRotationZ = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerRotationZ.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerRotationZ.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerRotationZ.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerRotationZ.setBounds(48,84,60,24);
		add(numericSpinnerRotationZ);
		label1 = new java.awt.Label("X");
		label1.setBounds(36,36,12,25);
		add(label1);
		label2 = new java.awt.Label("Y");
		label2.setBounds(36,60,12,25);
		add(label2);
		label3 = new java.awt.Label("Z");
		label3.setBounds(36,84,12,25);
		add(label3);
		numericSpinnerRotationAngle = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerRotationAngle.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerRotationAngle.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerRotationAngle.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerRotationAngle.setBounds(48,120,60,32);
		add(numericSpinnerRotationAngle);
		label4 = new java.awt.Label("angle");
		label4.setBounds(12,120,36,24);
		add(label4);
		label5 = new java.awt.Label("Rotation");
		label5.setBounds(48,12,60,24);
		add(label5);
		numericSpinnerTranslationX = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerTranslationX.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerTranslationX.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerTranslationX.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerTranslationX.setBounds(252,36,60,24);
		add(numericSpinnerTranslationX);
		numericSpinnerTranslationY = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerTranslationY.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerTranslationY.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerTranslationY.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerTranslationY.setBounds(252,60,60,24);
		add(numericSpinnerTranslationY);
		numericSpinnerTranslationZ = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerTranslationZ.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerTranslationZ.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerTranslationZ.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerTranslationZ.setBounds(252,84,60,24);
		add(numericSpinnerTranslationZ);
		label6 = new java.awt.Label("X");
		label6.setBounds(240,36,12,25);
		add(label6);
		label7 = new java.awt.Label("Y");
		label7.setBounds(240,60,12,25);
		add(label7);
		label8 = new java.awt.Label("Z");
		label8.setBounds(240,84,12,25);
		add(label8);
		label9 = new java.awt.Label("Translation");
		label9.setBounds(252,12,72,24);
		add(label9);
		numericSpinnerScalingX = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerScalingX.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerScalingX.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerScalingX.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerScalingX.setBounds(156,36,60,24);
		add(numericSpinnerScalingX);
		numericSpinnerScalingY = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerScalingY.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerScalingY.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerScalingY.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerScalingY.setBounds(156,60,60,24);
		add(numericSpinnerScalingY);
		numericSpinnerScalingZ = new symantec.itools.awt.util.spinner.NumericSpinner();
		try {
			numericSpinnerScalingZ.setMin(-1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerScalingZ.setMax(1000);
		}
		catch(java.beans.PropertyVetoException e) { }
		try {
			numericSpinnerScalingZ.setEditable(true);
		}
		catch(java.beans.PropertyVetoException e) { }
		numericSpinnerScalingZ.setBounds(156,84,60,24);
		add(numericSpinnerScalingZ);
		label10 = new java.awt.Label("X");
		label10.setBounds(144,36,12,25);
		add(label10);
		label11 = new java.awt.Label("Y");
		label11.setBounds(144,60,12,25);
		add(label11);
		label12 = new java.awt.Label("Z");
		label12.setBounds(144,84,12,25);
		add(label12);
		label13 = new java.awt.Label("Scaling/Shearing");
		label13.setBounds(132,12,96,24);
		add(label13);
		label15 = new java.awt.Label("Image");
		label15.setBounds(348,12,36,24);
		add(label15);
		choiceImage = new java.awt.Choice();
		choiceImage.addItem("cubewa");
		choiceImage.addItem("cow");
		choiceImage.addItem("cube1");
		try {
			choiceImage.select(0);
		}
		catch (IllegalArgumentException e) { }
		add(choiceImage);
		choiceImage.setBounds(348,48,72,24);
		buttonRotate = new java.awt.Button();
		buttonRotate.setLabel("rotate");
		buttonRotate.setBounds(48,156,61,14);
		buttonRotate.setBackground(new Color(12632256));
		add(buttonRotate);
		buttonTranslate = new java.awt.Button();
		buttonTranslate.setLabel("translate");
		buttonTranslate.setBounds(252,108,61,14);
		buttonTranslate.setBackground(new Color(12632256));
		add(buttonTranslate);
		buttonScale = new java.awt.Button();
		buttonScale.setLabel("scale");
		buttonScale.setBounds(156,132,61,14);
		buttonScale.setBackground(new Color(12632256));
		add(buttonScale);
		panel1 = new java.awt.Panel();
		panel1.setLayout(null);
		panel1.setBounds(0,180,456,12);
		panel1.setBackground(new Color(0));
		add(panel1);
		panel2 = new java.awt.Panel();
		panel2.setLayout(null);
		panel2.setBounds(0,0,456,12);
		panel2.setBackground(new Color(0));
		add(panel2);
		panel3 = new java.awt.Panel();
		panel3.setLayout(null);
		panel3.setBounds(0,12,9,192);
		panel2.add(panel3);
		panel4 = new java.awt.Panel();
		panel4.setLayout(null);
		panel4.setBounds(444,12,12,174);
		panel4.setBackground(new Color(0));
		add(panel4);
		panel5 = new java.awt.Panel();
		panel5.setLayout(null);
		panel5.setBounds(0,12,12,174);
		panel5.setBackground(new Color(0));
		add(panel5);
		checkboxCompose = new java.awt.Checkbox("compose");
		checkboxCompose.setBounds(348,36,72,12);
		add(checkboxCompose);
		buttonShear = new java.awt.Button();
		buttonShear.setLabel("shear");
		buttonShear.setBounds(156,108,61,14);
		buttonShear.setBackground(new Color(12632256));
		add(buttonShear);
		buttonPespective = new java.awt.Button();
		buttonPespective.setLabel("use orthographic projection");
		buttonPespective.setBounds(156,156,167,12);
		buttonPespective.setBackground(new Color(12632256));
		add(buttonPespective);
		//}}

		//{{REGISTER_LISTENERS
		SymItem lSymItem = new SymItem();
		radioButtonGroupPanelColor.addItemListener(lSymItem);
		choiceImage.addItemListener(lSymItem);
		SymAction lSymAction = new SymAction();
		buttonRotate.addActionListener(lSymAction);
		buttonTranslate.addActionListener(lSymAction);
		buttonShear.addActionListener(lSymAction);
		buttonScale.addActionListener(lSymAction);
		buttonPespective.addActionListener(lSymAction);
		//}}
////////////////////////////////////////////////////////

		canvas1 = new ewaCanvas();
		canvas1.setBounds(30,200,350,350);
		add(canvas1);

      raster = new ZRaster(canvas1.getSize().width, canvas1.getSize().height);
      raster.fill(getBackground());

      // initialize viewing parameters to default
      // values in case they are not defined by the
      // input file
      eye = new Point3D(0, 0, -10);
      lookat = new Point3D(0, 0, 0);
      up = new Point3D(0, 1, 0);
      fov = 30;

      screen = raster.toImage();

      vertList = new Vertex3D[CHUNKSIZE];
      vertices = 0;
      triList = new Triangle[CHUNKSIZE];
      triangles = 0;
      lightList = new Light[CHUNKSIZE];
      lights = 0;
      surfaceList = new Surface[CHUNKSIZE];
      surfaces = 0;

        //String filename = getParameter("datafile");
      filename = "cube.obj";
      Debug.debug("Reading "+filename);
      handleFileInput();
	}



	 public void handleFileInput(){

	   InputStream is = null;
        try {
            is = new URL(getDocumentBase(), filename).openStream();
            ReadInput(is);
            is.close();
        } catch (IOException e) {
            Debug.debug("Error reading "+filename);
            showStatus("Error reading "+filename);
        }

        ////////////////////
        cull = false;

        canonicalList = new Vertex3D[vertList.length];
        worldList = new Vertex3D[vertList.length];
        for (int i = 0; i < vertices; i++) {
            canonicalList[i] = new Vertex3D();
            worldList[i] = new Vertex3D();
        }
        initEye = new Point3D(eye);
        initLookat = new Point3D(lookat);

        project = new Matrix3D(raster);
        view = new Matrix3D();
        float t = (float) Math.sin(Math.PI*(fov/2)/180);
        float s = (t*getSize().height)/getSize().width;
        Debug.debug("view0:\n" + view.toString());

        if (!usePerspective)
           view.orthographic(-t, t, -s, s, -1, -200);
        else
           view.perspective(-t, t, -s, s, -1, -200);
        Debug.debug("view1:\n" + view.toString());
        view.lookAt(eye.getX(), eye.getY(), eye.getZ(), lookat.getX(), lookat.getY(), lookat.getZ(),
        up.getX(), up.getY(), up.getZ());
        Debug.debug("view2:\n" + view.toString());

        model = new Matrix3D();
        long time = System.currentTimeMillis();
        //model.transform(vertList, worldList, 0, vertices);
        //view.transform(worldList, tranList, 0, vertices);
        DrawObject();
        time = System.currentTimeMillis() - time;
        showStatus("Time = "+time+" ms");
        screen = raster.toImage();
        Debug.debug("view:\n" + view.toString());
        Debug.debug("model:\n" + model.toString());
        //Debug.stopError();
        canvas1.scene = screen;
    }

    private void test(){

  		Matrix3D testT = new Matrix3D();
  		Matrix3D testS = new Matrix3D();
  		Matrix3D testRx = new Matrix3D();
  		Matrix3D testRx1 = new Matrix3D();
  		Matrix3D testRx2 = new Matrix3D();
  		Matrix3D testRy = new Matrix3D();
  		Matrix3D testRz = new Matrix3D();
  		Matrix3D testA = new Matrix3D();
  		Matrix3D testB = new Matrix3D();
  		Matrix3D testC = new Matrix3D();
  		testT.translate(1, 0, 0);
  		testS.scale(2, 2, 2);
  		testRx1.rotate(1, 0, 0, 0);
  		testRx2.rotate(1, 0, 0, (float)Math.PI/2.0f);
  		testRx.rotate(1, 0, 0, (float)Math.PI);
  		testRy.rotate(0, 1, 0, (float)Math.PI);
  		testRz.rotate(0, 0, 1, (float)Math.PI);
  		Debug.debug("Cos(PI) : "+ Math.cos(Math.PI));
  		Debug.debug("Sin(PI) : "+ Math.sin(Math.PI));
  		Debug.debug("Cos(PI)f : "+ (float)Math.cos(Math.PI));
  		Debug.debug("Sin(PI)f : "+ (float)Math.sin(Math.PI));

  		Debug.debug("Translation: \n"+testT.toString());
  		Debug.debug("Scaling: \n"+testS.toString());
  		Debug.debug("Rotation x: \n"+testRx.toString());
  		Debug.debug("Rotation x-0: \n"+testRx1.toString());
  		Debug.debug("Rotation x-90: \n"+testRx2.toString());
  		Debug.debug("Rotation y: \n"+testRy.toString());
  		Debug.debug("Rotation z: \n"+testRz.toString());
  	}


    private double getNumber(StreamTokenizer st) throws IOException
    {
        if (st.nextToken() != StreamTokenizer.TT_NUMBER) {
            System.err.println("ERROR: line "+st.lineno()+": expected number");
            throw new IOException(st.toString());
        }
        return st.nval;
    }

    private void growList()
    {
        Triangle newList[] = new Triangle[triList.length+CHUNKSIZE];
        System.arraycopy(triList, 0, newList, 0, triList.length);
        triList = newList;
    }

    public void ReadInput(InputStream is) throws IOException
    {
	    StreamTokenizer st = new StreamTokenizer(is);
    	st.commentChar('#');
        scan: while (true) {
	        switch (st.nextToken()) {
	          default:
		        break scan;
	          case StreamTokenizer.TT_WORD:
		        if (st.sval.equals("v")) {
		            float x = (float) getNumber(st);
		            float y = (float) getNumber(st);
		            float z = (float) getNumber(st);
		            if (vertices == vertList.length) {
		                Vertex3D newList[] = new Vertex3D[vertList.length+CHUNKSIZE];
                        System.arraycopy(vertList, 0, newList, 0, vertList.length);
		                vertList = newList;
		            }
		            vertList[vertices++] = new Vertex3D(x, y, z);
		        } else
		        if (st.sval.equals("f")) {
		            int faceTris = 0;
                  int v0 = (int) getNumber(st);
		            int v1 = (int) getNumber(st);
		            while (st.nextToken() == StreamTokenizer.TT_NUMBER) {
		                st.pushBack();
		                int v2 = (int) getNumber(st);
		                if (v2 == v0) continue;
		                if (triangles == triList.length) growList();
		                triList[triangles] = new Triangle(v0, v1, v2);
                        if (surfaces == 0) {
                            surfaceList[surfaces] = new Surface(0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 5.0f);
		                    surfaces += 1;
                        }
                        triList[triangles].setSurface(surfaceList[surfaces-1]);
		                v1 = v2;
		                faceTris += 1;
                        triangles += 1;
		            }
		            st.pushBack();
			    } else
			    if (st.sval.equals("eye")) {
                  eye.setX((float) getNumber(st));
                  eye.setY((float) getNumber(st));
		            eye.setZ((float) getNumber(st));
			    } else
			    if (st.sval.equals("look")) {
                  lookat.setX((float) getNumber(st));
                  lookat.setY((float) getNumber(st));
		            lookat.setZ((float) getNumber(st));
			    } else
			    if (st.sval.equals("up")) {
                  up.setX((float) getNumber(st));
                  up.setY((float) getNumber(st));
		            up.setZ((float) getNumber(st));
			    } else
			    if (st.sval.equals("fov")) {
                    fov = (float) getNumber(st);
			    } else
			    if (st.sval.equals("la")) {             // ambient light source
                  float r = (float) getNumber(st);
                  float g = (float) getNumber(st);
		            float b = (float) getNumber(st);
		            lightList[lights] = new Light(Light.AMBIENT, 0, 0, 0, r, g, b);
		            lights += 1;
			    } else
			    if (st.sval.equals("ld")) {             // directional light source
                  float r = (float) getNumber(st);
                  float g = (float) getNumber(st);
		            float b = (float) getNumber(st);
		            float x = (float) getNumber(st);
		            float y = (float) getNumber(st);
		            float z = (float) getNumber(st);
		            lightList[lights] = new Light(Light.DIRECTIONAL, x, y, z, r, g, b);
		            lights += 1;
			    } else
			    if (st.sval.equals("lp")) {             // point light source
                  float r = (float) getNumber(st);
                  float g = (float) getNumber(st);
		            float b = (float) getNumber(st);
		            float x = (float) getNumber(st);
		            float y = (float) getNumber(st);
		            float z = (float) getNumber(st);
		            lightList[lights] = new Light(Light.POINT, x, y, z, r, g, b);
		            lights += 1;
			    } else
			    if (st.sval.equals("surf")) {
                    float r = (float) getNumber(st);
                    float g = (float) getNumber(st);
		            float b = (float) getNumber(st);
		            float ka = (float) getNumber(st);
		            float kd = (float) getNumber(st);
		            float ks = (float) getNumber(st);
		            float ns = (float) getNumber(st);
		            surfaceList[surfaces] = new Surface(r, g, b, ka, kd, ks, ns);
		            surfaces += 1;
			    } else {
                    System.err.println("ERROR: line "+st.lineno()+": unexpected token :"+st.sval);
                    break scan;
			    }
			    break;
	        }
	        if (triangles % 100 == 0) showStatus("triangles = "+triangles);
	    }
        is.close();
	    if (st.ttype != StreamTokenizer.TT_EOF) {
	        throw new IOException(st.toString());
	    }
	}

    public void paint(Graphics g)
    {
        //g.drawImage(screen, 0, 0, this);
    }

    public void update(Graphics g)
    {
        paint(g);
    }

    float v0x, v0y, v0z;
    int oldx, oldy;

     public boolean mouseDown(Event e, int x, int y)
    {
        if (e.metaDown()) {
            System.out.println("Reset view matrix");
            eye.copy(initEye);
            lookat.copy(initLookat);
            view.loadIdentity();
            float t = (float) Math.sin(Math.PI*(fov/2)/180);
            float s = (t*size().height)/size().width;
            view.perspective(-t, t, -s, s, -1, -200);
            view.lookAt(eye.getX(), eye.getY(), eye.getZ(), lookat.getX(), lookat.getY(), lookat.getZ(),
            up.getX(), up.getY(), up.getZ());
            DrawObject();
        }
        oldx = x;
        oldy = y;
	    return true;
    }

    public boolean mouseDrag(Event e, int x, int y)
    {
        if (e.metaDown()) {
            System.out.println("Resetting view matrix");
        } else {
            float ax = lookat.getX() - eye.getX();
            float ay = lookat.getY() - eye.getY();
            float az = lookat.getZ() - eye.getZ();
            float t = (float) (getSize().width / (2*Math.tan(((fov * Math.PI)/180)/2)));
            Matrix3D r = new Matrix3D( );
            t = (float) (Math.atan((double)(x - oldx)/t));
            r.rotate(up.getX(), up.getY(), up.getZ(), t);
            eye.setX(eye.getX() + ax*(oldy - y)/size().height);
            eye.setY(eye.getY() + ay*(oldy - y)/size().height);
            eye.setZ(eye.getZ() + az*(oldy - y)/size().height);
            lookat.setX(r.get(0,0)*ax + r.get(1,0)*ay + r.get(2,0)*az);
            lookat.setY(r.get(0,1)*ax + r.get(1,1)*ay + r.get(2,1)*az);
            lookat.setZ(r.get(0,2)*ax + r.get(1,2)*ay + r.get(2,2)*az);
            /////////
            /////////
            /////////
            lookat.setX(lookat.getX() + eye.getX());
            lookat.setY(lookat.getY() + eye.getY());
            lookat.setZ(lookat.getZ() + eye.getZ());
            t = (float) Math.sin(Math.PI*(fov/2)/180);
            float s = (t*size().height)/size().width;
            view.loadIdentity();
            view.perspective(-t, t, -s, s, -1, -200);
            view.lookAt(eye.getX(), eye.getY(), eye.getZ(), lookat.getX(), lookat.getY(), lookat.getZ(),
            up.getX(), up.getY(), up.getZ());
            oldx = x;
            oldy = y;
            DrawObject();
        }
        return true;
    }

    /*
    void DrawObject()
    {
        int ix, iy;
        int w, h;
        w = raster.getWidth();
        h = raster.getHeight();
        raster.fill(getBackground());
        //if (!useColor)
        //for (int i = 0; i < vertices; i++) {
        //    ix = (int) tranList[i].getX();
        //    iy = (int) tranList[i].getY();
        //    if (ix >= 0 && iy >= 0 && ix < w && iy < h)
        //        raster.setPixel(0xffa000a0, ix, iy);
        //}
        //if (useColor) {
        	double zmin = tranList[0].getZ();
        	double zmax = tranList[0].getZ();
        	double iz, z, scale, zscale, fullscale;
        	int b, g, r, c;
        	for (int i = 1; i < vertices; i++) {
        		z = tranList[i].getZ();
        		if (z > zmax)	zmax = z;
        		if (z < zmin)	zmin = z;
        	}
        	Debug.debug("Z min: "+zmin+"       z max: "+zmax);
        	fullscale = zmax - zmin;
        	for (int i = 0; i < vertices; i++) {
            	ix = (int) tranList[i].getX();
            	iy = (int) tranList[i].getY();
            	iz = tranList[i].getZ();
            	zscale = iz - zmin;
            	scale = zscale/fullscale;
            	//if (zscale <= fullscale/2) {
            	//	r = (int) (0xff*2*(1-scale));
            	//	b = 0x00;
            	//}
            	//else {
            	//	r = 0x00;
            	//	b = (int) (0xff*2*scale);
            	//}

            if (useColor){
            // red forground
            // green middleground
            // blue background
    			   g = (int) (0xff* 2 * (.5 - Math.abs(scale-.5)));
    			   b = (int) (0xff*scale);
    			   r = (int) (0xff*(1-scale));
    		   }
    		   else {
    		   // black foreground
    		   // gray middleground
    		   // white background
    			   double indicator = 0;
       			if (zscale <= fullscale/5)	indicator = fullscale/10;
       			else						indicator = 0;
    	   		g = (int) (0xff*(1-scale) + 0xff*indicator);
    		   	b = (int) (0xff*(1-scale) + 0xff*indicator);
    			   r = (int) (0xff*(1-scale) + 0xff*indicator);
    			}
    			c = (b&0xff) | ((g&0xff)<<8) | ((r&0xff)<<16) |  (0xFF<<24);
	            if (ix >= 0 && iy >= 0 && ix < w && iy < h)
    	            raster.setPixel(c, ix, iy);
    	    }
        //}
    }
    */


   void DrawObject()
    {
        long time = System.currentTimeMillis();
        showStatus("Drawing "+triangles+" triangles ...");

        /*
            ... cull and illuminate in world space ...
        */
        if (worldList != null) {
           model.transform(vertList, worldList, vertices);
           triList[0].setVertexList(worldList);
           for (int i = 0; i < triangles; i++) {
               triList[i].Illuminate(lightList, lights, eye, cull);
           }
        }

        /*
            .... clip in canonical eye coordinates ...
        */

        /////view.transform(worldList, canonicalList, vertices);
        view.transform(worldList, canonicalList, vertices);
        triList[0].setVertexList(canonicalList);
        raster.fill(Color.white);
        raster.resetz();
        for (int i = 0; i < triangles; i++) {
            // check if trivially rejected
            if (triList[i].isVisible()) {
                triList[i].ClipAndDraw(raster, project);
            }
        }
        time = System.currentTimeMillis() - time;
        showStatus("Time = "+time+" ms");
        screen = raster.toImage( );
        repaint();
    }



	//{{DECLARE_CONTROLS
	symantec.itools.awt.RadioButtonGroupPanel radioButtonGroupPanelColor;
	java.awt.Checkbox radioColor;
	CheckboxGroup Group1;
	java.awt.Checkbox radioBW;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerRotationX;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerRotationY;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerRotationZ;
	java.awt.Label label1;
	java.awt.Label label2;
	java.awt.Label label3;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerRotationAngle;
	java.awt.Label label4;
	java.awt.Label label5;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerTranslationX;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerTranslationY;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerTranslationZ;
	java.awt.Label label6;
	java.awt.Label label7;
	java.awt.Label label8;
	java.awt.Label label9;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerScalingX;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerScalingY;
	symantec.itools.awt.util.spinner.NumericSpinner numericSpinnerScalingZ;
	java.awt.Label label10;
	java.awt.Label label11;
	java.awt.Label label12;
	java.awt.Label label13;
	java.awt.Label label15;
	java.awt.Choice choiceImage;
	java.awt.Button buttonRotate;
	java.awt.Button buttonTranslate;
	java.awt.Button buttonScale;
	java.awt.Panel panel1;
	java.awt.Panel panel2;
	java.awt.Panel panel3;
	java.awt.Panel panel4;
	java.awt.Panel panel5;
	java.awt.Checkbox checkboxCompose;
	java.awt.Button buttonShear;
	java.awt.Button buttonPespective;
	//}}

	class SymMouse extends java.awt.event.MouseAdapter
	{
		public void mouseClicked(java.awt.event.MouseEvent event)
		{
			Object object = event.getSource();
			if (object == canvas1)
				canvas1_MouseClicked(event);
		}
	}

	void canvas1_MouseClicked(java.awt.event.MouseEvent event)
	{
		// to do: code goes here.

		//{{CONNECTION
		// Show a status message... Get Applet Information
		showStatus(getAppletInfo());
		//}}
	}

	class ewaCanvas extends Canvas
	{
		public Image scene;


		public ewaCanvas()
		{
		}

	    public void paint(Graphics g)
	    {
	    	if (scene != null)
	    	{
	        g.drawImage(scene, 0, 0, this);
	      }
	    }

	    public void update(Graphics g)
  		 {
  	      paint(g);
    	 }
   }


	class SymItem implements java.awt.event.ItemListener
	{
		public void itemStateChanged(java.awt.event.ItemEvent event)
		{
			Object object = event.getSource();
			if (object == radioButtonGroupPanelColor)
				radioButtonGroupPanelColor_itemStateChanged(event);
			else if (object == choiceImage)
				choiceImage_ItemStateChanged(event);
		}
	}

	void radioButtonGroupPanelColor_itemStateChanged(java.awt.event.ItemEvent event)
	{
		if(radioColor.getState()){
		   useColor = true;
		   DrawObject();
         screen = raster.toImage();
         canvas1.scene = screen;
         canvas1.repaint();
      }
		else{
		   useColor = false;
		   DrawObject();
         screen = raster.toImage();
         canvas1.scene = screen;
         canvas1.repaint();
      }
	}

	void choiceImage_ItemStateChanged(java.awt.event.ItemEvent event)
	{
		filename = choiceImage.getSelectedItem()+".obj";
		if (checkboxCompose.getState())
		   handleFileInput();
	  	else {
	  	  raster = new ZRaster(canvas1.getSize().width, canvas1.getSize().height);
        raster.fill(getBackground());
        screen = raster.toImage();

      // initialize viewing parameters to default
      // values in case they are not defined by the
      // input file
      eye = new Point3D(0, 0, -10);
      lookat = new Point3D(0, 0, 0);
      up = new Point3D(0, 1, 0);
      fov = 30;

      screen = raster.toImage();

      vertList = new Vertex3D[CHUNKSIZE];
      vertices = 0;
      triList = new Triangle[CHUNKSIZE];
      triangles = 0;
      lightList = new Light[CHUNKSIZE];
      lights = 0;
      surfaceList = new Surface[CHUNKSIZE];
      surfaces = 0;

        //String filename = getParameter("datafile");
      filename = "cube.obj";
      Debug.debug("Reading "+filename);
      handleFileInput();
        screen = raster.toImage();
        canvas1.scene = screen;
        canvas1.repaint();

      }


	}

	class SymAction implements java.awt.event.ActionListener
	{
		public void actionPerformed(java.awt.event.ActionEvent event)
		{
			Object object = event.getSource();
			if (object == buttonRotate)
				buttonRotate_ActionPerformed(event);
			else if (object == buttonTranslate)
				buttonTranslate_ActionPerformed(event);
			else if (object == buttonShear)
				buttonShear_ActionPerformed(event);
			else if (object == buttonScale)
				buttonScale_ActionPerformed(event);
			else if (object == buttonPespective)
				buttonPespective_ActionPerformed(event);
		}
	}

	void buttonRotate_ActionPerformed(java.awt.event.ActionEvent event)
	{
      float angle;
      float ax, ay, az, n;
      String angleS, axS, ayS, azS;

      angleS= (numericSpinnerRotationAngle.getCurrentText());
      axS   = (numericSpinnerRotationX.getCurrentText());
      ayS   = (numericSpinnerRotationY.getCurrentText());
      azS   = (numericSpinnerRotationZ.getCurrentText());
      angle = (float)Math.PI * ((Float.valueOf(angleS)).floatValue() / 180.0f);
      ax    = (Float.valueOf(axS)).floatValue();
      ay    = (Float.valueOf(ayS)).floatValue();
      az    = (Float.valueOf(azS)).floatValue();

      model.rotate(ax, ay, az, angle);
      model.transform(vertList, worldList, vertices);
      view.transform(worldList, canonicalList, vertices);
      DrawObject();
      screen = raster.toImage();
      canvas1.scene = screen;
      canvas1.repaint();
	}

	void buttonTranslate_ActionPerformed(java.awt.event.ActionEvent event)
	{
      float tx, ty, tz;
      String txS, tyS, tzS;

      txS   = (numericSpinnerTranslationX.getCurrentText());
      tyS   = (numericSpinnerTranslationY.getCurrentText());
      tzS   = (numericSpinnerTranslationZ.getCurrentText());
      tx    = (Float.valueOf(txS)).floatValue();
      ty    = (Float.valueOf(tyS)).floatValue();
      tz    = (Float.valueOf(tzS)).floatValue();

      model.translate(tx, ty, tz);
      model.transform(vertList, worldList, vertices);
      view.transform(worldList, canonicalList, vertices);
      DrawObject();
      screen = raster.toImage();
      canvas1.scene = screen;
      canvas1.repaint();
	}

	void buttonShear_ActionPerformed(java.awt.event.ActionEvent event)
	{
      float shx, shy, shz;
      String shxS, shyS, shzS;

      shxS   = (numericSpinnerScalingX.getCurrentText());
      shyS   = (numericSpinnerScalingY.getCurrentText());
      shzS   = (numericSpinnerScalingZ.getCurrentText());
      shx    = (Float.valueOf(shxS)).floatValue();
      shy    = (Float.valueOf(shyS)).floatValue();
      shz    = (Float.valueOf(shzS)).floatValue();

      if(shx < 0)
         shx = Math.abs(1/shx);

      if(shy < 0)
         shy = Math.abs(1/shy);

      if(shz < 0)
         shz = Math.abs(1/shz);

      model.shear(shx, shy, shz);
      model.transform(vertList, worldList, vertices);
      view.transform(worldList, canonicalList, vertices);
      DrawObject();
      screen = raster.toImage();
      canvas1.scene = screen;
      canvas1.repaint();
   }

	void buttonScale_ActionPerformed(java.awt.event.ActionEvent event)
	{
      float sx, sy, sz;
      String sxS, syS, szS;

      sxS   = (numericSpinnerScalingX.getCurrentText());
      syS   = (numericSpinnerScalingY.getCurrentText());
      szS   = (numericSpinnerScalingZ.getCurrentText());

      sx    = (Float.valueOf(sxS)).floatValue();
      sy    = (Float.valueOf(syS)).floatValue();
      sz    = (Float.valueOf(szS)).floatValue();

      if(sx < 0)
         sx = Math.abs(1/sx);

      if(sy < 0)
         sy = Math.abs(1/sy);

      if(sz < 0)
         sz = Math.abs(1/sx);

      model.scale(sx, sy, sz);
      model.transform(vertList, worldList, vertices);
      view.transform(worldList, canonicalList, vertices);
      DrawObject();
      screen = raster.toImage();
      canvas1.scene = screen;
      canvas1.repaint();
	}

	void buttonPespective_ActionPerformed(java.awt.event.ActionEvent event)
	{
		if(buttonPespective.getLabel().equalsIgnoreCase("use orthographic projection")){
		   usePerspective = false;
		   buttonPespective.setLabel("use perspective projection");
		   DrawObject();
         screen = raster.toImage();
         canvas1.scene = screen;
         canvas1.repaint();
      }
		else{
		   usePerspective = true;
		   buttonPespective.setLabel("use orthographic projection");
		   DrawObject();
         screen = raster.toImage();
         canvas1.scene = screen;
         canvas1.repaint();
      }
	}
}