import Light;
import Point3D;
import Surface;
import Vertex3D;
import ZRaster;

public final class Triangle {

  private static Vertex3D vertex[];                //vertices for all triangles

  //To optimize the triangle procedure for speed, all intermediary variables
  //necessary to draw the triangle are made private static to prevent their
  //reinitialization each time.  Also, arrays are avoided in favor of multiple
  //independent variables.
  private static Vertex3D clip0=new Vertex3D();    //first clipping vertex
  private static Vertex3D clip1=new Vertex3D();    //second clipping vertex
  private static Vertex3D clip2=new Vertex3D();    //third clipping vertex
  private static Vertex3D clip3=new Vertex3D();    //fourth clipping vertex
  private static Vertex3D cl0, cl1;                //temp clipping vertices
  private static Vertex3D V0=new Vertex3D();       //first vertex
  private static Vertex3D V1=new Vertex3D();       //second vertex
  private static Vertex3D V2=new Vertex3D();       //third vertex
  private static Vertex3D top, breakpoint, bottom; //sorted vertices
  private static Vertex3D vert0, vert1;            //indexed vertices
  private static Vertex3D vert2, vert3;            //indexed vertices
  private static Vertex3D vt0, vt1, vt2, vt3;      //temp clipping vertices
  private static boolean left;                     //breakpoint side
  private static float common;                     //common part of A, B, and C
  private static float dillum, sillum, nl;         //intermediary illuminations
  private static float plane00, plane01, plane02;  //plane equations
  private static float plane10, plane11, plane12;  //plane equations
  private static float plane20, plane21, plane22;  //plane equations
  private static float r, g, b;                    //vertex illuminations
  private static float eyex, eyey, eyez;           //eye normal
  private static float lx, ly, lz;                 //light source normal
  private static float rx, ry, rz;                 //reflective normal
  private static float temp;                       //temporary variable
  private static int A_z, B_z, C_z;                //A*x+B*y+C=z component
  private static int A_r, B_r, C_r;                //A*x+B*y+C=red component
  private static int A_g, B_g, C_g;                //A*x+B*y+C=green component
  private static int A_b, B_b, C_b;                //A*x+B*y+C=blue component
  private static int bound;
  private static int breakpoint_stop, bottom_stop; //loop termination values
  private static int clipmask;                     //clipping mask
  private static int dstart, dstop;                //scanline endpoint deltas
  private static int pixel_z, scan_z;              //intermediary z values
  private static int pixel_r, pixel_g, pixel_b;    //colors at pixel
  private static int scanline_stop;                //loop termination value
  private static int scan_r, scan_g, scan_b;       //colors at scanline origin
  private static int start, stop;                  //scanline endpoint x values
  private static int top_r, breakpoint_r, bottom_r;//red vertex components
  private static int top_g, breakpoint_g, bottom_g;//green vertex components
  private static int top_b, breakpoint_b, bottom_b;//blue component of vertices
  private static int vertices;                     //polygon vertex count
  private static int xy, x, y, z;                  //drawn pixel coordinates

  //Only these three variables are non-static, and they represent the
  //definition of a Triangle.
  private Surface surface;                         //surface characteristics
  public  boolean visible;                         //visible or not
  private float nx, ny, nz;                        //triangle normal vector
  public float ntx, nty, ntz;                      //transformed normal vector
  private float v0r, v1r, v2r;                     //red values at vertices
  private float v0g, v1g, v2g;                     //green values at vertices
  private float v0b, v1b, v2b;                     //blue values at vertices
  private int v0, v1, v2;                          //vertices

  //triangle constructor
  public Triangle(int vert0, int vert1, int vert2, float x, float y, float z) {

    v0=vert0;
    v1=vert1;
    v2=vert2;
    nx=x;
    ny=y;
    nz=z;
    common=(float) 1/(float) Math.sqrt(nx*nx + ny*ny + nz*nz);
    nx*=common;
    ny*=common;
    nz*=common;
    ntx=nx;
    nty=ny;
    ntz=nz;
  }

  //update list of vertices
  public final void setVertexList(Vertex3D in[]) {

    vertex=in;
  }

  //update surface
  public final void setSurface(Surface in) {

    surface=in;
  }

  //procedure to reset surface normals
  public final void resetNormals() {

    ntx=nx;
    nty=ny;
    ntz=nz;
  }

  //procedure to apply illumination to triangles
  public final void IlluminateAndCull(Light[] lights, int n, Point3D eye,
				      boolean cull) {

    //retrieve vertices
    vert0=vertex[v0];
    vert1=vertex[v1];
    vert2=vertex[v2];

    //perform backface culling
    if (cull) {
      if (ntx*(eye.x-vert0.x)+nty*(eye.y-vert0.y)+ntz*(eye.z-vert0.z)<0)
	visible=true;
      else {
	visible=false;
	return;
      }
    }
    else
      visible=true;

    //compute illumination
    common=(float) -1/(float) Math.sqrt(eye.x*eye.x+eye.y*eye.y+eye.z*eye.z);
    eyex=eye.x*common;
    eyey=eye.y*common;
    eyez=eye.z*common;

    r=g=b=v0r=v0g=v0b=v1r=v1g=v1b=v2r=v2g=v2b=0;
    for (n--; n>=0; n--) {

      switch(lights[n].lightType) {

      case Light.AMBIENT:
	if (surface.ka>0) {
	  r+=lights[n].ir*surface.ka;
	  g+=lights[n].ig*surface.ka;
	  b+=lights[n].ib*surface.ka;
	}
	break;

      case Light.DIRECTIONAL:
	if (surface.kd>0||surface.ks>0) {

	  //vertex 0
	  nl=vert0.nx*lights[n].x+vert0.ny*lights[n].y+vert0.nz*lights[n].z;
	  if (nl>0&&surface.kd>0)
	    dillum=nl*surface.kd;
	  else
	    dillum=0;
	  if (surface.ks>0) {
	    common=2*nl;
	    rx=common*vert0.nx-lights[n].x;
	    ry=common*vert0.ny-lights[n].y;
	    rz=common*vert0.nz-lights[n].z;
	    common=(float) 1/(float) Math.sqrt(rx*rx+ry*ry+rz*rz);
	    rx*=common;
	    ry*=common;
	    rz*=common;
	    sillum=(float) Math.pow(rx*eyex+ry*eyey+rz*eyez, surface.ns)*
	      surface.ks;
	    if (sillum>0)
	      dillum+=sillum;
	  }
	  v0r+=lights[n].ir*dillum;
	  v0g+=lights[n].ig*dillum;
	  v0b+=lights[n].ib*dillum;

	  //vertex 1
	  nl=vert1.nx*lights[n].x+vert1.ny*lights[n].y+vert1.nz*lights[n].z;
	  if (nl>0&&surface.kd>0)
	    dillum=nl*surface.kd;
	  else
	    dillum=0;
	  if (surface.ks>0) {
	    common=2*nl;
	    rx=common*vert1.nx-lights[n].x;
	    ry=common*vert1.ny-lights[n].y;
	    rz=common*vert1.nz-lights[n].z;
	    common=(float) 1/(float) Math.sqrt(rx*rx+ry*ry+rz*rz);
	    rx*=common;
	    ry*=common;
	    rz*=common;
	    sillum=(float) Math.pow(rx*eyex+ry*eyey+rz*eyez, surface.ns)*
	      surface.ks;
	    if (sillum>0)
	      dillum+=sillum;
	  }
	  v1r+=lights[n].ir*dillum;
	  v1g+=lights[n].ig*dillum;
	  v1b+=lights[n].ib*dillum;

	  //vertex 2
	  nl=vert2.nx*lights[n].x+vert2.ny*lights[n].y+vert2.nz*lights[n].z;
	  if (nl>0&&surface.kd>0)
	    dillum=nl*surface.kd;
	  else
	    dillum=0;
	  if (surface.ks>0) {
	    common=2*nl;
	    rx=common*vert2.nx-lights[n].x;
	    ry=common*vert2.ny-lights[n].y;
	    rz=common*vert2.nz-lights[n].z;
	    common=(float) 1/(float) Math.sqrt(rx*rx+ry*ry+rz*rz);
	    rx*=common;
	    ry*=common;
	    rz*=common;
	    sillum=(float) Math.pow(rx*eyex+ry*eyey+rz*eyez, surface.ns)*
	      surface.ks;
	    if (sillum>0)
	      dillum+=sillum;
	  }
	  v2r+=lights[n].ir*dillum;
	  v2g+=lights[n].ig*dillum;
	  v2b+=lights[n].ib*dillum;
	}
	break;

      case Light.POINT:
	if (surface.kd>0||surface.ks>0) {

	  //vertex 0
	  lx=vert0.x-lights[n].x;
	  ly=vert0.y-lights[n].y;
	  lz=vert0.z-lights[n].z;
	  common=(float) 1/(float) Math.sqrt(lx*lx+ly*ly+lz*lz);
	  lx*=common;
	  ly*=common;
	  lz*=common;
	  nl=vert0.nx*lx+vert0.ny*ly+vert0.nz*lz;
	  if (nl>0&&surface.kd>0)
	    dillum=nl*surface.kd;
	  else
	    dillum=0;
	  if (surface.ks>0) {
	    common=2*nl;
	    rx=common*vert0.nx-lx;
	    ry=common*vert0.ny-ly;
	    rz=common*vert0.nz-lz;
	    common=(float) 1/(float) Math.sqrt(rx*rx+ry*ry+rz*rz);
	    rx*=common;
	    ry*=common;
	    rz*=common;
	    sillum=(float) Math.pow(rx*eyex+ry*eyey+rz*eyez, surface.ns)*
	      surface.ks;
	    if (sillum>0)
	      dillum+=sillum;
	  }
	  v0r+=lights[n].ir*dillum;
	  v0g+=lights[n].ig*dillum;
	  v0b+=lights[n].ib*dillum;

	  //vertex 1
	  lx=vert1.x-lights[n].x;
	  ly=vert1.y-lights[n].y;
	  lz=vert1.z-lights[n].z;
	  common=(float) 1/(float) Math.sqrt(lx*lx+ly*ly+lz*lz);
	  lx*=common;
	  ly*=common;
	  lz*=common;
	  nl=vert1.nx*lx+vert1.ny*ly+vert1.nz*lz;
	  if (nl>0&&surface.kd>0)
	    dillum=nl*surface.kd;
	  else
	    dillum=0;
	  if (surface.ks>0) {
	    common=2*nl;
	    rx=common*vert1.nx-lx;
	    ry=common*vert1.ny-ly;
	    rz=common*vert1.nz-lz;
	    common=(float) 1/(float) Math.sqrt(rx*rx+ry*ry+rz*rz);
	    rx*=common;
	    ry*=common;
	    rz*=common;
	    sillum=(float) Math.pow(rx*eyex+ry*eyey+rz*eyez, surface.ns)*
	      surface.ks;
	    if (sillum>0)
	      dillum+=sillum;
	  }
	  v1r+=lights[n].ir*dillum;
	  v1g+=lights[n].ig*dillum;
	  v1b+=lights[n].ib*dillum;

	  //vertex 2
	  lx=vert2.x-lights[n].x;
	  ly=vert2.y-lights[n].y;
	  lz=vert2.z-lights[n].z;
	  common=(float) 1/(float) Math.sqrt(lx*lx+ly*ly+lz*lz);
	  lx*=common;
	  ly*=common;
	  lz*=common;
	  nl=vert2.nx*lx+vert2.ny*ly+vert2.nz*lz;
	  if (nl>0&&surface.kd>0)
	    dillum=nl*surface.kd;
	  else
	    dillum=0;
	  if (surface.ks>0) {
	    common=2*nl;
	    rx=common*vert2.nx-lx;
	    ry=common*vert2.ny-ly;
	    rz=common*vert2.nz-lz;
	    common=(float) 1/(float) Math.sqrt(rx*rx+ry*ry+rz*rz);
	    rx*=common;
	    ry*=common;
	    rz*=common;
	    sillum=(float) Math.pow(rx*eyex+ry*eyey+rz*eyez, surface.ns)*
	      surface.ks;
	    if (sillum>0)
	      dillum+=sillum;
	  }
	  v2r+=lights[n].ir*dillum;
	  v2g+=lights[n].ig*dillum;
	  v2b+=lights[n].ib*dillum;
	}
      }
    }
    v0r=(v0r+r)*surface.r;
    if (v0r>1)
      v0r=1;
    v0g=(v0g+g)*surface.g;
    if (v0g>1)
      v0g=1;
    v0b=(v0b+b)*surface.b;
    if (v0b>1)
      v0b=1;
    v1r=(v1r+r)*surface.r;
    if (v1r>1)
      v1r=1;
    v1g=(v1g+g)*surface.g;
    if (v1g>1)
      v1g=1;
    v1b=(v1b+b)*surface.b;
    if (v1b>1)
      v1b=1;
    v2r=(v2r+r)*surface.r;
    if (v2r>1)
      v2r=1;
    v2g=(v2g+g)*surface.g;
    if (v2g>1)
      v2g=1;
    v2b=(v2b+b)*surface.b;
    if (v2b>1)
      v2b=1;
  }

  //procedure to clip and draw a triangle onto a raster
  public final void ClipAndDraw(ZRaster raster, Matrix3D project) {

    //retrieve vertices
    vert0=vertex[v0];
    vert0.r=v0r;
    vert0.g=v0g;
    vert0.b=v0b;
    vert1=vertex[v1];
    vert1.r=v1r;
    vert1.g=v1g;
    vert1.b=v1b;
    vert2=vertex[v2];
    vert2.r=v2r;
    vert2.g=v2g;
    vert2.b=v2b;

    //check against hither
    vertices=3;
    clipmask=0;
    if (vert0.z>-1)
      clipmask+=1;
    if (vert1.z>-1)
      clipmask+=2;
    if (vert2.z>-1)
      clipmask+=4;

    //trivially reject
    if (clipmask==7)
      return;

    //clip against hither
    if (clipmask!=0) {
      vt0=vert0;
      vt1=vert1;
      vt2=vert2;
      vt3=vert3;
      cl0=clip0;
      cl1=clip1;
      bound=-1;
      clip();
      vert0=vt0;
      vert1=vt1;
      vert2=vt2;
      vert3=vt3;
    }

    //check against yon
    clipmask=0;
    if (vert0.z<-200)
      clipmask+=1;
    if (vert1.z<-200)
      clipmask+=2;
    if (vert2.z<-200)
      clipmask+=4;

    if (vertices==3) {

      //trivially reject
      if (clipmask==7)
	return;

      //clip against yon
      if (clipmask!=0) {
	vt0=vert0;
	vt1=vert1;
	vt2=vert2;
	vt3=vert3;
	cl0=clip2;
	cl1=clip3;
	bound=-200;
	clip();
	vert0=vt0;
	vert1=vt1;
	vert2=vt2;
	vert3=vt3;
      }
    }
    else {
      //For now, ignore triangles that clip in both hither and yon
    }

    //draw triangles
    project.transform(vert0, V0);
    project.transform(vert1, V1);
    project.transform(vert2, V2);
    Draw(raster);
    if (vertices==4) {
      vert1.r=vert2.r;
      vert1.g=vert2.g;
      vert1.b=vert2.b;
      V1.x=V2.x;
      V1.y=V2.y;
      V1.z=V2.z;
      vert2=vert3;
      project.transform(vert2, V2);
      Draw(raster);
    }
  }

  //procedure to clip a triangle
  private void clip() {

    switch(clipmask) {

      //clip vertex 0
    case 1:
      common=(vt1.z-bound)/(vt1.z-vt0.z);
      cl0.r=vt1.r-(vt1.r-vt0.r)*common;
      cl0.g=vt1.g-(vt1.g-vt0.g)*common;
      cl0.b=vt1.b-(vt1.b-vt0.b)*common;
      cl0.x=vt1.x-(vt1.x-vt0.x)*common;
      cl0.y=vt1.y-(vt1.y-vt0.y)*common;
      cl0.z=bound;
      common=(vt2.z-bound)/(vt2.z-vt0.z);
      cl1.r=vt2.r-(vt2.r-vt0.r)*common;
      cl1.g=vt2.g-(vt2.g-vt0.g)*common;
      cl1.b=vt2.b-(vt2.b-vt0.b)*common;
      cl1.x=vt2.x-(vt2.x-vt0.x)*common;
      cl1.y=vt2.y-(vt2.y-vt0.y)*common;
      cl1.z=bound;
      vt0=cl0;
      vt3=cl1;
      vertices++;
      return;

      //clip vertex 1
    case 2:
      common=(vt0.z-bound)/(vt0.z-vt1.z);
      cl0.r=vt0.r-(vt0.r-vt1.r)*common;
      cl0.g=vt0.g-(vt0.g-vt1.g)*common;
      cl0.b=vt0.b-(vt0.b-vt1.b)*common;
      cl0.x=vt0.x-(vt0.x-vt1.x)*common;
      cl0.y=vt0.y-(vt0.y-vt1.y)*common;
      cl0.z=bound;
      common=(vt2.z-bound)/(vt2.z-vt1.z);
      cl1.r=vt2.r-(vt2.r-vt1.r)*common;
      cl1.g=vt2.g-(vt2.g-vt1.g)*common;
      cl1.b=vt2.b-(vt2.b-vt1.b)*common;
      cl1.x=vt2.x-(vt2.x-vt1.x)*common;
      cl1.y=vt2.y-(vt2.y-vt1.y)*common;
      cl1.z=bound;
      vt1=cl0;
      vt3=vt2;
      vt2=cl1;
      vertices++;
      return;

      //clip vertex 2
    case 4:
      common=(vt0.z-bound)/(vt0.z-vt2.z);
      cl0.r=vt0.r-(vt0.r-vt2.r)*common;
      cl0.g=vt0.g-(vt0.g-vt2.g)*common;
      cl0.b=vt0.b-(vt0.b-vt2.b)*common;
      cl0.x=vt0.x-(vt0.x-vt2.x)*common;
      cl0.y=vt0.y-(vt0.y-vt2.y)*common;
      cl0.z=bound;
      common=(vt1.z-bound)/(vt1.z-vt2.z);
      cl1.r=vt1.r-(vt1.r-vt2.r)*common;
      cl1.g=vt1.g-(vt1.g-vt2.g)*common;
      cl1.b=vt1.b-(vt1.b-vt2.b)*common;
      cl1.x=vt1.x-(vt1.x-vt2.x)*common;
      cl1.y=vt1.y-(vt1.y-vt2.y)*common;
      cl1.z=bound;
      vt3=cl0;
      vt2=cl1;
      vertices++;
      return;

      //clip vertex 0 and 1
    case 3:
      common=(vt2.z-bound)/(vt2.z-vt0.z);
      cl0.r=vt2.r-(vt2.r-vt0.r)*common;
      cl0.g=vt2.g-(vt2.g-vt0.g)*common;
      cl0.b=vt2.b-(vt2.b-vt0.b)*common;
      cl0.x=vt2.x-(vt2.x-vt0.x)*common;
      cl0.y=vt2.y-(vt2.y-vt0.y)*common;
      cl0.z=bound;
      common=(vt2.z-bound)/(vt2.z-vt1.z);
      cl1.r=vt2.r-(vt2.r-vt1.r)*common;
      cl1.g=vt2.g-(vt2.g-vt1.g)*common;
      cl1.b=vt2.b-(vt2.b-vt1.b)*common;
      cl1.x=vt2.x-(vt2.x-vt1.x)*common;
      cl1.y=vt2.y-(vt2.y-vt1.y)*common;
      cl1.z=bound;
      vt0=cl0;
      vt1=cl1;
      return;

      //clip vertex 0 and 2
    case 5:
      common=(vt1.z-bound)/(vt1.z-vt0.z);
      cl0.r=vt1.r-(vt1.r-vt0.r)*common;
      cl0.g=vt1.g-(vt1.g-vt0.g)*common;
      cl0.b=vt1.b-(vt1.b-vt0.b)*common;
      cl0.x=vt1.x-(vt1.x-vt0.x)*common;
      cl0.y=vt1.y-(vt1.y-vt0.y)*common;
      cl0.z=bound;
      common=(vt1.z-bound)/(vt1.z-vt2.z);
      cl1.r=vt1.r-(vt1.r-vt2.r)*common;
      cl1.g=vt1.g-(vt1.g-vt2.g)*common;
      cl1.b=vt1.b-(vt1.b-vt2.b)*common;
      cl1.x=vt1.x-(vt1.x-vt2.x)*common;
      cl1.y=vt1.y-(vt1.y-vt2.y)*common;
      cl1.z=bound;
      vt0=cl0;
      vt2=cl1;
      return;

      //clip vertex 1 and 2
    case 6:
      common=(vt0.z-bound)/(vt0.z-vt1.z);
      cl0.r=vt0.r-(vt0.r-vt1.r)*common;
      cl0.g=vt0.g-(vt0.g-vt1.g)*common;
      cl0.b=vt0.b-(vt0.b-vt1.b)*common;
      cl0.x=vt0.x-(vt0.x-vt1.x)*common;
      cl0.y=vt0.y-(vt0.y-vt1.y)*common;
      cl0.z=bound;
      common=(vt0.z-bound)/(vt0.z-vt2.z);
      cl1.r=vt0.r-(vt0.r-vt2.r)*common;
      cl1.g=vt0.g-(vt0.g-vt2.g)*common;
      cl1.b=vt0.b-(vt0.b-vt2.b)*common;
      cl1.x=vt0.x-(vt0.x-vt2.x)*common;
      cl1.y=vt0.y-(vt0.y-vt2.y)*common;
      cl1.z=bound;
      vt1=cl0;
      vt2=cl1;
      return;
    }
  }

  //procedure to draw a triangle onto a raster
  public final void Draw(ZRaster raster) {

    //sort vertices
    if (V0.y<V1.y)
      if (V0.y<V2.y) {
	top=V0;
	top_r=(int) (255*vert0.r);
	top_g=(int) (255*vert0.g);
	top_b=(int) (255*vert0.b);
	if (V1.y>V2.y) {
	  breakpoint=V2;
	  breakpoint_r=(int) (255*vert2.r);
	  breakpoint_g=(int) (255*vert2.g);
	  breakpoint_b=(int) (255*vert2.b);
	  bottom=V1;
	  bottom_r=(int) (255*vert1.r);
	  bottom_g=(int) (255*vert1.g);
	  bottom_b=(int) (255*vert1.b);
	}
	else {
	  breakpoint=V1;
	  breakpoint_r=(int) (255*vert1.r);
	  breakpoint_g=(int) (255*vert1.g);
	  breakpoint_b=(int) (255*vert1.b);
	  bottom=V2;
	  bottom_r=(int) (255*vert2.r);
	  bottom_g=(int) (255*vert2.g);
	  bottom_b=(int) (255*vert2.b);
	}
      }
      else {
	top=V2;
	top_r=(int) (255*vert2.r);
	top_g=(int) (255*vert2.g);
	top_b=(int) (255*vert2.b);
	if (V0.y>V1.y) {
	  breakpoint=V1;
	  breakpoint_r=(int) (255*vert1.r);
	  breakpoint_g=(int) (255*vert1.g);
	  breakpoint_b=(int) (255*vert1.b);
	  bottom=V0;
	  bottom_r=(int) (255*vert0.r);
	  bottom_g=(int) (255*vert0.g);
	  bottom_b=(int) (255*vert0.b);
	}
	else {
	  breakpoint=V0;
	  breakpoint_r=(int) (255*vert0.r);
	  breakpoint_g=(int) (255*vert0.g);
	  breakpoint_b=(int) (255*vert0.b);
	  bottom=V1;
	  bottom_r=(int) (255*vert1.r);
	  bottom_g=(int) (255*vert1.g);
	  bottom_b=(int) (255*vert1.b);
	}
      }
    else
      if (V1.y<V2.y) {
	top=V1;
	top_r=(int) (255*vert1.r);
	top_g=(int) (255*vert1.g);
	top_b=(int) (255*vert1.b);
	if (V0.y>V2.y) {
	  breakpoint=V2;
	  breakpoint_r=(int) (255*vert2.r);
	  breakpoint_g=(int) (255*vert2.g);
	  breakpoint_b=(int) (255*vert2.b);
	  bottom=V0;
	  bottom_r=(int) (255*vert0.r);
	  bottom_g=(int) (255*vert0.g);
	  bottom_b=(int) (255*vert0.b);
	}
	else {
	  breakpoint=V0;
	  breakpoint_r=(int) (255*vert0.r);
	  breakpoint_g=(int) (255*vert0.g);
	  breakpoint_b=(int) (255*vert0.b);
	  bottom=V2;
	  bottom_r=(int) (255*vert2.r);
	  bottom_g=(int) (255*vert2.g);
	  bottom_b=(int) (255*vert2.b);
	}
      }
      else {
	top=V2;
	top_r=(int) (255*vert2.r);
	top_g=(int) (255*vert2.g);
	top_b=(int) (255*vert2.b);
	if (V0.y>V1.y) {
	  breakpoint=V1;
	  breakpoint_r=(int) (255*vert1.r);
	  breakpoint_g=(int) (255*vert1.g);
	  breakpoint_b=(int) (255*vert1.b);
	  bottom=V0;
	  bottom_r=(int) (255*vert0.r);
	  bottom_g=(int) (255*vert0.g);
	  bottom_b=(int) (255*vert0.b);
	}
	else {
	  breakpoint=V0;
	  breakpoint_r=(int) (255*vert0.r);
	  breakpoint_g=(int) (255*vert0.g);
	  breakpoint_b=(int) (255*vert0.b);
	  bottom=V1;
	  bottom_r=(int) (255*vert1.r);
	  bottom_g=(int) (255*vert1.g);
	  bottom_b=(int) (255*vert1.b);
	}
      }

    //create simulated matrix for plane equation calculations
    plane00=breakpoint.y-bottom.y;
    plane01=bottom.y-top.y;
    plane02=top.y-breakpoint.y;
    plane10=bottom.x-breakpoint.x;
    plane11=top.x-bottom.x;
    plane12=breakpoint.x-top.x;
    plane20=-(plane00*breakpoint.x+plane10*breakpoint.y);
    plane21=-(plane01*bottom.x+plane11*bottom.y);
    plane22=-(plane02*top.x+plane12*top.y);

    //2^11 multiplier for float to integer conversion and 2*Area divisor
    common=2048/(top.y*plane10+breakpoint.y*plane11+bottom.y*plane12);

    //z values
    if (top.z==breakpoint.z&&top.z==bottom.z) {//vertices have equal z
      A_z=0;
      B_z=0;
      C_z=(int) (top.z*2048)+1024;             //1024 rounds the result
    }
    else {                                     //vertices have unequal z
      A_z=(int) ((plane00*top.z+plane01*breakpoint.z+plane02*bottom.z)*common);
      B_z=(int) ((plane10*top.z+plane11*breakpoint.z+plane12*bottom.z)*common);
      C_z=(int) ((plane20*top.z+plane21*breakpoint.z+plane22*bottom.z)*common)
	+1024;                                //1024 rounds the result
    }

    //red values
    if (top_r==breakpoint_r&&top_r==bottom_r) {//vertices have equal red
      A_r=0;
      B_r=0;
      C_r=top_r<<11;
    }
    else {                                    //vertices have unequal red
      A_r=(int) ((plane00*top_r+plane01*breakpoint_r+plane02*bottom_r)*common);
      B_r=(int) ((plane10*top_r+plane11*breakpoint_r+plane12*bottom_r)*common);
      C_r=(int) ((plane20*top_r+plane21*breakpoint_r+plane22*bottom_r)*common)
	+1024;                                //1024 rounds the result
    }

    //green values
    if (top_g==breakpoint_g&&top_g==bottom_g) {//vertices have equal green
      A_g=0;
      B_g=0;
      C_g=top_g<<11;
    }
    else {                                    //vertices have unequal green
      A_g=(int) ((plane00*top_g+plane01*breakpoint_g+plane02*bottom_g)*common);
      B_g=(int) ((plane10*top_g+plane11*breakpoint_g+plane12*bottom_g)*common);
      C_g=(int) ((plane20*top_g+plane21*breakpoint_g+plane22*bottom_g)*common)
	+1024;                                //1024 rounds the result
    }

    //blue values
    if (top_b==breakpoint_b&&top_b==bottom_b) {//vertices have equal blue
      A_b=0;
      B_b=0;
      C_b=top_b<<11;
    }
    else {                                    //vertices have unequal blue
      A_b=(int) ((plane00*top_b+plane01*breakpoint_b+plane02*bottom_b)*common);
      B_b=(int) ((plane10*top_b+plane11*breakpoint_b+plane12*bottom_b)*common);
      C_b=(int) ((plane20*top_b+plane21*breakpoint_b+plane22*bottom_b)*common)
	+1024;                                //1024 rounds the result
    }

    //determine scanline loop boundaries
    if (top.y<=-1)
      y=0;
    else
      y=(int) Math.ceil(top.y);
    breakpoint_stop=((int) breakpoint.y+1)*raster.width;
    if (breakpoint_stop<y)
      breakpoint_stop=y;
    if (bottom.y<raster.height)
      bottom_stop=(int) bottom.y*raster.width;
    else
      bottom_stop=(raster.height-1)*raster.width;

    //determine which side the breakpoint is on
    temp=(top.x-bottom.x)/(top.y-bottom.y);
    if (bottom.x+(breakpoint.y-bottom.y)*temp>breakpoint.x)
      left=true;
    else
      left=false;

    if (left) {                                //breakpoint on left side

      //determine the deltas of scanline x coordinates between scanlines
      dstart=(int) (65536*(top.x-breakpoint.x)/(top.y-breakpoint.y));
      dstop=(int) (65536*temp);

      //determine scanline endpoint x values  (65535 is a ceiling factor)
      start=(int) (65536*breakpoint.x+(y-breakpoint.y)*dstart)+65535;
      stop=(int) (65536*bottom.x+(y-bottom.y)*dstop);
    }
    else {                                     //breakpoint on right side

      //determine the deltas of scanline x coordinates between scanlines
      dstart=(int) (65536*temp);
      dstop=(int) (65536*(top.x-breakpoint.x)/(top.y-breakpoint.y));

      //determine scanline endpoint x values  (65535 is a ceiling factor)
      start=(int) (65536*bottom.x+(y-bottom.y)*dstart)+65535;
      stop=(int) (65536*breakpoint.x+(y-breakpoint.y)*dstop);
    }

    //calculate z and color components at beginning endpoint of scanline
    scan_z=B_z*y+C_z;
    scan_r=B_r*y+C_r;
    scan_g=B_g*y+C_g;
    scan_b=B_b*y+C_b;

    //draw scanlines
    y*=raster.width;
    for (; y<=bottom_stop; y+=raster.width) {

      //check to see if at breakpoint
      if (y==breakpoint_stop) {
	if (left) {                           //breakpoint on left side
	  //calculate new start and dstart  (65535 is a ceiling factor)
	  dstart=(int) (65536*(breakpoint.x-bottom.x)/(breakpoint.y-bottom.y));
	  start=(int) (65536*breakpoint.x-
		       (breakpoint.y-y/raster.width)*dstart)+65535;
	}
	else {
	  //calculate new stop and dstop for lower half of triangle
	  dstop=(int) (65536*(breakpoint.x-bottom.x)/(breakpoint.y-bottom.y));
	  stop=(int) (65536*breakpoint.x-
		      (breakpoint.y-y/raster.width)*dstop);
	}
      }

      //set beginning endpoint for scanline
      x=start>>16;
      if (x<0)
	x=0;

      //calculate z and color components at first pixel in scanline
      pixel_z=scan_z+A_z*x;
      pixel_r=scan_r+A_r*x;
      pixel_g=scan_g+A_g*x;
      pixel_b=scan_b+A_b*x;

      xy=x+y;
      scanline_stop=stop>>16;
      if (scanline_stop>=raster.width)
	scanline_stop=raster.width-1;
      for (; x<=scanline_stop; x++, xy++) {         //iterate across scanline

	//check z buffer
	z=pixel_z>>11;
	if (z<raster.zbuff[xy]) {
	  //draw pixel onto raster  (-16777216 sets alpha channel to 0xff)
	  raster.pixel[xy]=(pixel_r>>11<<16)+(pixel_g>>11<<8)+
	    (pixel_b>>11)-16777216;
	  raster.zbuff[xy]=z;
	}

	//calculate z and color components for next pixel
	pixel_z+=A_z;
	pixel_r+=A_r;
	pixel_g+=A_g;
	pixel_b+=A_b;
      }

      //calculate z and color components for next scanline origin
      scan_z+=B_z;
      scan_r+=B_r;
      scan_g+=B_g;
      scan_b+=B_b;

      //calculate scanline endpoints for next scanline
      start+=dstart;
      stop+=dstop;
    }
  }
}
