import Drawable;
import Vertex2D;

// George Eadon
// 6.837 Project #2
// October 13, 1998

class Triangle implements Drawable {
  protected Vertex2D v[];
  int iter = 0;
  
  public Triangle() {
  }

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

  public void doScanLine(MyVertex2D v0, MyVertex2D v1, Raster rast) {
    // 
    // This function draws a horizontal line between v0 and v1 onto rast.
    // It is assumed that v0.y = v1.y = an integer. v0 and v1 can be
    // given in any order.  
    // All pixels whose centers are between v0 and v1 are turned on.
    // If a pixel lies exactly on v0 or v1, it too is turned on.
    // The color of each pixel is interpolated linearly between v0 and v1 
    // according to the following functions that form a parametric description
    // of the line we want to draw:
    //
    //     x(t) = (1-t)*x0 + t*x1 ===> t(x) = (x-x0)/(x1-x0)
    // alpha(t) = (1-t)*a0 + t*a1   
    //   red(t) = (1-t)*r0 + t*r1
    // green(t) = (1-t)*g0 + t*g1
    //  blue(t) = (1-t)*b0 + t*b1
    //
    // So given an x-value, we can compute the appropriate color value
    // at that point with: alpha = alpha(t(x))
    //                       red = red(t(x))
    //                     green = green(t(x))
    //                      blue = blue(t(x))
    //
    // The code is written for brevity and readibility with no real regard
    // to speed. If speed were desired, one could easily compute the
    // approriate delta-a, delta-r, delta-g and delta-b for an increment
    // of x by one and then we could calculate the color of each
    // pixel incrementally. We could even keep track of the color values
    // and increments with fixed-point integers for a very fast implementation.
    // This might lead to some in accuracies tho'.

    int y = (int) v0.y;
    if (v0.x > v1.x) {       // Swap the verticies if necessary so that
      MyVertex2D t = v0;     // v0 lies to the left of v1. 
      v0 = v1;
      v1 = t;
    }
    int x = (int) Math.ceil(v0.x);

    if (v0.argb == v1.argb)             // Lines of constant color can be
      for (; x<=Math.floor(v1.x); ++x)  // handled w/o interpolating
	rast.setPixel(v0.argb, x, y);
    else { 
      int pix, a, r, g, b;
      double t;

      if (v1.x == v0.x && v1.x == x)      // unlikely, but hafta check
	rast.setPixel(v0.argb, x, y);
      else {
	for (; x<=Math.floor(v1.x); ++x) {
	  t = (x - v0.x)/(v1.x - v0.x);  // inverse of:
	                                 // y(t) = (1-t)*v0.x + t*v1.x

	  a = (int)((1-t)*v0.a + t*v1.a + 0.5);
	  r = (int)((1-t)*v0.r + t*v1.r + 0.5);
	  g = (int)((1-t)*v0.g + t*v1.g + 0.5);
	  b = (int)((1-t)*v0.b + t*v1.b + 0.5);

	  // We could use the algebraicly equivelent equations of the form:
	  //  a = (int)(v0.a + t*(v1.a-v0.a) + 0.5)
	  // We could pre-compute v1.a-v0.a and v0.a+0.5 outside of the loop
	  // to reduce the calculations to one multiply and one add.
	  // But the equations that are used are more intuitive, I think.

	  pix = (a<<24)|(r<<16)|(g<<8)|b;
	  
	  rast.setPixel(pix, x, y);
	}
      }
    }
  }
    
  public void Draw(Raster r) {
    EdgeWalker e0, e1;
    int y;

    // Sort the verticies top to bottom
    if (v[1].y < v[0].y) { Vertex2D t = v[0]; v[0] = v[1]; v[1] = t; }
    if (v[2].y < v[0].y) { Vertex2D t = v[0]; v[0] = v[2]; v[2] = t; }
    if (v[2].y < v[1].y) { Vertex2D t = v[1]; v[1] = v[2]; v[2] = t; }

    // check to be sure the three given points don't lie on a line!
    if (v[0].x == v[1].x && v[1].x == v[2].x)
      return;
    else if ((v[0].x != v[1].x && v[1].x != v[2].x) &&
	     ((v[0].y-v[1].y)/(v[0].x-v[1].x) == (v[1].y-v[2].y)/(v[1].x-v[2].x)))
      return;

    // Check for triangles w/ horizontal tops
    if (Math.ceil(v[0].y) == Math.ceil(v[1].y)) {
      e0 = new EdgeWalker(v[0],v[2]);
      e1 = new EdgeWalker(v[1],v[2]);
      for(y = (int) Math.ceil(v[0].y); y <= Math.floor(v[2].y); ++y)
	doScanLine(e0.Step(y), e1.Step(y), r);

    } else {     // General Case
      e0 = new EdgeWalker(v[0], v[1]);
      e1 = new EdgeWalker(v[0], v[2]);

      for(y = (int) Math.ceil(v[0].y); y <= Math.floor(v[1].y); ++y)
	doScanLine(e0.Step(y), e1.Step(y), r);

      if (v[1].y != v[2].y) {            
	e0 = new EdgeWalker(v[1], v[2]);

	for(y = (int) Math.ceil(v[1].y); y <= Math.floor(v[2].y); ++y)
	  doScanLine(e0.Step(y), e1.Step(y), r);
      }
    }
  }
}





