import Drawable;
import Vertex2D;

class Triangle implements Drawable {
  protected Vertex2D v[];
  
  public Triangle() {
  }
  
  public Triangle(Vertex2D v0, Vertex2D v1, Vertex2D v2) {
    v = new Vertex2D[3];
    v[0] = v0;
    v[1] = v1;
    v[2] = v2;
  }
  
  /**********************************************************************/
  /* Incremental Edge Walking Triangle Fill                             */
  /*                                                                    */
  /* Parameters are interpolated across a triangle incrementally        */
  /* For each parameter the incremental change for a step along an      */
  /* and for a horizontal step are precomputed                          */
  /**********************************************************************/

  public void Draw(Raster r)  
    {
      
      int max = 0;                      // index of top vertex
      boolean left = true;              // breakpoint direction flag
      
      float[] bp = new float[2];
      
      // find top left vertex
      
      for (int i = 1; i < 3; i++)
	if (v[i].y <= v[max].y)
	  {
	    if (v[i].y < v[max].y)
	      max = i;
	    else
	      if (v[i].x < v[max].x)
		max = i;
	  }

      // find left and right vertices

      int lv = max-1;
      if (lv < 0)
	lv = 2;
      int rv = lv-1;
      if (rv < 0)
	rv = 2;

      // set breakpoints
   
      if (v[lv].y <= v[rv].y)
	{
	  bp[0] = v[lv].y;
	  bp[1] = v[rv].y;
	}
      else
	{
	  bp[0] = v[rv].y;
	  bp[1] = v[lv].y;
	  left = false;
	}

      
      // initialize left and right edges
      
      // The starting values for the parameters and the incremental changes
      // for steps along the edge are computed in the Edge constructor 

      Edge l_edge= new Edge(v[max], v[lv]);
      Edge r_edge = new Edge(v[max], v[rv]);
      
      // initialize break point edge

      Edge next_edge;
      if (left)
	next_edge = new Edge(v[lv], v[rv]);
      else
	next_edge = new Edge(v[rv], v[lv]);
      
      // retrieve initial values for parameters

      float xl = l_edge.startx;
      float xr = r_edge.startx;
      float l_alpha = l_edge.starta;
      float l_red = l_edge.startr;
      float l_green = l_edge.startg;
      float l_blue = l_edge.startb;

      float x_dalpha, x_dred, x_dgreen, x_dblue;
      
      // Compute horizontal step values for parameters
      // Uses the formula: (dz/dx = ((dy2*dz1 - dy1*dz2)/(dy2*dx1 - dy1*dx2))
      // for any parameter z.

      float dx1 = l_edge.deltax;
      float dy1 = l_edge.deltay;
      float dx2 = r_edge.deltax;
      float dy2 = r_edge.deltay;
      float det = dy2*dx1 - dy1*dx2;
      
      int da1 = l_edge.deltaa;
      int da2 = r_edge.deltaa;
      x_dalpha = (dy2*da1 - dy1*da2)/ det;
      
      int dr1 = l_edge.deltar;
      int dr2 = r_edge.deltar;
      x_dred = (dy2*dr1 - dy1*dr2)/ det;

      int dg1 = l_edge.deltag;
      int dg2 = r_edge.deltag;
      x_dgreen = (dy2*dg1 - dy1*dg2)/det;
      
      int db1 = l_edge.deltab;
      int db2 = r_edge.deltab;
      x_dblue = (dy2*db1 - dy1*db2)/det;
 
      // initialize y 
      int y = (int) Math.ceil(v[max].y);
      
      boolean brk = false; // breakpoint flag

      while (y <= bp[1])
	{
	  if (!brk)

	    // Change edge when past brakpoint
	    if (y >= bp[0] && y != bp[1])
	      {
		if (left)
		  {
		    // initial parameters change when left edge changes

		    l_edge = next_edge;
		    xl = l_edge.startx;
		    l_alpha = l_edge.starta;
		    l_red = l_edge.startr;
		    l_green = l_edge.startg;
		    l_blue = l_edge.startb;
		    brk = true;
		  }
		else
		  {
		    r_edge = next_edge;
		    xr = r_edge.startx;
		    brk = true;
		  }
	      }

	  // Compute endpoints of scan line

	  int ixl = (int) Math.ceil(xl - 0.0005); 
	  int ixr = (int) (xr + 0.0005);

	  // adjust accumulated rounding errors

	  if (Math.abs(xl - ixl) <= 0.0005)
	    xl = ixl;
	  if (Math.abs(xr - ixr) <= 0.0005)
	    xr = ixr;
	  

	  // Update initial parameter values using fractional offset from
	  // the ideal endpoints

	  float alpha = (x_dalpha)*(ixl-xl) + l_alpha;
	  float red = (x_dred)*(ixl-xl) + l_red;
	  float green = (x_dgreen)*(ixl - xl) + l_green;
	  float blue = (x_dblue)*(ixl - xl) + l_blue;
	  
	  // Move across scan line

	  for (int x = ixl; x <= ixr; x++)
	    {
	      r.setPixel(((int)(alpha+ 0.5) << 24) |  ((int) (red + 0.5) << 16) | ((int) (green + 0.5) << 8) | ((int) (blue + 0.5)), x, y);
	      
	      // increment parameters horizontally

	      alpha += x_dalpha;
	      red += x_dred;
	      blue += x_dblue;
	      green += x_dgreen;
	    }
	  
	  // move down edge and increment parameters in edge direction
 
	  y++;
	  xl += l_edge.dx;
	  xr += r_edge.dx;
	  l_alpha += l_edge.da;
	  l_red += l_edge.dr;
	  l_green += l_edge.dg;
	  l_blue += l_edge.db;
	}
    }
}
	      








