//
import java.awt.*;

// A VERY SIMPLE 3D RENDERER BUILT IN JAVA 1.0 - KEN PERLIN

public class Shape
{
   public final int CUBE = 0;
   public final int CYLINDER = 1;
   public final int NTYPES = 2;

   private int type = CUBE;

// SHAPE DATA FOR A CUBE

   private static int[][] cubeFace = {
      {4,5,7,6}, {5,1,3,7}, {6,7,3,2}, {0,2,3,1}, {4,6,2,0}, {0,1,5,4}
   };

   private static double[][] cubeVertices = {
      {-1,-1,-1}, { 1,-1,-1}, {-1, 1,-1}, { 1, 1,-1},
      {-1,-1, 1}, { 1,-1, 1}, {-1, 1, 1}, { 1, 1, 1},
   };

// SHAPE DATA FOR A PYRAMID

   private static int[][] pyramidFace = {
   };

   private static double[][] pyramidVertices = {
   };

   public int[][] face;
   public double[][] vertices;

// CONSTRUCTOR

   public Shape() {
      Matrix.identity(matrix);
      useShape(cubeFace, cubeVertices);
   }

// ALTERNATE SHAPES

   public void cube() {
      useShape(cubeFace, cubeVertices);
   }

   int cylinderFace[][][] = new int[100][][];
   double cylinderVertices[][][] = new double[100][][];

   public void cylinder(int n) {
      cylinder(n, true);
   }
   public void cylinder(int n, boolean capped) {
      n = Math.max(3, Math.min(99, n));
      if (cylinderFace[n] == null) {
	 cylinderFace[n] = new int[n+(capped?2:0)][];
	 for (int i = 0 ; i < n ; i++) {
	    cylinderFace[n][i] = new int[4];
	    cylinderFace[n][i][0] = i;
	    cylinderFace[n][i][1] = (i+1)%n;
	    cylinderFace[n][i][2] = n + (i+1)%n;
	    cylinderFace[n][i][3] = n + i;
         }
	 if (capped) {
	    cylinderFace[n][n  ] = new int[n];
	    cylinderFace[n][n+1] = new int[n];
	    for (int i = 0 ; i < n ; i++) {
	       cylinderFace[n][n  ][i] = n-1-i;
	       cylinderFace[n][n+1][i] = n+i;
            }
         }
	 cylinderVertices[n] = new double[2*n][3];
	 for (int i = 0 ; i < n ; i++) {
	    double theta = 2 * i * Math.PI / n;
	    double cos = Math.cos(theta);
	    double sin = Math.sin(theta);
	    Vec.set(cylinderVertices[n][  i], cos, sin, -1);
	    Vec.set(cylinderVertices[n][n+i], cos, sin,  1);
	 }
      }
      useShape(cylinderFace[n], cylinderVertices[n]);
   }

// PUBLIC ACCESS FUNCTIONS

   public void setColor(double[] src) {
      Vec.copy(src, color);
   }

   public void getColor(double[] dst) {
      Vec.copy(color, dst);
   }

   public void setTranslation(double[] src) {
      Vec.copy(src, translation);
      mustRecalc = true;
   }

   public void setRotation(double[] src) {
      Vec.copy(src, rotation);
      mustRecalc = true;
   }

   public void setScale(double[] src) {
      Vec.copy(src, scale);
      mustRecalc = true;
   }

   public void getTranslation(double[] dst) {
      Vec.copy(translation, dst);
   }

   public void getRotation(double[] dst) {
      Vec.copy(rotation, dst);
   }

   public void getScale(double[] dst) {
      Vec.copy(scale, dst);
   }

   public void getMatrix(double[][] m) {
      if (mustRecalc) {
	 Matrix.identity(matrix);
	 Matrix.translate(matrix, translation[0],translation[1],translation[2]);
	 Matrix.rotateX(matrix, rotation[0]);
	 Matrix.rotateY(matrix, rotation[1]);
	 Matrix.rotateZ(matrix, rotation[2]);
	 Matrix.scale(matrix, scale[0],scale[1],scale[2]);
	 mustRecalc = false;
      }
      Matrix.copy(matrix, m);
   }

// INTERNALLY STORED COLOR

   private double[] color = { 1,1,1 };

// TRANSLATION X Y Z, ROTATION X Y Z, SCALE X Y Z

   private double[] translation = { 0,0,0 };
   private double[] rotation    = { 0,0,0 };
   private double[] scale       = { 1,1,1 };

// INTERNALLY CALCULATED MATRIX

   private double[][] matrix = new double[4][4];

// NEED TO RECALCULATE MATRIX?

   private boolean mustRecalc = true;

   private void useShape(int[][] f, double[][] v) {
      face = f;
      vertices = v;
   }
}