import Drawable;
import Vertex2D;

// Creates an edgeList and scans the triangle. Implements color interpolation.

class Triangle implements Drawable {

  
  protected Vertex2D v[];
  EdgeList edges;
  float ymax, ymin;
  double area2;
  double Ab, Bb, Cb, Ag, Bg, Cg, Ar, Br, Cr, Aa, Ba, Ca;

  
  public Triangle() {
  }

  
  public Triangle(Vertex2D v0, Vertex2D v1, Vertex2D v2) {
    v = new Vertex2D[3];
    v[0] = v0;
    v[1] = v1;
    v[2] = v2;
    setMaxMin();
  }


  public void Draw(Raster raster) {

    buildEdgeList();

    // calculate double area (for colors)
    area2 = (v[0].x*(v[1].y-v[2].y) - v[1].x*(v[0].y-v[2].y) +
	    v[2].x*(v[0].y-v[1].y));

    // compute color coefficients
    colorCoeff();

    // scan the triangle and fill in pixels
    edgeWalk(raster);

  }


  // compute coefficients for color plane equations
  public void colorCoeff() {

    int c0 = v[0].argb;
    int c1 = v[1].argb;
    int c2 = v[2].argb;
    int t0, t1, t2;

    // compute blue
    t0 = c0 & 0xff;
    t1 = c1 & 0xff;
    t2 = c2 & 0xff;
    Ab = (v[1].y-v[2].y)*t0 + (v[2].y-v[0].y)*t1 + (v[0].y-v[1].y)*t2;
    Ab = Ab/area2;
    Bb = (v[2].x-v[1].x)*t0 + (v[0].x-v[2].x)*t1 + (v[1].x-v[0].x)*t2;
    Bb = Bb/area2;
    Cb = (v[1].x*v[2].y-v[2].x*v[1].y)*t0 + (v[2].x*v[0].y-v[0].x*v[2].y)*t1 +
      (v[0].x*v[1].y-v[1].x*v[0].y)*t2;
    Cb = Cb/area2;
    
    // compute green
    c0 >>= 8;   c1 >>= 8;   c2 >>= 8;
    t0 = c0 & 0xff;
    t1 = c1 & 0xff;
    t2 = c2 & 0xff;    
    Ag = (v[1].y-v[2].y)*t0 + (v[2].y-v[0].y)*t1 + (v[0].y-v[1].y)*t2;
    Ag = Ag/area2;
    Bg = (v[2].x-v[1].x)*t0 + (v[0].x-v[2].x)*t1 + (v[1].x-v[0].x)*t2;
    Bg = Bg/area2;
    Cg = (v[1].x*v[2].y-v[2].x*v[1].y)*t0 + (v[2].x*v[0].y-v[0].x*v[2].y)*t1 +
      (v[0].x*v[1].y-v[1].x*v[0].y)*t2;
    Cg = Cg/area2;

    // compute red
    c0 >>= 8;   c1 >>= 8;   c2 >>= 8;
    t0 = c0 & 0xff;
    t1 = c1 & 0xff;
    t2 = c2 & 0xff;
    Ar = (v[1].y-v[2].y)*t0 + (v[2].y-v[0].y)*t1 + (v[0].y-v[1].y)*t2;
    Ar = Ar/area2;
    Br = (v[2].x-v[1].x)*t0 + (v[0].x-v[2].x)*t1 + (v[1].x-v[0].x)*t2;
    Br = Br/area2;
    Cr = (v[1].x*v[2].y-v[2].x*v[1].y)*t0 + (v[2].x*v[0].y-v[0].x*v[2].y)*t1 +
      (v[0].x*v[1].y-v[1].x*v[0].y)*t2;
    Cr = Cr/area2;

    // compute alpha
    c0 >>= 8;   c1 >>= 8;   c2 >>= 8;
    t0 = c0 & 0xff;
    t1 = c1 & 0xff;
    t2 = c2 & 0xff;
    Aa = (v[1].y-v[2].y)*t0 + (v[2].y-v[0].y)*t1 + (v[0].y-v[1].y)*t2;
    Aa = Aa/area2;
    Ba = (v[2].x-v[1].x)*t0 + (v[0].x-v[2].x)*t1 + (v[1].x-v[0].x)*t2;
    Ba = Ba/area2;
    Ca = (v[1].x*v[2].y-v[2].x*v[1].y)*t0 + (v[2].x*v[0].y-v[0].x*v[2].y)*t1 +
      (v[0].x*v[1].y-v[1].x*v[0].y)*t2;
    Ca = Ca/area2;

  }


  public void edgeWalk(Raster raster) {

    // set upper and lower limits for y
    int yMin = (int)(Math.ceil(ymin));
    int yMax = (int)(ymax);

    // set the start point for incrementing plane equations
    double a0 = Ba * yMin + Ca;
    double b0 = Bb * yMin + Cb;
    double r0 = Br * yMin + Cr;
    double g0 = Bg * yMin + Cg;

    // scan each line

    for (int scan=yMin; scan<=yMax; scan++) {

      // switch edges if reached the breakpoint
      if (edges.isBreakpoint) {
	if (scan>=edges.yBreakpoint) {
	  edges.updateBreakpoint();
	}
      }

      // set upper and lower limits for x
      int leftx = (int)(Math.ceil(edges.left.xIntersect));
      int rightx = (int)(edges.right.xIntersect);

      // set initial plane equations
      double a1 = a0 + Aa * leftx;
      double r1 = r0 + Ar * leftx;
      double g1 = g0 + Ag * leftx;
      double b1 = b0 + Ab * leftx;
      
      // fill in the color between the edges
      for (int i=leftx; i<=rightx; i++) {

	// round the colors to the nearest integer
	int a = (int)(a1 + 0.5);
	int r = (int)(r1 + 0.5);
	int b = (int)(b1 + 0.5);
	int g = (int)(g1 + 0.5);

	// make sure that the colors are between 0 and 255
	int pixa = ((a > 255) ? 0xff000000 : ((a < 0) ? 0 : a << 24));
	int pixr = ((r > 255) ? 0x00ff0000 : ((r < 0) ? 0 : r << 16));
	int pixg = ((g > 255) ? 0x0000ff00 : ((g < 0) ? 0 : g << 8));
	int pixb = ((b > 255) ? 0x000000ff : ((b < 0) ? 0 : b ));

	raster.setPixel(pixa | pixr | pixg | pixb, i, scan);

	// increment the A*x term of the plane equations
	a1 += Aa;
	r1 += Ar;
	g1 += Ag;
	b1 += Ab;

      }

      edges.left.updateX();
      edges.right.updateX();

      
      // increment the B*y term of the plane equations
      a0 += Ba;
      r0 += Br;
      g0 += Bg;
      b0 += Bb;
	
    } 
  }
  

  private void setMaxMin() {

    // set ymax and ymin

    if (v[0].y>=v[1].y && v[0].y>=v[2].y) {
      ymax = v[0].y;
      if (v[1].y < v[2].y)
	ymin = v[1].y;
      else
	ymin = v[2].y;
    }

    else if (v[1].y>=v[2].y && v[1].y>=v[0].y) {
      ymax = v[1].y;
      if (v[0].y < v[2].y)
	ymin = v[0].y;
      else
	ymin = v[2].y;
    }
      
    else {
      ymax = v[2].y;
      if (v[0].y < v[1].y)
	ymin = v[0].y;
      else
	ymin = v[1].y;
    }

  }


  private void buildEdgeList() {

    Edge e1, e2, e0;
    e0 = new Edge(v[0].y, v[2].y, v[0].x, v[2].x);
    e1 = new Edge(v[1].y, v[2].y, v[1].x, v[2].x);
    e2 = new Edge(v[1].y, v[0].y, v[1].x, v[0].x);
    
    // determine if there are 2 or 3 edges (i.e., if there is a breakpoint)
    if (v[0].y == v[1].y) {
      edges = new EdgeList(e0, e1, e2, true);
    }
    else if (v[1].y == v[2].y) {
      edges = new EdgeList(e0, e2, e1, true);
    }
    else if (v[0].y == v[2].y) {
      edges = new EdgeList(e2, e1, e0, true);
    }
    else {
      // create 3 edges;
      edges = new EdgeList(e0, e1, e2, false);
    }
      
  }
    
  
}

