import java.awt.image.*;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Image;
import java.awt.Font;



class ZeroNowMatrix
{
  double m[][];

  public ZeroNowMatrix(int rx, int ry, int rz)
  {
    m = new double[3][3];

    double cx, sx, cy, sy, cz, sz;

    cx = Math.cos(rx*3.14159/2048);
    cy = Math.cos(ry*3.14159/2048);
    cz = Math.cos(rz*3.14159/2048);

    sx = Math.sin(rx*3.14159/2048);
    sy = Math.sin(ry*3.14159/2048);
    sz = Math.sin(rz*3.14159/2048);

    m[0][0] =          cy*cz; m[0][1] =          cy*sz; m[0][2] = -sy;
    m[1][0] = sx*sy*cz-cx*sz; m[1][1] = sx*sy*sz+cx*cz; m[1][2] = sx*cy;
    m[2][0] = cx*sy*cz+sx*sz; m[2][1] = cx*sy*sz-sx*cz; m[2][2] = cx*cy;
  }

  public void identity()
  {
    m[0][0] = 1; m[0][1] = 0; m[0][2] = 0;
    m[1][0] = 0; m[1][1] = 1; m[1][2] = 0;
    m[2][0] = 0; m[2][1] = 0; m[2][2] = 1;
  }
  
  public void rotate_x(int r)
  {
    double c = Math.cos(r*3.14159/2048.0);
    double s = Math.sin(r*3.14159/2048.0);
    double p, q;

    p = m[0][1]; q = m[0][2]; m[0][1] = p*c+q*s; m[0][2] = q*c-p*s;
    p = m[1][1]; q = m[1][2]; m[1][1] = p*c+q*s; m[1][2] = q*c-p*s;
    p = m[2][1]; q = m[2][2]; m[2][1] = p*c+q*s; m[2][2] = q*c-p*s;
  }
  
  public void transformPoints(double[] x, double[] y, double[] z, double[] tx, double[] ty, double[] tz, int num)
  {
    for(int i=0; i<num; i++)
    {
      tx[i] = m[0][0]*x[i] + m[0][1]*y[i] + m[0][2]*z[i];
      ty[i] = m[1][0]*x[i] + m[1][1]*y[i] + m[1][2]*z[i];
      tz[i] = m[2][0]*x[i] + m[2][1]*y[i] + m[2][2]*z[i];
    }
  }

  public void transformNormals(double[] x, double[] y, double[] z, double[] tx, double[] ty, double[] tz, int num)
  {
    for(int i=0; i<num; i++)
    {
      tx[i] = m[0][0]*x[i] + m[0][1]*y[i] + m[0][2]*z[i];
      ty[i] = m[1][0]*x[i] + m[1][1]*y[i] + m[1][2]*z[i];
      tz[i] = m[2][0]*x[i] + m[2][1]*y[i] + m[2][2]*z[i];
    }
  }

}



class ZeroNowObject
{
  int numpoints, maxpoints;
  int numfaces, maxfaces;

  double[] x, y, z, nx, ny, nz, tx, ty, tz, tnx, tny, tnz, sx, sy;
  int[] p1, p2, p3;

  int image_w, image_h;
  byte[] buf;

  double tri_x1, tri_y1, tri_c1;
  double tri_x2, tri_y2, tri_c2;
  double tri_x3, tri_y3, tri_c3;

  public ZeroNowObject(int points, int faces)
  {
    maxpoints = points;
    maxfaces = maxfaces;
    numpoints = 0;
    numfaces = 0;

    x = new double[points];
    y = new double[points];
    z = new double[points];
    nx = new double[points];
    ny = new double[points];
    nz = new double[points];
    tx = new double[points];
    ty = new double[points];
    tz = new double[points];
    tnx = new double[points];
    tny = new double[points];
    tnz = new double[points];
    sx = new double[points];
    sy = new double[points];
    p1 = new int[faces];
    p2 = new int[faces];
    p3 = new int[faces];
  }

  public void addPoint(double x, double y, double z, double nx, double ny, double nz)
  {
    this.x[numpoints] = x;
    this.y[numpoints] = y;
    this.z[numpoints] = z;
    this.nx[numpoints] = nx;
    this.ny[numpoints] = ny;
    this.nz[numpoints] = nz;
    numpoints ++;
  }

  public void addFace(int p1, int p2, int p3)
  {
    this.p1[numfaces] = p1;
    this.p2[numfaces] = p2;
    this.p3[numfaces] = p3;
    numfaces++;
  }

  public void render(byte[] buf, int w, int h, int rx, int ry, int rz)
  {
    int i;
    
    image_w = w; image_h = h; this.buf = buf;

    //- Clear buffer

    for(i=0; i<image_w*image_h; i++)
    {
      buf[i] = 0;
    }

    //- Transform points

    ZeroNowMatrix m = new ZeroNowMatrix(rx, ry, rz);

    m.transformPoints(x, y, z, tx, ty, tz, numpoints);
    m.transformPoints(nx, ny, nz, tnx, tny, tnz, numpoints);

    //- Make screen points

    for(i=0; i<numpoints; i++)
    {
      sx[i] = 0.5*w + 700.0*tx[i]/(tz[i]+10);
      sy[i] = 0.5*h + 700.0*ty[i]/(tz[i]+10);
      tri_col();
    }

//    tri_x1 = 100; tri_y1 = 10; tri_c1 = 255;
//    tri_x2 = 250; tri_y2 = 170; tri_c2 = 128;
//    tri_x3 = 170; tri_y3 = 240; tri_c3 = 255;

    //- Render faces

    for(i=0; i<numfaces; i++)
    {
      tri_x1 = sx[p1[i]]; tri_y1 = sy[p1[i]]; tri_c1 =  64;
      tri_x2 = sx[p2[i]]; tri_y2 = sy[p2[i]]; tri_c2 = 128;
      tri_x3 = sx[p3[i]]; tri_y3 = sy[p3[i]]; tri_c3 = 255;
      tri_col();
    }

  }


  public void tri_col()
  {
    double t;
    int x, y, y1, y2, x1, x2, o, ti;
    double c1, c2;
    double dc;
    
    //  Don't draw if backfacing
    
    //  if((tri_x2-tri_x1)*(tri_y3-tri_y1) > (tri_x3-tri_x1)*(tri_y2-tri_y1)) return;
    
    // Sort points
    
    if(tri_y1 > tri_y2)
    {
      t=tri_y2; tri_y2=tri_y1; tri_y1=t;
      t=tri_x2; tri_x2=tri_x1; tri_x1=t;
      t=tri_c2; tri_c2=tri_c1; tri_c1=t;
    }
    if(tri_y2 > tri_y3)
    {
      t=tri_y3; tri_y3=tri_y2; tri_y2=t;
      t=tri_x3; tri_x3=tri_x2; tri_x2=t;
      t=tri_c3; tri_c3=tri_c2; tri_c2=t;
    }
    if(tri_y1 > tri_y2)
    {
      t=tri_y2; tri_y2=tri_y1; tri_y1=t;
      t=tri_x2; tri_x2=tri_x1; tri_x1=t;
      t=tri_c2; tri_c2=tri_c1; tri_c1=t;
    }
    
    // Clip top point to screen and pixel
    
    y1 = (int)tri_y1; if(y1>=image_h-1) return; if(y1<1) y1 = 1;
    y2 = (int)tri_y3; if(y2<1) return; if(y2>image_h-1) y2 = image_h-1;

    c1=0; c2=0;

    for(y=y1-1; y<y2+1; y++)
    {
      if(y >= tri_y1 && y < tri_y3)
      {
        if(y >= tri_y1 && y < tri_y2)
        {
          x1 = (int)(tri_x1 + (tri_x3-tri_x1) * (y-tri_y1) / (tri_y3-tri_y1));
          x2 = (int)(tri_x1 + (tri_x2-tri_x1) * (y-tri_y1) / (tri_y2-tri_y1));
          c1 = (int)(tri_c1 + (tri_c3-tri_c1) * (y-tri_y1) / (tri_y3-tri_y1));
          c2 = (int)(tri_c1 + (tri_c2-tri_c1) * (y-tri_y1) / (tri_y2-tri_y1));
        }
        else
        {
          x1 = (int)(tri_x1 + (tri_x3-tri_x1) * (y-tri_y1) / (tri_y3-tri_y1));
          x2 = (int)(tri_x2 + (tri_x3-tri_x2) * (y-tri_y2) / (tri_y3-tri_y2));
          c1 = (int)(tri_c1 + (tri_c3-tri_c1) * (y-tri_y1) / (tri_y3-tri_y1));
          c2 = (int)(tri_c2 + (tri_c3-tri_c2) * (y-tri_y2) / (tri_y3-tri_y2));
        }
        if(x1 > x2)
        {
          ti = x2; x2 = x1; x1 = ti;
          t = c2; c2 = c1; c1 = t;
        }
        if(x1 < 0) x1 = 0;
        if(x2 > image_w) x2 = image_w;
    
        if(x2 > x1)
        {
          dc = (c2-c1)/(x2-x1);
    
          o = (y*image_w+x1);
    
          for(x=x1; x<x2; x++)
          {
            buf[o++] ^= (byte)c1;
            c1 += dc;
          }
        }
          
      }
    }
    
  }


}




public class ZeroNow extends java.applet.Applet implements Runnable
{
  Thread runThread;
  static int image_w = 320, image_h = 320;

  byte r[], g[], b[];
  byte buf[];
  byte back[];
  IndexColorModel pal;

  MemoryImageSource src;
  Image im;

  public void init()
  {
    int i;

    //- Create render buffer & palette

    int size = image_w*image_h;
    buf = new byte[size];
    back = new byte[size];
    r = new byte[256];
    g = new byte[256];
    b = new byte[256];

    for(i=0; i<size; i++)
    {
      buf[i] = 0;
    }

    for(i=0; i<256; i++)
    {
      r[i] = (byte)i;
      g[i] = (byte)i;
      b[i] = (byte)i;
    }

    pal = new IndexColorModel(8, 256, r, g, b);
    src = new MemoryImageSource(image_w, image_h, pal, buf, 0, image_w);
    src.setAnimated(true);
    im = createImage(src);
  }

  public void start()
  {
    if(runThread == null)
    {
      runThread = new Thread(this);
      runThread.start();
    }
  }

  public void stop()
  {
    if(runThread != null)
    {
      runThread.stop();
      runThread = null;
    }
  }

  public void run()
  {

    //- Create object
    
    ZeroNowObject zno = new ZeroNowObject(256, 256);
    
    zno.addPoint( 1,  1,  1,  1,  1,  1);
    zno.addPoint(-1,  1,  1, -1,  1,  1);
    zno.addPoint( 1, -1,  1,  1, -1,  1);
    zno.addPoint( 1,  1, -1,  1,  1, -1);
    zno.addPoint( 0,  2,  1,  2,  1,  2);
    zno.addPoint(-2,  1,  2, -1,  2,  1);
    zno.addPoint( 0, -2,  0,  2, -1,  2);
    zno.addPoint( 2,  1, -2,  1,  2, -1);

    zno.addFace(0, 1, 2);
    zno.addFace(0, 3, 5);
    zno.addFace(0, 2, 3);
    zno.addFace(1, 2, 4);
    zno.addFace(0, 2, 5);
    zno.addFace(5, 1, 3);
    zno.addFace(0, 2, 3);
    zno.addFace(1, 4, 3);

    //- Animate

    int i, j, k;

    k = 0;

    while(true) {
      try { runThread.sleep(20); } catch (InterruptedException e) {}

      k = (k+1)&4095;
      zno.render(buf, image_w, image_h, k*11, k*4, k*7);

      src.newPixels(0, 0, image_w, image_h);
    }

  }

  public boolean mouseDown(Event evt,int x,int y) {
    return(true);
  }
  
  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g) {
    g.drawImage(im, 0, 0, this);
  }

}



