/*  This is Joey's triangle class for project 2 of 6.837, Fall 1998.
*/

import Drawable;
import Vertex2D;
import planeEquation;

class Triangle implements Drawable {
    protected Vertex2D v[];
	protected float m[], b[];	// slopes and intercepts
	protected int whichCase;	// which type of vtx configuration do we have?
	protected static final int NO_BRK_T=0, NO_BRK_B=1, BRK_L=2, BRK_R=3;
	protected int startLine, stopLine, breakLine; // points in the scan procedure
	protected int spans[][];	// starts/stops for the spans...
	protected planeEquation myEq;	

    public Triangle() {
    }

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

		myEq=new planeEquation(v);
		
		m = new float[3];
		b = new float[3];
		/* find the minimum y vtx */
		if ((v[1].y < v[0].y)||(v[2].y <= v[0].y)) // can 1 or 2 be lowest?
		{
			if(v[1].y <= v[2].y) // is 1 the lowest?
			{
				Vertex2D tmpVtx = new Vertex2D(0,0,0);
				tmpVtx = v[0];
				v[0] = v[1];	// rotate to put old v[1] at the top
				v[1] = v[2];
				v[2] = tmpVtx;
				tmpVtx = null;	// throw out tmpVtx
			} else { // 2 < 1 < 0
				Vertex2D tmpVtx = new Vertex2D(0,0,0);
				tmpVtx = v[0];
				v[0] = v[2];	// rotate to put old v[2] at top
				v[2] = v[1];
				v[1] = tmpVtx;
				tmpVtx = null;	// throw out tmpVtx
			}
		}	// otherwise, 0 is the minimum, so we don't do anything

		/* We now have the vertices ordered such that any single minimum-y
			vertex is 0, with the others in clockwise order.  If 2 share the
			same minimum y-value, then 0 is the left-most of them with 2 being
			the loner at the higher y-value.
		*/
		
		if (v[0].y==v[1].y)
			whichCase = NO_BRK_B;	// no breakpoint, single at bottom
		else if (v[1].y == v[2].y)
			whichCase = NO_BRK_T;	// no breakpoint, single at top
		else if (v[1].y < v[2].y)
			whichCase = BRK_R;		// breakpoint on right
		else
			whichCase = BRK_L;		// breakpoint on left

		switch(whichCase) // set up the coords for rendering
		{
			case NO_BRK_B: // no breakpoint, single at bot
				m[0] = getSlope(v[0], v[2]);
				m[1] = getSlope(v[1], v[2]);
				b[0] = getIntercept(v[0], v[2]);
				b[1] = getIntercept(v[1], v[2]);
				startLine = (int) Math.ceil(v[0].y);
				stopLine = (int) Math.floor(v[2].y);
				breakLine = 500; // off the end of the raster
				break;
			case NO_BRK_T: // no breakpoint, single at top
				m[0] = getSlope(v[0], v[2]);
				m[1] = getSlope(v[0], v[1]);
				b[0] = getIntercept(v[0], v[2]);
				b[1] = getIntercept(v[0], v[1]);
				startLine = (int) Math.ceil(v[0].y);
				stopLine = (int) Math.floor(v[2].y);
				breakLine = 500; // off the end of the raster
				break;
			case BRK_R: // breakpoint on right
				m[0] = getSlope(v[0], v[2]);
				m[1] = getSlope(v[0], v[1]);
				m[2] = getSlope(v[1], v[2]);
				b[0] = getIntercept(v[0], v[2]);
				b[1] = getIntercept(v[0], v[1]);
				b[2] = getIntercept(v[1], v[2]);
				startLine = (int) Math.ceil(v[0].y);
				breakLine = (int) Math.floor(v[1].y);
				stopLine = (int) Math.floor(v[2].y);
				break;
			case BRK_L: // breakpoint on left
				m[0] = getSlope(v[0], v[2]);
				m[1] = getSlope(v[0], v[1]);
				m[2] = getSlope(v[2], v[1]);
				b[0] = getIntercept(v[0], v[2]);
				b[1] = getIntercept(v[0], v[1]);
				b[2] = getIntercept(v[2], v[1]);
				startLine = (int) Math.ceil(v[0].y);
				breakLine = (int) Math.floor(v[2].y);
				stopLine = (int) Math.floor(v[1].y);
				break;
		}
		
		spans = new int[400][2];
		
		int i;
		for(i=startLine; (i<=breakLine)&&(i<=stopLine); i++)
		{
			spans[i][0]=(int) Math.ceil(m[0]*i+b[0]);
			spans[i][1]=(int) Math.floor(m[1]*i+b[1]);
		}
		if(breakLine < stopLine)
		{
			if(whichCase == BRK_R)
			{
				while(i <= stopLine)
				{
					spans[i][0]=(int) Math.ceil(m[0]*i+b[0]);
					spans[i][1]=(int) Math.floor(m[2]*i+b[2]);
					i++;
				}
			} else { // must be BRK_L
				while(i <= stopLine)
				{
					spans[i][0]=(int) Math.ceil(m[2]*i+b[2]);
					spans[i][1]=(int) Math.floor(m[1]*i+b[1]);
					i++;
				}
			}
		}					
	}
    
	/**
	 * Returns the (inverse) slope dx/dy
	 *
	 */
    private float getSlope(Vertex2D v0, Vertex2D v1)  
    {
    	return( (v1.x-v0.x)/(v1.y-v0.y) );
    }
    
	/**
	 * Returns the (inverse) intercept s.t. x=my+b
	 *
	 */
    private float getIntercept(Vertex2D v0, Vertex2D v1)
    {
    	return(v0.x-v0.y*(v1.x-v0.x)/(v1.y-v0.y));
    }
    
    public void Draw(Raster r) {
		for(int i=startLine; i<=stopLine; i++)
		{
			if(spans[i][1] >= spans[i][0])
			{
				for(int x=spans[i][0];x<=spans[i][1];x++)
				{
					r.setPixel(myEq.computeColor(x,i), x, i);
				}
			}
		}
    }
}
