import java.awt.*;
import java.lang.*;

public class Triangle3D implements Drawable {
  public final static int FRACBITS = 12 ;
  protected Vertex3D v[];
  protected int flatClr;
  protected float zEqn[];
  protected Surface surface;
  protected boolean hasSurface;
  
  protected EdgeEqn edge[];
  protected float twiceArea;
  protected double scale; 
  protected static byte sort[][]= {{0, 1},{1, 2},{0, 2},{2, 0},{2, 1},{1, 0}};
  protected int vtxSortX[];
  protected int vtxSortY[];
  protected int noseNegY, nosePosY, ltBreak, rtBreak;
  protected int activeEdge[];
  protected boolean  isPrllYZ;
  protected boolean  isPrllXZ;
  float  XOrYOrder[] ;

  ///////  CONSTRUCTORS /////////////////////////////  
  
  // constructor using three Vertex3D 
  public Triangle3D( Vertex3D v0,  Vertex3D v1,  Vertex3D v2 ) {
    v = new Vertex3D[3];
    v[0]=v0;    v[1]=v1;  v[2]=v2;
    v[0].setVertexNo(0);
    v[1].setVertexNo(1); 
    v[2].setVertexNo(2);
    vtxSortX = new int[3]; vtxSortY = new int[3];
    noseNegY= -1;    nosePosY= -1;
    ltBreak = -1;    rtBreak = -1;
    activeEdge = new int[2];
    hasSurface = false;
    flatClr = v[0].getColor();
    scale = -1;
    zEqn = new float[3];  
    //   this.triangleSetup();
  }	
  
  /////////  METHODS  ////////////////////////////
 
  protected void triangleSetup( ) {
      //       System.out.println("IN TRIANGLESETP(RASTER)" );
    if (edge == null) edge = new EdgeEqn[3];
    
    //Compute the three edge equations  
    edge[2] = new EdgeEqn(2, v[0], v[1]);
    edge[0] = new EdgeEqn(0, v[1], v[2]);
    edge[1] = new EdgeEqn(1, v[2], v[0]);
    
    //Trick #1: Orient edges so that the triangle's interior lies within all of
    //their positive half-spaces. Assuring that the area is positive accomplishes this
    twiceArea = edge[0].getC() + edge[1].getC() + edge[2].getC();
    //     System.out.println("twiceArea=" + twiceArea);
    if (twiceArea  < 0)   makeCounterClockWise();
    //    System.out.println("twiceArea=" +  twiceArea);

    //CHECKING FOR XZ AND YZ PLANES
    //if( (!isXZPlane()) && (!isYZPlane()) ) {
      //Trick #2: compute bounding box 
      int xflag = edge[2].flag + 2*edge[0].flag + 4*edge[1].flag;
      int yflag = (xflag >> 3) - 1;
      xflag = (xflag & 7) - 1;
      //System.out.println("xflag=" +  xflag);    
      vtxSortX[0] = sort[xflag][0];
      vtxSortX[2] = sort[xflag][1];
      vtxSortX[1] = 3 - ( vtxSortX[0] + vtxSortX[2] );
      vtxSortY[0] = sort[yflag][1];
      vtxSortY[2] = sort[yflag][0];
      vtxSortY[1] = 3 - ( vtxSortY[0] + vtxSortY[2] );
      isNoBreakPoint();
      //}
 
    float z[] = new float[3];
    for( int i=0; i<3; i++ ) {
	if( isNotZero(v[i].getCoord(3)) ) {
	    z[i] = v[i].getCoord(2)/v[i].getCoord(3);
	}
	//TK:DO SOMETHING ABOUT THIS
	if(isPosZero(v[i].getCoord(3)) ) {
	    System.err.println( "WARNING: InterpolatedTriangle3D.interpolatedTriangleSetup( ).isPosZero(v[i].getCoord(3) is accessed" );         
   	}
	if(isNegZero(v[i].getCoord(3)) ) {
	    System.err.println( "WARNING: InterpolatedTriangle3D.interpolatedTriangleSetup( ).isNegZero(v[i].getCoord(3) is accessed" );   
	}
    }
    planeEqn(zEqn, z[0], z[1], z[2]);
    return;
  }//TRIANGLESETUP

 //    protected boolean isYZPlane() {
//     if( (isNotZero(v[0].getCoord(0)-v[1].getCoord(0))==false) && (isNotZero(v[0].getCoord(0)-v[2].getCoord(0))==false) ) {
//       XOrYOrder = new float[3];  
//       XOrYOrder[0]= v[0].getCoord(1);      XOrYOrder[1]= v[1].getCoord(1);      XOrYOrder[2]= v[2].getCoord(1); 
//       arrange( XOrYOrder,3);
//       isPrllYZ = true; 
//       return true;
//     } else{
//       isPrllYZ = false; 
//       return false;   
//     }
// }
//   protected boolean isXZPlane() {
//     if( (isNotZero(v[0].getCoord(1)-v[1].getCoord(1))==false) && (isNotZero(v[0].getCoord(1)-v[2].getCoord(1))==false) ) { 
//       XOrYOrder = new float[3];   
//       XOrYOrder[0]= v[0].getCoord(0);      XOrYOrder[1]= v[1].getCoord(0);      XOrYOrder[2]= v[2].getCoord(2); 
//       arrange( XOrYOrder, 3);
//       isPrllXZ = true; 
//       return true;
//     }  else {
//       isPrllXZ = false; 
//       return false;   
//     }
//   }


  protected void makeCounterClockWise() {
      edge[0].flip();    edge[1].flip();    edge[2].flip();
      twiceArea = -twiceArea ;

      Vertex3D tempVtx = v[1];
      v[1] = v[2];
      v[2] = tempVtx;
      v[1].setVertexNo(1);
      v[2].setVertexNo(2);

      edge[0].reSet( 0, v[1], v[2] );
      edge[1].reSet( 1, v[2], v[0] );
      edge[2].reSet( 2, v[0], v[1] );
  }


  protected void isNoBreakPoint() {
      //System.out.println("IN ISNOBREAKPOINT" );
    int i=0;
    int flag = 0;
    do {
      if( edge[i].horiz ) {
	flag = 1;
	noseNegY = i;
      }
      i++;
    } while( (flag==0) && (i<3) );
    if( flag==1 ){
      if( noseNegY==vtxSortY[0] ){
	 return; 
      }
      else {
	nosePosY=noseNegY; noseNegY= -1;
	return; 
      }
    }
    float x = v[vtxSortY[1]].getCoord(0); 
    float y = v[vtxSortY[1]].getCoord(1); 
    if( (vtxSortY[1] !=vtxSortX[2])&&(edge[vtxSortY[1]].findXAtGivenY(y) > x) ) {
      ltBreak = vtxSortY[1];
      return;
    }
    if( (vtxSortY[1] !=vtxSortX[0])&&(edge[vtxSortY[1]].findXAtGivenY(y) < x  )  ) {
      rtBreak = vtxSortY[1];
      return;
    }
    return;
  } //ISNOBREAKPOINT


 //this will always return first the edge closer to x=0 and then the one further away
  public void getActiveEdges( ) {
      //System.out.println("IN GETACTIVEEDGES(void)" );
    if( noseNegY >= 0 ) {
      activeEdge[0] = getEdgeNo( noseNegY+1 );
      activeEdge[1] = getEdgeNo( noseNegY-1 ); 
      return;
    }
    if( nosePosY >= 0 ) {
      activeEdge[0] = getEdgeNo( nosePosY-1 );
      activeEdge[1] = getEdgeNo( nosePosY+1 ); 
      return;
    }
    return;
  }//GETACTIVEEDGES



  //this will always return first the edge closer to x=0 and then the one further away
  public void getActiveEdges( boolean isBeforeBreak) {
      //System.out.println("IN GETACTIVEEDGES(isBeforeBreak)" );
    if( ltBreak >= 0) {
      if( isBeforeBreak ) {
	activeEdge[0] = getEdgeNo( ltBreak-1 );
	activeEdge[1] = getEdgeNo( ltBreak );
      }
      else {
	activeEdge[0] = getEdgeNo( ltBreak+1 );
	activeEdge[1] = getEdgeNo( ltBreak );
      }
      return;
    }
    if( rtBreak >= 0) {
      if( isBeforeBreak ) {
	activeEdge[0] = getEdgeNo( rtBreak );
	activeEdge[1] = getEdgeNo( rtBreak+1 );
      }
      else {
	activeEdge[0] = getEdgeNo( rtBreak );
	activeEdge[1] = getEdgeNo( rtBreak-1 );
      }
      return;
    }
    return;
  }//GETACTIVEEDGES



  //returns edge number between 0 and 2, irrespective of "int someNo"
  public int getEdgeNo( int someNo ){
    if( someNo >= 3 ) return( getEdgeNo(someNo-3) );
    if( someNo < 0 ) return( getEdgeNo(someNo+3) );
    return someNo;
  }

   
  public int getVertexNo(int someNo ){
    if( someNo >= 3 ) return( getVertexNo(someNo-3) );
    if( someNo < 0 ) return( getVertexNo(someNo+3) );
    return someNo;
  }

  /* Accounts for vertex nos not 0,1,2 */
  public Vertex3D getVtx3D(int someNo ){
    return v[getVertexNo(someNo)];
  }
  
  //*********************************************************************//

  public void draw( Raster r) {
    //System.out.println("IN TRIANGLE3D.DRAW" );
    /** TRIANGLE HAS TO BE SET UP BEFORE ANY MAJOR FUNCTIONS LIKE DRAWING, CLIPPING */
    this.triangleSetup( );  

    if( noseNegY >= 0){
      getActiveEdges();
      drawSpan( activeEdge, v[ getVertexNo(noseNegY) ], v[ getVertexNo(noseNegY-1) ], r );
    }
    if( nosePosY >= 0){
      getActiveEdges(); 
      drawSpan( activeEdge, v[ getVertexNo(nosePosY+1) ], v[ getVertexNo(nosePosY) ], r ); 
    }
    if( ltBreak >= 0){
      getActiveEdges(true);
      drawSpan( activeEdge, v[ getVertexNo(ltBreak+1) ], v[ getVertexNo(ltBreak) ], r );
      getActiveEdges(false);
      drawSpan( activeEdge, v[ getVertexNo(ltBreak) ], v[ getVertexNo(ltBreak-1) ], r );
    }
    if( rtBreak >= 0){
      getActiveEdges(true);
      drawSpan( activeEdge, v[ getVertexNo(rtBreak-1) ], v[ getVertexNo(rtBreak) ], r );
      getActiveEdges(false);
      drawSpan( activeEdge, v[ getVertexNo(rtBreak) ], v[ getVertexNo(rtBreak+1) ], r );
    }
  }//DRAW





    // interface methods 
    public  void drawLine( Raster r ){;}
    public  void drawLine( Raster r, float[] alpha, float[] red,  float[] green,  float[] blue) {;}


  protected void drawSpan( int[] aE, Vertex3D vStart, Vertex3D vFinish, Raster r) {
      //System.out.println("IN TRIANGLE.DRAWSPAN......" );
    int h = r.getHeight(); 
    int w = r.getWidth();
    int pix;
    int aE0 = aE[0];    int aE1 = aE[1]; 
    //System.out.println( " aE0=" + aE0 +" aE1=" + aE1 ); 
    int x, y;
    y = (int)(vStart.getCoord(1)+0.5);
    int yFinish = (int)(vFinish.getCoord(1)+0.5);
    //System.out.println( " yStart=" + y +" yFinish=" + yFinish ); 

    while( y <= yFinish ) {
      int xFloor = (int)( -( (edge[aE0].getC()+(edge[aE0].getB() *(float)y)  )/ (edge[aE0].getA()) ) + 0.5 );
      int xCeil = (int)( -( (edge[aE1].getC()+(edge[aE1].getB() *(float)y)  )/ (edge[aE1].getA()) ) );
      //System.out.println( " y=" + y +"*************" ); 
      //System.out.println( "xFloor =" + xFloor +"xCeil =" + xCeil ); 
      for( x=xFloor; x<=xCeil; x++ ) {
	  //System.out.println( " x=" + x );
	pix = flatClr; //CHANGE THIS AFTER INTERPOLATION
// 	System.out.println( " pix=" + pix +"*************" ); 
	if (x >= 0 && y >= 0 && x < w && y < h){
	  r.setPixel( pix, x, y);
	}
      }
      y++;
    }
  }//DRAWSPAN


  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
  public void draw( Raster r, Buffer buf) {
    this.triangleSetup();
    if( noseNegY >= 0){
      getActiveEdges();
      drawSpan( activeEdge, v[ getVertexNo(noseNegY) ], v[ getVertexNo(noseNegY-1) ], r, buf );
    }
    if( nosePosY >= 0){
      getActiveEdges(); 
      drawSpan( activeEdge, v[ getVertexNo(nosePosY+1) ], v[ getVertexNo(nosePosY) ], r, buf ); 
    }
    if( ltBreak >= 0){
      getActiveEdges(true);
      drawSpan( activeEdge, v[ getVertexNo(ltBreak+1) ], v[ getVertexNo(ltBreak) ], r, buf );
      getActiveEdges(false);
      drawSpan( activeEdge, v[ getVertexNo(ltBreak) ], v[ getVertexNo(ltBreak-1) ], r, buf );
    }
    if( rtBreak >= 0){
      getActiveEdges(true);
      drawSpan( activeEdge, v[ getVertexNo(rtBreak-1) ], v[ getVertexNo(rtBreak) ], r, buf );
      getActiveEdges(false);
      drawSpan( activeEdge, v[ getVertexNo(rtBreak) ], v[ getVertexNo(rtBreak+1) ], r, buf );
    }

  }//DRAW


 protected void drawSpan( int[] aE, Vertex3D vStart, Vertex3D vFinish, Raster r, Buffer buf) {
   //    System.out.println("IN INTERPOLATEDTRIANGLE.DRAWSPAN......" );
    int pix;
    float z;
    int h = r.getHeight(); 
    int w = r.getWidth();
//     System.out.println( " w=" + w +" h=" + h ); 

    int aE0 = aE[0];    int aE1 = aE[1]; 
 //    System.out.println( " ae0=" + aE0 +" aE1=" + aE1 );
    int x, y;
    y = (int)(vStart.getCoord(1)+0.5);
    int yFinish = (int)(vFinish.getCoord(1)+0.5);
//     System.out.println( " yStart=" + y +" yFinish=" + yFinish ); 

    while( y <= yFinish ) {
      int xFloor = (int)( -( (edge[aE0].getC()+(edge[aE0].getB() *(float)y)  )/ (edge[aE0].getA()) ) + 0.5 );
      int xCeil = (int)( -( (edge[aE1].getC()+(edge[aE1].getB() *(float)y)  )/ (edge[aE1].getA()) ) );
      //System.out.println( " y=" + y +"*************" ); 
      //System.out.println( "xFloor =" + xFloor +"xCeil =" + xCeil ); 
      for( x=xFloor; x<=xCeil; x++ ) {
	//System.out.println( "x =" + x  ); 
	z = this.getZBuf(x, y);
	if (x >= 0 && y >= 0 && x < w && y < h){
	    if( buf.getZBuf(x, y) >= z ) {
		pix = flatClr;
		//System.out.println( " x=" + x +" y=" + y +  " pix=" + pix   );
		r.setPixel( pix, x, y);
		buf.setZBuf( z, x, y );
	    }
	}
      }
      y++;
    }
  }//DRAWSPAN


  protected float getZBuf( int x, int y) {
    return (zEqn[0]*x + zEqn[1]*y + zEqn[2] );
  } 
  
  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//


  protected void planeEqn(float eqn[], int p0, int p1, int p2){
      //System.out.println("IN PLANE EQN" );
    float Ap, Bp, Cp;
    if (scale <= 0) {
      scale = (double)1. / ((double) twiceArea);
    }
    double sp0 = scale * p0;
    double sp1 = scale * p1;
    double sp2 = scale * p2;

    Ap = (float)(edge[2].getA()*sp2 + edge[0].getA()*sp0 + edge[1].getA()*sp1);
    Bp = (float)(edge[2].getB()*sp2 + edge[0].getB()*sp0 + edge[1].getB()*sp1);
    Cp = (float)(edge[2].getC()*sp2 + edge[0].getC()*sp0 + edge[1].getC()*sp1);
    eqn[0] = Ap;
    eqn[1] = Bp;
    eqn[2] =  Cp;
    return;
  }//PLANEEQN
  protected void planeEqn(float eqn[], float p0, float p1, float p2){
      //System.out.println("IN PLANE EQN" );
    float Ap, Bp, Cp;
    if (scale <= 0) {
      scale = (double)1. / ((double) twiceArea);
    }
    double sp0 = scale * p0;
    double sp1 = scale * p1;
    double sp2 = scale * p2;

    Ap = (float)(edge[2].getA()*sp2 + edge[0].getA()*sp0 + edge[1].getA()*sp1);
    Bp = (float)(edge[2].getB()*sp2 + edge[0].getB()*sp0 + edge[1].getB()*sp1);
    Cp = (float)(edge[2].getC()*sp2 + edge[0].getC()*sp0 + edge[1].getC()*sp1);
    eqn[0] = Ap;
    eqn[1] = Bp;
    eqn[2] =  Cp;
    return;
  }//PLANEEQN

    

  //*********************************************************************/

  public void getNormal( Vector3D normal ) {
      Vector3D edge2 = new Vector3D(( this.v[1].getCoord(0) - this.v[0].getCoord(0) ),
				    ( this.v[1].getCoord(1) - this.v[0].getCoord(1) ),
				    ( this.v[1].getCoord(2) - this.v[0].getCoord(2) )
				    );
      Vector3D edge0 = new Vector3D(( this.v[2].getCoord(0) - this.v[1].getCoord(0) ),
				    ( this.v[2].getCoord(1) - this.v[1].getCoord(1) ),
				    ( this.v[2].getCoord(2) - this.v[1].getCoord(2) )
				    );
      edge2.crossProd( edge0, normal );
      normal.normalize();
  }	
    
  public void setSurface( Surface surf ) {
    surface = surf; //THIS IS A POINTER
    hasSurface = true;
  }

  public Surface getSurface( ) {
    return surface; 
  }

  public void printSurface() {
      System.out.println(" PRINTING SURFACE" );    
      System.out.println(" r= " + surface.getClrFac(0)+ " g= " + surface.getClrFac(1) +  " b= " + surface.getClrFac(2) ); 
      System.out.println(" ka= " + surface.getKa() +" kd= " + surface.getKd() + " ks= " + surface.getKs()  +  " ns= " + surface.getNs() ); 
  }


  void illuminate( Light[] ltList, int nLights ) {
//     for( int i=0; i<nLights; i++ ) { ltList[i].print() ; }
    
    if( hasSurface ) {
      float clrFac[] = new float[3];
      for( int j=0; j<3; j++ ) {
	clrFac[j]= 0;
      }
      float ka = surface.getKa();    float   kd = surface.getKd();
      float ks = surface.getKs();    float   ns = surface.getNs();
      for( int j=0; j<3; j++ ) {
	for( int i=0; i<nLights; i++ ) {
	  if( ltList[i].getLtType() == Light.AMBIENT ){
	    clrFac[j] += ka*ltList[i].getIFac(j);
	  }
	  if( ltList[i].getLtType() == Light.DIRECTIONAL ){
	    Vector3D normToTri = new Vector3D();
	    this.getNormal( normToTri );
// 	    System.out.println("PRINTING NORMTOTRI"  );   
// 	    normToTri .print();
	    float cosTh = normToTri.dotProd(ltList[i].getDirection());
// 	    System.out.println("cosTh = " + cosTh ); 
	    if( cosTh>0){
	      clrFac[j] += kd*ltList[i].getIFac(j)*cosTh ;
	    }
	    //INCLUDE SPECULAR REFLECTION
	  }
	  //INCLUDE POINT SOURCE
	  if( ltList[i].getLtType() == Light.DIRECTIONAL ){
	    //INCLUDE DIFFUSE REFLECTION
	    //INCLUDE SPECULAR REFLECTION
	  }
	}// NLIGHTS
      }//THREE COMPONENTS RGB
      for( int j=0; j<3; j++ ) {
//          System.out.println("clrFac[j] = " + clrFac[j] ); 
	clrFac[j] *= surface.getClrFac(j);
//          System.out.println("clrFac[j] = " + clrFac[j] ); 
      }
      float clrFacOrder[] = new float[3];
      System.arraycopy(clrFac, 0, clrFacOrder, 0, clrFac.length);
      arrange( clrFacOrder, 3 );
//       for( int j=0; j<3; j++ ) {
//          System.out.println("clrFacOrder[j] = " + clrFacOrder[j] ); 
//       }      
      if( !isNotZero(clrFacOrder[2]) ) {
	  for( int j=0; j<3; j++ )   clrFac[j] /= clrFacOrder[2];
      }
//       for( int j=0; j<3; j++ ) {
//          System.out.println("clrFac[j] = " + clrFac[j] ); 
//       }
//        else 
// 	  if( isNotZero(clrFacOrder[1]) ) {
// 	      for( int j=0; j<3; j++ )   clrFac[j] /= clrFacOrder[1];
// 	  } else 
// 	      if( isNotZero(clrFacOrder[0]) ) {
// 		  for( int j=0; j<3; j++ )  clrFac[j] /= clrFacOrder[0];
// 	      }
    
//       scaleToRGBCube( clrFac,3 );
  
      flatClr= (255<<24) | ( (int)(clrFac[0]*255+0.5f)<<16  ) | ( (int)(clrFac[1]*255+0.5f)<<8 ) | ( (int)(clrFac[2]*255+0.5f) );
//          System.out.println(" flatClr= " + flatClr );    
  } else {
     System.err.println("ERROR:Triangle3D.illuminate: Surface has not been initialized  " );
    }
  }

    protected void scaleToRGBCube( float[] ary, int length){
	float fac = 0;
    	for( int i=0; i< length; i++) {
	    fac += ary[i]*ary[i];
	}   
	fac = (float) Math.sqrt(fac);
	if( fac>0) {
	    for( int i=0; i< length; i++) {
		ary[i] /=fac;
	    }	    
	} else 
	    System.err.println("ERROR:Triangle3D.scaleToRGBCube: fac not>0 ");
    }


    protected void scaleClrFac( float[] ary, int length){
	boolean repeat = false;
    	for( int i=0; i< length; i++) {
	    if(ary[i]>1) repeat=true;
	}   
	if(repeat){
	    for( int i=0; i< length; i++){
		if(ary[i]>1) ary[i] -= 1;
	    }
	    scaleClrFac( ary, length );
	} else return;
    }

    protected void arrange( float[] ary, int length){
	for( int i=1; i< length; i++) {
	    for( int j=0; j< i; j++) {
		if( ary[i]<ary[j]) {
		    float temp = ary[j];
		    ary[j]=ary[i];
		    ary[i]=temp;
		}
	    }
	}
    }


  public void setFlatColor( int flClr ) {
    flatClr = flClr;
  }

  public int getFlatColor( ) {
    return flatClr;
  }

  //***********************************************************************//

  public int clipTri3D( Triangle3D[] clipList, int clipTris, Plane3D[] H, int nPlanes, int CLIPFAC ) {
//     System.out.println("IN TRIANGLE3D.CLIPTRI3D" );
    /** TRIANGLE HAS TO BE SET UP BEFORE ANY MAJOR FUNCTIONS LIKE DRAWING, CLIPPING */
    //this.triangleSetup( );
    int nClipVtx = 3;
    Vertex3D[] inVtx = new Vertex3D[CLIPFAC]; 
    inVtx = this.v;
    Vertex3D[] clipVtxList = new Vertex3D[CLIPFAC]; 

    for( int i=0; i<nPlanes; i++ ) {
	if( nClipVtx > 0) { 
	    Vertex3D[] outVtx = new Vertex3D[CLIPFAC];
// 	    System.out.println("CLIPPING AGAINS PLANE = " + i );  
// 	    for( int j=0; j<nClipVtx; j++) {
// 		System.out.println("inVtx["+j+"]= " + j ); 	
// 		inVtx[j].print();
// 	    }
	    nClipVtx =  suthHodgPolyClip( inVtx,  outVtx, nClipVtx,  H[i] ) ;
// 	    for( int j=0; j<nClipVtx; j++) {
// 		System.out.println("outVtx["+j+"]= " + j ); 	
// 		outVtx[j].print();
// 	    }
	    inVtx = outVtx;
// 	    System.out.println("no of clipped vertices  = " + nClipVtx ); 
	    if( i==(nPlanes-1) ) clipVtxList = outVtx;
	}
	else break;
    }
    if( nClipVtx >= 3) {
	triangulate( clipList, clipTris, nClipVtx, clipVtxList  );
	return (nClipVtx-2);//NUMBE OF TRIANGLES CREATED
    } else{
	return 0;
    }
  }

  protected void triangulate(  Triangle3D[] outTri, int outStart, int nVtx, Vertex3D[] vtx ) {
//       System.out.println("IN TRIANGLE3D.TRIANGULATE" );
    //TRIANGLES CREATED = NVTX -2
    for(int i=0; i< (nVtx-2); i++ ) {
      outTri[outStart+i] = new Triangle3D( vtx[0], vtx[i+1], vtx[i+2] );
    }
    return;
  } 
  

  protected int suthHodgPolyClip( Vertex3D[] in,  Vertex3D[] out, int inLength, Plane3D H ) {
//       System.out.println("IN TRIANGLE3D.SUTHHODPOLYCLIP" );
//       for( int jj=0; jj<inLength; jj++) {
// 	  System.out.println("Printing in["+jj+"]" ); 	
// 	  in[jj].print();
//       }
    Vertex3D s, p;
    int outLength = 0;

    if( inLength>0) {
	s = in[ inLength-1];
	for( int j=0; j<inLength; j++ ) {
// 	    System.out.println(" ANALYZING LINE SEGMENT ="+j ); 
	    p = in[j];
	    // 	System.out.println("Printing vertex p" );
	    // 	p.print();
	    if( isInside(p, H) ) {
// 		System.out.println("p is inside H" );
		if( isInside(s, H) ) {
// 		    System.out.println("s is inside H" );
		    outLength = output( p, outLength, out );
		}
		else {
// 		    System.out.println("s is 0utside H" );
		    Vertex3D i = new Vertex3D();
		    intersect(s, p, H, i);
		    // 		System.out.println("Printing intersection vertex i" );
		    // 		i.print();
		    outLength = output( i, outLength, out );
		    outLength = output( p, outLength, out );
		}
	    }
	    else {
// 		System.out.println("p is outside H" );
		if( isInside(s, H) ) {
// 		    System.out.println("s is inside H" );
		    Vertex3D i = new Vertex3D();
		    intersect( s, p, H, i );
		    outLength = output( i, outLength, out );
		}
	    }
	    // 	for( int jj=0; jj<inLength; jj++) {
	    // 	    System.out.println("4-Printing in["+jj+"]" ); 	
	    // 	    in[jj].print();
	    // 	}
	    // 	for( int jj=0; jj<outLength; jj++) {
	    // 	    System.out.println("out["+jj+"]= " + jj ); 	
	    // 	    out[jj].print();
	    // 	}
	    s=p;
	    
	}//FOR J
    }
    return outLength;   
  } //SUTHHODGPOLYCLIP
    

  protected int output( Vertex3D v, int outLength, Vertex3D[] out ) {
    out[outLength] = new Vertex3D( v.getCoord(0), v.getCoord(1), v.getCoord(2), v.getCoord(3), v.getColor() );
    outLength++;
    return outLength;
  }
    

  /** THIS NEEDS TO BE POLISHED FOR CASES NOT GIVEN BY BELOW
       Assumes that H.v0 and H.v1 are in different halfplanes of H */
  protected void intersect( Vertex3D v0, Vertex3D v1, Plane3D H, Vertex3D res ) {
//       System.out.println(" IN INTERSECT"  );
    float normDist0 = H.dot(v0);
    float normDist1 = H.dot(v1);
    float fac = normDist0 /(normDist0 - normDist1 );
//     System.out.println("normDist0 " + normDist0 );
//     System.out.println("normDist " + normDist1 );
//     System.out.println("fac " + fac  );

    if(( (normDist0<0)&&(normDist1>0) )||( (normDist0>0)&&(normDist1<0) ) ) {
	res.setCoord(0, (v0.getCoord(0) + fac*( v1.getCoord(0) - v0.getCoord(0) ) ) );
	res.setCoord(1, (v0.getCoord(1) + fac*( v1.getCoord(1) - v0.getCoord(1) ) ) );
	res.setCoord(2, (v0.getCoord(2) + fac*( v1.getCoord(2) - v0.getCoord(2) ) ) );
	res.setCoord(3, 1 );
	//	System.out.println("res.getCoord0="+ res.getCoord(0)+"res.getCoord1="+ res.getCoord(1) );
	//	System.out.println("res.color=" + this.setColor(res.getCoord(0),res.getCoord(1))  );	
	// SINCE RES.W = 1, RES.X AND RES.Y CAN BE DIRECTLY INPUT TO THIS.SETCOLOR(X,Y)
	res.setColor( flatClr );
    } else {
      System.err.println("ERROR: Triangle3D.intersect(): line and plane do not intersect OR line is on plane" );
    }
  } //INTERSECT

  
    /** =0 DOESNOT MAKE SENSE FOR FLOATS */
    protected boolean isInside( Vertex3D v, Plane3D H ) {
    if((H.dot(v)>0)||(!isNotZero(H.dot(v)))) return true; else return false;
    }


    //***********************************************************************//

 
  protected boolean isNotZero( float f ) {
    if ( Math.abs(f) >= (1>>FRACBITS) ) return true; else return false;
  }
  protected boolean isPosZero( float f ) {
    if ( (f>0)&&( Math.abs(f) < (1>>FRACBITS)) ) return true;
    else return false;
  }
  protected boolean isNegZero( float f ) {
    if ( (f<0)&&( Math.abs(f) < (1>>FRACBITS)) ) return true;
    else return false;
  }

    public boolean isPointInside(float x, float y) {
	int flag = 0;
	for( int i=0; i<3; i++ ) {
	    if( edge[i].evaluateAt(x,y)>=0 ) flag+=1;
	}
	if( flag==3 ) return true; 
	else return false;
    }

  public void printVertices() {
     for( int i=0; i<3; i++ ) {
      System.out.println(" Vertex no.= " + i );   
      System.out.println(" x= " + v[i].getCoord(0)+ " y= " + v[i].getCoord(1) +  " z= " + v[i].getCoord(2) +  " w= " + v[i].getCoord(3)+  " color= " + v[i].getColor() );  
    }  
  }

  public void deHomogenize() {
    for( int i=0; i<3; i++ ) {
      if( isNotZero(v[i].getCoord(3)) ){
	for( int j=0; j<4; j++ ) {
	  v[i].setCoord( j, (v[i].getCoord(j) / v[i].getCoord(3)) );
	}
      }
      if( isPosZero(v[i].getCoord(3)) ){
	  System.err.println( "WARNING: Triangle3D.deHomogenize( ).isPosZero(v[i].getCoord(3) is accessed" );
	v[i].setCoord( 3, 0 );
      }
      if( isNegZero(v[i].getCoord(3)) ){
	  System.err.println( "WARNING: Triangle3D.deHomogenize( ).isNegZero(v[i].getCoord(3) is accessed" );
	for( int j=0; j<3; j++ ) {
	  v[i].setCoord( j, -v[i].getCoord(j) );
	}
	v[i].setCoord( 3, 0 );
      }
    }
  }


} // CLASS TRIANGLE3D
