/*********************************************************************************************/
/*                                                                                           */
/*  Java 3D Engine Basics Tutorials - Tut x.x                                                */
/*  Auth: bkenwright@xbdev.net                                                               */
/*                                                                                           */
/*                                                                                           */
/*********************************************************************************************/

import java.awt.image.*;
import java.awt.*;
import java.applet.*;


/*********************************************************************************************/
/*                                                                                           */
/*  class Vector3                                                                            */
/*  Its much more tidier in the long run, if we represent our x,y and z data values as       */
/*  a single class - then when we need to add or subract or even do a dot product operation  */
/*  on them we can just do a + b or a - b...                                                 */
/*                                                                                           */
/*********************************************************************************************/

class Vector3
{
   public float m_x, m_y, m_z;
   
   public Vector3(){}
   public Vector3(float x, float y, float z)
	 { m_x=x;  m_y=y;  m_z=z; };
   public Vector3(double x, double y, double z)
	 { m_x=(float)x;  m_y=(float)y;  m_z=(float)z; }
	 
	 
   static public Vector3 add(Vector3 a, Vector3 b)
   {
      Vector3 result = new Vector3();
      result.m_x = a.m_x + b.m_x;
      result.m_y = a.m_y + b.m_y;
      result.m_z = a.m_z + b.m_z;
      return result;
   }//End add(..)
   
   static public Vector3 subtract(Vector3 a, Vector3 b)
   {
      Vector3 result = new Vector3();
      result.m_x = a.m_x - b.m_x;
      result.m_y = a.m_y - b.m_y;
      result.m_z = a.m_z - b.m_z;
      return result;
   }//End add(..)
   
   static public Vector3 invert(Vector3 a)
   {
      Vector3 result = new Vector3();
      result.m_x = -a.m_x;
      result.m_y = -a.m_y;
      result.m_z = -a.m_z;
      return result;
   }// End invert(..)
   
   static public float dot(Vector3 a, Vector3 b)
   {
      float result = a.m_x*b.m_x  +  a.m_y*b.m_y  +  a.m_z*b.m_z;
      return result;
   }// End dot(..)
   
   static public Vector3 cross(Vector3 a, Vector3 b)
   {
      Vector3 result = new Vector3();
      result.m_x  =   a.m_y * b.m_z  -  a.m_z * b.m_y;
      result.m_y  =  -a.m_x * b.m_z  +  a.m_z * b.m_x;
      result.m_z  =   a.m_x * b.m_z  -  a.m_z * b.m_x;
      return result;
   }// End cross(..)
   
   static public Vector3 scale(Vector3 a, float f)
   {
      Vector3 result = new Vector3();
      result.m_x = a.m_x * f;
      result.m_y = a.m_y * f;
      result.m_z = a.m_z * f;
      return result;
   }// End scale(..)
   
   static public float length(Vector3 a)
   {
      float result = a.m_x*a.m_x + a.m_y*a.m_y + a.m_z*a.m_z;
      result = (float)Math.sqrt(result);
      return result;
   }// End length(..)
   
   static public Vector3 normalize(Vector3 a)
   {
      Vector3 result = new Vector3();
      float len = length(a);
      result.m_x =  a.m_x / len;
      result.m_y =  a.m_y / len;
      result.m_z =  a.m_z / len;
      return result;
   }// End normalize(..)
   
}//End Vector3

/*********************************************************************************************/
/*                                                                                           */
/*  class Triangle                                                                           */
/*  We need a way of representing our triangles...so that we can have a number of them       */
/*  and not have to deal with large numbers of arrays of values and things.  By putting      */
/*  them in a class each triangle is all nice and tidy.                                      */
/*                                                                                           */
/*********************************************************************************************/

class Triangle
{
   float x0, y0, z0;
   float x1, y1, z1;
   float x2, y2, z2;
   Color c;
   
   // Average z
   float avg_z;
   
   // Transformed coords
   float tx0, ty0, tz0;
   float tx1, ty1, tz1;
   float tx2, ty2, tz2;
   Color tc;
   
   // Triangle Normal
   float tnx, tny, tnz;
   
   // Cull our triangle if we can't see it
   boolean bCull;
   
   public Triangle(){} // default constructor
   public Triangle( float x0_, float y0_, float z0_,
                    float x1_, float y1_, float z1_,
                    float x2_, float y2_, float z2_,
                    Color c_  )
   {
       tx0=x0=x0_; ty0=y0=y0_; tz0=z0=z0_;
       tx1=x1=x1_; ty1=y1=y1_; tz1=z1=z1_;
       tx2=x2=x2_; ty2=y2=y2_; tz2=z2=z2_;
       tc = c = c_;
       bCull = false;
   }// End of Triangle(..) constructor
   
   public Triangle( Vector3 a, Vector3 b, Vector3 ccc, Color c_ )
   {
       tx0=x0=a.m_x; ty0=y0=a.m_y; tz0=z0=a.m_z;
       tx1=x1=b.m_x; ty1=y1=b.m_y; tz1=z1=b.m_z;
       tx2=x2=ccc.m_x; ty2=y2=ccc.m_y; tz2=z2=ccc.m_z;
       tc = c = c_;
   }// End of Translate(..) constructor
  
   public void Translate( float x, float y, float z )
   {
   			tx0 += x;  ty0 += y;  tz0 += z;
   			tx1 += x;  ty1 += y;  tz1 += z;
   			tx2 += x;  ty2 += y;  tz2 += z;
   }// End of Translate(..)
   
   public void ResetCoords()
   {
     tx0=x0; ty0=y0; tz0=z0;
     tx1=x1; ty1=y1; tz1=z1;
     tx2=x2; ty2=y2; tz2=z2;
     bCull = false;
     
   }// End ResetCoords()
   
   public void RotateX( float angle )
   {
      float cosA = (float)Math.cos(angle);
      float sinA = (float)Math.sin(angle);
      
      float temp_x, temp_y, temp_z;
      
      temp_x =  tx0;
      temp_y =  ty0*cosA - tz0*sinA;
      temp_z =  ty0*sinA + tz0*cosA;
      tx0 = temp_x;  ty0 = temp_y;  tz0 = temp_z;
      
      temp_x =  tx1;
      temp_y =  ty1*cosA - tz1*sinA;
      temp_z =  ty1*sinA + tz1*cosA;
      tx1 = temp_x;  ty1 = temp_y;  tz1 = temp_z;
      
      temp_x =  tx2;
      temp_y =  ty2*cosA - tz2*sinA;
      temp_z =  ty2*sinA + tz2*cosA;
      tx2 = temp_x;  ty2 = temp_y;  tz2 = temp_z;
      
   }// End RotateX(..)
   
  public void RotateY( float angle )
   {
      float cosA = (float)Math.cos(angle);
      float sinA = (float)Math.sin(angle);
      
      float temp_x, temp_y, temp_z;
      
      temp_x =   tx0*cosA + tz0*sinA;
      temp_y =   ty0;
      temp_z =  -tx0*sinA + tz0*cosA;
      tx0 = temp_x;  ty0 = temp_y;  tz0 = temp_z;
      
      temp_x =   tx1*cosA + tz1*sinA;
      temp_y =   ty1;
      temp_z =  -tx1*sinA + tz1*cosA;
      tx1 = temp_x;  ty1 = temp_y;  tz1 = temp_z;
      
      temp_x =   tx2*cosA + tz2*sinA;
      temp_y =   ty2;
      temp_z =  -tx2*sinA + tz2*cosA;
      tx2 = temp_x;  ty2 = temp_y;  tz2 = temp_z;
			 
   }// End RotateY(..)
   
   public void RotateZ( float angle )
   {
      float cosA = (float)Math.cos(angle);
      float sinA = (float)Math.sin(angle);
      
      float temp_x =  tx0*cosA - ty0*sinA;
      float temp_y =  tx0*sinA + ty0*cosA;
      float temp_z =  tz0;
      tx0 = temp_x;  ty0 = temp_y;  tz0 = temp_z;
      
      temp_x =  tx1*cosA - ty1*sinA;
      temp_y =  tx1*sinA + ty1*cosA;
      temp_z =  tz1;
      tx1 = temp_x;  ty1 = temp_y;  tz1 = temp_z;
      
      temp_x =  tx2*cosA - ty2*sinA;
      temp_y =  tx2*sinA + ty2*cosA;
      temp_z =  tz2;
      tx2 = temp_x;  ty2 = temp_y;  tz2 = temp_z;
   }// End RotateZ(..)
   
   public void CalculateNormal()
   {
      // First we need to find the direction of our triangle
      // using the cross product :)
      
      // Lets calculate the two direction vectors
      float vx0, vy0, vz0;
      float vx1, vy1, vz1;
      
      vx0 = tx2 - tx0;
      vy0 = ty2 - ty0;
      vz0 = tz2 - tz0;
    
      vx1 = tx1 - tx0;
      vy1 = ty1 - ty0;
      vz1 = tz1 - tz0;
      
      // Do the cross product
      float nx, ny, nz;
      nx =  vy0*vz1 - vz0*vy1;
      ny = -vx0*vz1 + vz0*vx1;
      nz =  vx0*vy1 - vy0*vx1;
      
      // And we normalize it so its magnitude is 1
      float length = nx*nx + ny*ny + nz*nz;
			length = (float)Math.sqrt(length);
			
			nx /= length;
			ny /= length;
			nz /= length;
      
			tnx = nx;
			tny = ny;
			tnz = nz;
   }// End CalculateNormal(..)
   
   
}// End of class triangle



/*********************************************************************************************/
/*                                                                                           */
/*  Program Entry Point                                                                      */
/*                                                                                           */
/*********************************************************************************************/

public class clipping extends Applet 
{
  Image myImage;
  Graphics offScreen;
  
  int mouse_x_left=0;
  int mouse_y_left=0;
  
  int mouse_x_right=0;
  int mouse_y_right=0;
  


  
  int m_NumTris = 12;
  Triangle[] m_tri;

  public void init() 
  {
  	 Dimension appletSize = this.getSize();
	   myImage = createImage(appletSize.width,appletSize.height);
   	 offScreen = myImage.getGraphics();
   	 
   	 m_tri = new Triangle[m_NumTris];
   	 // Front
   	 m_tri[0] = new Triangle(-1.0f, -1.0f, -1.0f,  -1.0f, 1.0f, -1.0f,  1.0f,  1.0f, -1.0f,  Color.red);
     m_tri[1] = new Triangle(-1.0f, -1.0f, -1.0f,   1.0f, 1.0f,-1.0f,   1.0f, -1.0f, -1.0f,  Color.red);
				
		// Back
   	m_tri[2] = new Triangle(-1.0f, -1.0f,  1.0f,  1.0f,  1.0f, 1.0f,  -1.0f, 1.0f,  1.0f,  Color.green);
    m_tri[3] = new Triangle(-1.0f, -1.0f,  1.0f,  1.0f, -1.0f, 1.0f,   1.0f, 1.0f,  1.0f,  Color.green);									 
   	 
   	// Bottom
   	m_tri[4] = new Triangle(-1.0f, -1.0f,  -1.0f,   1.0f, -1.0f, 1.0f,   -1.0f, -1.0f, 1.0f,   Color.blue);
    m_tri[5] = new Triangle(-1.0f, -1.0f,  -1.0f,   1.0f, -1.0f, -1.0f,   1.0f, -1.0f,  1.0f,  Color.blue);	
    
    // Top
    m_tri[6] = new Triangle(-1.0f,  1.0f,  -1.0f,  -1.0f, 1.0f, 1.0f,   1.0f,  1.0f, 1.0f,  Color.orange);
    m_tri[7] = new Triangle(-1.0f,  1.0f,  -1.0f,   1.0f, 1.0f,  1.0f,  1.0f, 1.0f, -1.0f,  Color.orange);
    
    // Left
    m_tri[8] = new Triangle(-1.0f, -1.0f,  -1.0f,   -1.0f,  1.0f, 1.0f,   -1.0f, 1.0f, -1.0f,  Color.yellow);
    m_tri[9] = new Triangle(-1.0f, -1.0f,  -1.0f,   -1.0f, -1.0f, 1.0f,   -1.0f, 1.0f,  1.0f,  Color.yellow);
    
    // Right
    m_tri[10] = new Triangle(1.0f, -1.0f,  -1.0f,   1.0f, 1.0f, -1.0f,   1.0f,  1.0f, 1.0f,  Color.pink);
    m_tri[11] = new Triangle(1.0f, -1.0f,  -1.0f,   1.0f, 1.0f,  1.0f,   1.0f, -1.0f, 1.0f,  Color.pink);
    
    
  }// End of init(..)

  
   // find the distance between a ray and a plane.
   public float distRayPlane(Vector3 vStart,
                             Vector3 vEnd,
                             Vector3 vnPlaneNormal,
                             Vector3 vPointOnPlane)
   {
      float cosAlpha;
      float deltaD;
      
      float planeD = Vector3.dot( vnPlaneNormal, vPointOnPlane );

      Vector3 vRayVector = Vector3.subtract(vEnd, vStart);
      Vector3 vnRayVector = Vector3.normalize(vRayVector);
      
      cosAlpha = Vector3.dot( vnPlaneNormal, vnRayVector );


      // parallel to the plane (alpha=90)
      if ( Math.abs(cosAlpha) < 0.001f ) return Vector3.length(vRayVector);

      deltaD = planeD - Vector3.dot(vStart, vnPlaneNormal);
    
      float l = (deltaD/cosAlpha);
      
      //float k = Vector3.dot( vnPlaneNormal, vPointOnPlane );
      //float a0 = Vector3.dot( vnPlaneNormal, vStart );
      //l = (k - a0)/cosAlpha;
      
      return l;
   }// End of distRayPlane(..)


   public Vector3 PointOnPlane( Vector3 vStart, Vector3 vEnd,
                      Vector3 vPlaneNormal, Vector3 vPointOnPlane )
	 {
        Vector3 vn = Vector3.subtract(vEnd,vStart);
        vn = Vector3.normalize(vn);
        float Length = distRayPlane(vStart,vEnd,vPlaneNormal,vPointOnPlane);
        Vector3 px = Vector3.scale( vn, Length );
        px = Vector3.add( vStart, px );
        
        return px;
   }// End of PointOnPlane(..)


  // Amazing function - it takes a triangle, and a plane, and slices our
  // triange..producing the 3 new triangles.  Returns 0 if it didn't cut
  // the triangle - 1 or 2 depending on how many times it cut the triangle
  // on the positive side of the triangle.  It uses an arry of 3 triangles
  // so we can also use the cut triangle(s) for something else incase we
  // use this function for somethign special :)
  // Returns 3 for all on the positve side of plane
  // Returns 0 for all on the negative side of plane
  // Returns 1 for 1 tri tnew[0] on positive side and tri[1] and tri[2] on neg side
  // Returns 2 for 2 tri tnew[0] & tnew[1] on pos side and of course tri[2] on neg
  int ClipTriangle( Triangle t,
                    Triangle tnew[],
                    Vector3 vPointOnPlane,
                    Vector3 vPlaneNormal )
  {
     // Checking - not necessary though
     vPlaneNormal = Vector3.normalize(vPlaneNormal);
     
     // Clipping
     Vector3 p0 = new Vector3( t.tx0, t.ty0, t.tz0 );
     Vector3 p1 = new Vector3( t.tx1, t.ty1, t.tz1 );
     Vector3 p2 = new Vector3( t.tx2, t.ty2, t.tz2 );
     
     Vector3 v1 = Vector3.subtract( p1, p0 );
     Vector3 v2 = Vector3.subtract( p2, p0 );
     
     //System.out.println("v1  x:" + v1.m_x + " y:" + v1.m_y + " z:" + v1.m_z );
     
     // Okay, lets see if our triangle actually cuts the plane
     float k = Vector3.dot( vPlaneNormal, vPointOnPlane );
     
     float a0 = Vector3.dot( vPlaneNormal, p0 );
     float a1 = Vector3.dot( vPlaneNormal, p1 );
     float a2 = Vector3.dot( vPlaneNormal, p2 );
     
     // Determine how many points are on the positive side of our plane
     int iCount = 0;
     boolean p0in = false;
     boolean p1in = false;
     boolean p2in = false;
     if( (k - a0)> 0 ){ iCount++; p0in=true; }
     if( (k - a1)> 0 ){ iCount++; p1in=true; }
     if( (k - a2)> 0 ){ iCount++; p2in=true; }
     
     //System.out.println("iCount = " + iCount );
     
     // If our triangle is fully on one side, or fully on the other side
     if( iCount==0) return 3;  // All on the positive side on plane
		 if( iCount==3) return 0;  // all on the negative side of plane
     
     
     // These two vectors will hold the two points on the plane that
     // actually cut the triangle.
     // For example if we go from PointA to PointB on our triangle,
     // and it goes through the plane, the point where A->B cuts the
     // plane is called px0 for example.
     Vector3 px0 = new Vector3();
     Vector3 px1 = new Vector3();
     
     
     // Now we have the two points on the plane that make
     // up the intersection points, that we can construct
     // the three new triangles from.
     if(iCount==1 )
     {
         Vector3 pA = new Vector3();
         Vector3 pB = new Vector3();
         Vector3 pC = new Vector3();
				
         if( p0in ){ pA=p0; pB=p1; pC=p2; };
         if( p1in ){ pA=p1; pB=p0; pC=p2; };
         if( p2in ){ pA=p2; pB=p1; pC=p0; };
				
         px0 = PointOnPlane( pA /*vStart*/, pB /*vEnd*/,
         vPlaneNormal, vPointOnPlane );
     
         px1 = PointOnPlane( pA /*vStart*/, pC /*vEnd*/,
         vPlaneNormal, vPointOnPlane );
                      
         Triangle t1 = new Triangle( pA, px0, px1, t.tc );
         tnew[0] = t1;
				   
         Triangle t2 = new Triangle( pB, px1, pC, t.tc );
         Triangle t3 = new Triangle( pB, px0, px1, t.tc );
         tnew[1] = t2;
         tnew[2] = t3;

         /*
         System.out.println("p0in: " + p0in + " p1in: " +  p1in + " p2in:" + p2in );
         System.out.println("p0  x:" + p0.m_x + " y:" + p0.m_y + " z:" + p0.m_z );
         System.out.println("p1  x:" + p1.m_x + " y:" + p1.m_y + " z:" + p1.m_z );
         System.out.println("p2  x:" + p2.m_x + " y:" + p2.m_y + " z:" + p2.m_z );
				
         Triangle ta = tnew[0];
         System.out.println("newp0  x:" + ta.x0 + " y:" + ta.y0 + " z:" + ta.z0 );
         System.out.println("newp1  x:" + ta.x1 + " y:" + ta.y1 + " z:" + ta.z1 );
         System.out.println("newp2  x:" + ta.x2 + " y:" + ta.y2 + " z:" + ta.z2 );
         */
				
         return 1;		
     }// End if(iCount==1)
     
     // We are here!..so two points are inside our plane
     if(iCount==2)
     {
				
        Vector3 pA = new Vector3();
        Vector3 pB = new Vector3();
        Vector3 pC = new Vector3();
				
        //System.out.println("p0in: " + p0in + " p1in: " +  p1in + " p2in:" + p2in );
				
        if( !p0in ){ pA=p1; pB=p2; pC=p0; };
        if( !p1in ){ pA=p0; pB=p2; pC=p1; };
        if( !p2in ){ pA=p0; pB=p1; pC=p2; };
				
				
        px0 = PointOnPlane( pB /*vStart*/, pC /*vEnd*/,
                            vPlaneNormal, vPointOnPlane );
     
        px1 = PointOnPlane( pA /*vStart*/, pC /*vEnd*/,
                            vPlaneNormal, vPointOnPlane );
        
        Triangle t1 = new Triangle( pC, px0, px1, t.tc );  // red
        tnew[2] = t1;
				   
        Triangle t2 = new Triangle( pB, pA, px0, t.tc );  // dark red
        Triangle t3 = new Triangle( pA, px0, px1, t.tc ); // green
        tnew[0] = t2;
        tnew[1] = t3;
              
        return 2;		
     }// End if(iCount==1)
   
     
     return 3;
  }// End ClipTriangle(..)
  


  boolean odd = false;

  public void RenderTriangle(Triangle t)
  {
     // Add this line if you want only a single side of the square (the red one)
     // to be rendered - for test purposes
     //if( t.tc != Color.red ) return;
     // Add these lines, if you only want a single one of the trianges rendered
     //odd = !odd;
     //if( odd ) return;
     
     
     Triangle[] tnew = new Triangle[3];
     tnew[0] = new Triangle();
     tnew[1] = new Triangle();
     tnew[2] = new Triangle();
     
     
     Vector3 vPointOnPlane = new Vector3( 0, 0, 1 );
     Vector3 vPlaneDirection = new Vector3( 1, 0, 0 );
     int clip = ClipTriangle( t,
                              tnew,
                              vPointOnPlane,
                              vPlaneDirection );
     // These many lines are for colour - so we can shade triangles on one side
     // of the plane differently than the one's on the other.
     Color cOrig = t.tc;
     int red = cOrig.getRed();
     int green = cOrig.getGreen();
     int blue = cOrig.getBlue();
     double lo = 0.7;
     double hi = 0.8;
     int redlo   = (int)(red*lo);
     int greenlo = (int)(green*lo);
     int bluelo  = (int)(blue*lo);
     int redhi   = (int)(red*hi);
     int greenhi = (int)(green*hi);
     int bluehi  = (int)(blue*hi);
     
     Color cOrigLo = new Color(redlo, greenlo, bluelo);
     Color cOrigHi = new Color(redhi, greenhi, bluehi);
     
     
     if( clip == 3 ) // All on the positive side of the plane
		 {								            
        ScreenPerspective( myImage,
	                         t.tx0, t.ty0, t.tz0,
	                         t.tx1, t.ty1, t.tz1,
	                         t.tx2, t.ty2, t.tz2,
	                         cOrigLo);
	    }// End if(..)
	    else if(clip==1) // One triangle is on the positive side of plane
	    {
	       tnew[0].tc = cOrig;
         tnew[1].tc = cOrigHi;
         tnew[2].tc = cOrigLo;
	       for(int i=0; i<3; i++)
	       {
	          ScreenPerspective( myImage,
	                             tnew[i].tx0, tnew[i].ty0, tnew[i].tz0,
	                             tnew[i].tx1, tnew[i].ty1, tnew[i].tz1,
	                             tnew[i].tx2, tnew[i].ty2, tnew[i].tz2,
	                             tnew[i].tc);
	        }// End for loop
	    }// End else if(...)
	    else if(clip==2) // Two triangles is on the positive side of plane
	    {
	       tnew[0].tc = cOrig;
         tnew[1].tc = cOrig;
         tnew[2].tc = cOrigLo;
         
	       for(int i=0; i<3; i++)
	       {
	          ScreenPerspective( myImage,
	                             tnew[i].tx0, tnew[i].ty0, tnew[i].tz0,
	                             tnew[i].tx1, tnew[i].ty1, tnew[i].tz1,
	                             tnew[i].tx2, tnew[i].ty2, tnew[i].tz2,
	                             tnew[i].tc);
	        }// End for loop
	    }// End else if(...)
	    else // All on the negative side of the plane
	    {
	       ScreenPerspective( myImage,
	                          t.tx0, t.ty0, t.tz0,
	                          t.tx1, t.ty1, t.tz1,
	                          t.tx2, t.ty2, t.tz2,
	                          t.tc);
	    }// End else(..)
	    
  }// End of RenderTriangle(..)
  
  void SortRenderOrder(Triangle t[], int iNumTris)
  {
     // First lets generate an average z for each triangle
     float average_z;
     for( int i=0; i<iNumTris; i++ )
     {
        average_z = t[i].tz0 + t[i].tz1 + t[i].tz2;
        average_z /= 3.0f;
        t[i].avg_z = average_z;
     }// End for loop
     
     // Now lets do a bruit force sort, so each triangle is in order
     // of our average z
     for( int outer=0; outer<iNumTris; outer++ )
     {
        for( int inner=0; inner<iNumTris; inner++ )
        {
            if( t[outer].avg_z > t[inner].avg_z )
            {
               Triangle temp = t[outer];
               t[outer] = t[inner];
               t[inner] = temp;
            }// End if(..)
        }// End inner for loop
     }// End outer for loop
     
  }// SortRenderOrder(..)

  
  void RenderAllTriangles(Triangle t[], int iNumTris)
  {   
     for(int i=0; i<iNumTris; i++)
     {
        if(t[i].bCull == false )
           RenderTriangle( t[i] );
     }// End for loop
  }// End of RenderAllTriangles(..)
  
  float DotProduct(float x0, float y0, float z0,
	                 float x1, float y1, float z1 )
  {
    return( x0*x1 + y0*y1 + z0*z1 );
  }// End of DotProduct(..)
  
  void CullTriangles( Triangle t[], int iNumTris )
  {
     // Our default camera is at 0,0,0 and faces in the positive z
     // direction
     
     for(int i=0; i<iNumTris; i++)
     {
        float cosA = DotProduct( t[i].tnx, t[i].tny, t[i].tnz,
                                 0.0f,     0.0f,     1.0f );

        // Well instead of checking for equal or less than 0, due to errors
        // with number rounding and things...we need to check for
        // less than 0.32 :)
				                              
        if( cosA < 0.32f )
           t[i].bCull = true;
        else
           t[i].bCull = false;
     }//End for loop
     
     
  }// End CullTriangles(..)
  
  void CalculateNormals(Triangle t[], int iNumTris)
  {
     for( int i=0; i<iNumTris; i++ )
     {
        t[i].CalculateNormal();
     }// End for loop
  }// End of CalculateNormals(..)
  
  void ResetAllCoords(Triangle t[], int iNumTris)
  {
  	 for(int i=0; i<iNumTris; i++)
     {
        t[i].ResetCoords();  
     }// End for loop
  }// End of ResetAllCoords(..)
  
  float m_AngleX = 0.5f;
  float m_AngleY = 0.5f;
  float m_AngleZ = 0.5f;
  
  public void paint(Graphics g) 
  {
      // Clear screen
		  offScreen.setColor(Color.white);
		  offScreen.fillRect(0,0,this.getSize().width,this.getSize().height);
			
			Dimension appletSize = this.getSize();
		  int width  = appletSize.width;
		  int height = appletSize.height;

		      
      ResetAllCoords( m_tri, m_NumTris );
      
      // Lets rotate all of the triangles and translate them back into the horizon
      for(int i=0; i<m_NumTris; i++)
      {
		      m_tri[i].RotateY(m_AngleY);
		      m_tri[i].RotateZ(m_AngleZ);
		      m_tri[i].RotateX(m_AngleX);
		      m_tri[i].Translate(0.0f, 0.0f, 3.0f);
		  }
		  //m_AngleY += 0.05f;
		  //m_AngleZ += 0.03f;
		  m_AngleX += 0.09f;

      CalculateNormals( m_tri, m_NumTris );
      CullTriangles( m_tri, m_NumTris );
      SortRenderOrder( m_tri, m_NumTris );
			RenderAllTriangles( m_tri, m_NumTris );


      drawLine(myImage, width/2, 10 , width/2, height-10, new Color(0x0,0x0,0x0));

		             
  	 //offScreen.drawLine( mouse_x_left+10,  mouse_y_left+10,
		 //            mouse_x_right+10, mouse_y_right+10);
  	 
	   g.drawImage(myImage,0,0,this);
  }// End of paint(..)


  // So we dont get the screen getting cleared all the time
  public void update(Graphics g){ paint(g); }
  
  public void ScreenPerspective( Image image,
	                               float x0, float y0, float z0,
	                               float x1, float y1, float z1,
	                               float x2, float y2, float z2,
	                               Color c )
  {
			    // This is where we'll put our final values that we calculate
			    // we only have an x and a y, as we'll convert our x,y and z into
			    // a screen coordinate square which has all the sizing and things
			    // done to it already

		
		      // Get the screen size of our applet
		      Dimension appletSize = this.getSize();
		      int width  = appletSize.width;
		      int height = appletSize.height;
		
		      
		      //if( (z0<1) && (z1<1) && (z2<1) )
		      //   return;
		         
		      //if( z0 < 1 ) z0 = 1;
		      //if( z1 < 1 ) z1 = 1;
		      //if( z2 < 1 ) z2 = 1;
		      
		         
		      // Something worth noting - we have a pole at z=0...as when we
		      // create a perspective ..which is x_per = d*x/z for example, if
		      // z is 0 or very close to zero, we'll get an infinit number...so
		      // if we have a z value less than 1 we'll just round z to 1 and it
		      // will get clipped later down the line.
		      
		      // Whats this pTan stuff for?...well we can't just clip the z value
		      // to 1...as what if its on a really steep angle...so what we do, is
		      // we work out a new value for x and z and then clip it to the near
		      // clip plane :)
		      
		      // Add some clipping code so Z is clipped to the near view plane
		      // ++ //
		
					// From x,y,z to x,y for the screen.
					// -1-Perspective Conversion

		      // Focul point of 1 (Normalised view plane 90 degrees)
					float d = 1.0f; 
					float Perspective_x0 = -d*x0 / z0;
					float Perspective_x1 = -d*x1 / z1;
					float Perspective_x2 = -d*x2 / z2;
					
					// its between -1 to 1...so we need to scale it to 0 to 1...and scale to
					// the size of the screen.
					Perspective_x0 += 1;
					Perspective_x1 += 1;
					Perspective_x2 += 1; 
					// So now its between 0 and 2 and not -1 to 1;
					Perspective_x0 *= 0.5;
					Perspective_x1 *= 0.5;
					Perspective_x2 *= 0.5;
					// -2- Now we scale it to the size of the screen
					float Screen_x0 = (width-1)*Perspective_x0;
					float Screen_x1 = (width-1)*Perspective_x1;
					float Screen_x2 = (width-1)*Perspective_x2;
					
					// Add some clipping to make sure its on the screen here
					// ++ //
		              
					// Now we do the same for the y value to convert it
		      // to screen coordinates … its exactly the same as for the x above,
		      // but I've combined all the stages into a single line for each
		      // one

					// Note y1 is the top of square, and y2 is the bottom of our square
					
					float Screen_y0 = ((-d*y0 / z0)+1)*0.5f *(height-1);
					float Screen_y1 = ((-d*y1 / z1)+1)*0.5f *(height-1);
					float Screen_y2 = ((-d*y2 / z2)+1)*0.5f *(height-1);

          // Debug information
          /*
          System.out.println( "Color: " + c );
					System.out.println( "x0: " + Screen_x0 + "  y0: " + Screen_y0 );
					System.out.println( "x1: " + Screen_x1 + "  y1: " + Screen_y1 );
					System.out.println( "x2: " + Screen_x2 + "  y2: " + Screen_y2 );
					System.out.println("");
					*/

					// Render our data
			    drawTriangle( image,
			                  Screen_x0, Screen_y0,
			                  Screen_x1, Screen_y1,
			                  Screen_x2, Screen_y2,
			                  c );            

  }// End ScreenPerspective(...)


	public void drawTriangle( Image image,
	                          float x0, float y0,
	                          float x1, float y1,
	                          float x2, float y2,
	                          Color c )
	{
	   //First we sort our points out into y order...where it goes
	   // 0
	   // 2
	   // 1
	   float t; // temp variable
	   if( y1 < y0 )
	   {
	      t  = y0;
	      y0 = y1;
	      y1 = t;
	      
	      t  = x0;
	      x0 = x1;
	      x1 = t;
	   }
		 
		 if( y2 < y0 )
	   {
	      t  = y0;
	      y0 = y2;
	      y2 = t;
	      
	      t  = x0;
	      x0 = x2;
	      x2 = t;
	   }  
		 
		if( y1 < y2 )
	   {
	      t  = y1;
	      y1 = y2;
	      y2 = t;
	      
	      t  = x1;
	      x1 = x2;
	      x2 = t;
	   }     
	   
	   // Next part...
	   // Render top of triangle
	   float x_left  = x0;
	   float x_right = x0;
	   
	   float dy_top    = y2 - y0;
	   float dy_bottom = y1 - y2;
	   
	   float dx_left  = x2 - x0;   // to the middle then we change it
	   float dx_right = x1 - x0;   // all the way to the bottom :)
	   
	   //int dx_left_dir  = dx_left  < 0 ? -1 : 1;
	   //int dx_right_dir = dx_right < 0 ? -1 : 1;
	   
	   //dx_left  *= dx_left_dir;
	   //dx_right *= dx_right_dir;
	   
	   //dx_left  = dx_left  / dy_top;
	   //dx_right = dx_right / dy_top;
	   
		 dx_left  = (x2 - x0) / (y2 - y0);
	   dx_right = (x1 - x0) / (y1 - y0);
	   
	   int s = 0;
	   if( dx_left > dx_right )
	   {
	      t = dx_left;
	      dx_left = dx_right;
	      dx_right = t;
	      s=1;
	   }//End if
	   
	   for(float y=y0; y<y2; y++)
	   {
	        for(float x=x_left; x<x_right; x++)
	        {
	   			   setPixel(image, (int)x, (int)y, c );
		  		}// End inner for loop
		  		x_left  += dx_left;
		  		x_right += dx_right;
	   
	   }// End outer for loop
	
	   // **IMPORTANT UPDATE **
	   // Its important to check that the top of the triangle exists...as its af flat top
	   // then we have to make sure our bottom is okay :)  Not the added lines of code
	   // so its not x0 for the starting point.
     
	   // Now for the bottom of the triangle
		 if( s == 0 )
	   {
	      dx_left = (x1 - x2) / (y1 - y2);
	      x_left = x2;
	   }
	   else
	   {
	      dx_right = (x1 - x2) / (y1 - y2);
	      x_right = x2;
	   }
	
	   
	   for(float y=y2; y<y1; y++)
	   {
	      for(float x=x_left; x<x_right; x++)
	      {
	         setPixel(image, (int)x, (int)y, c);
	      }
	      x_left  += dx_left;
	      x_right += dx_right;
	   }// End outer for loop
		
	
	}//End of drawTriangle(..)

  public void drawLine(Image image, int x0, int y0, int x1, int y1, Color c)
  {
  		float sx = (float)x0;
  		float sy = (float)y0;
  		float ex = (float)x1;
  		float ey = (float)y1;
  		
  		float dx = ex-sx;
  		float dy = ey-sy;	
  		
  		int xdir = dx > 0 ? 1 : -1;
  		int ydir = dy > 0 ? 1 : -1;
  		
  		dx = dx*xdir;
  		dy = dy*ydir;
  		
  		
  		float incx =  dx / dy;
  		float incy =  dy / dx;
  		
  		incx *= xdir;
  		incy *= ydir;
  		
  		if( dy > dx )
  		{
		  		for( int y=0; y<dy; y++)
		  		{
		  				setPixel(image, (int)sx, (int)sy, c );
		  				sx += incx;
		  				sy += ydir;

		  		}// End for loop(..)
  		}
  		else
  		{
  		    for( int x=0; x<dx; x++)
		  		{
		  				setPixel(image, (int)sx, (int)sy, c );
		  				sy += incy;
		  				sx += xdir;

		  		}// End for loop(..)
  		}
  		
  }//End of drawline(..)
  
  public void setPixel(Image image, int x, int y, Color color ) 
  {
     Graphics g = image.getGraphics( );
     g.setColor( color );
     g.fillRect( x, y, 1, 1 );
     g.dispose( );
  }// End of setPixel(..)


/*********************************************************************************************/
/*                                                                                           */
/*  Capture event when mouse is pressed                                                      */
/*    Parameters:                                                                            */
/*    evt is The event...                                                                    */
/*    x is the x mouse position                                                              */
/*    y is the y mouse position                                                              */
/*    return whether or not we handled the event                                             */
/*                                                                                           */
/*********************************************************************************************/

	public boolean mouseDown(Event evt, int x, int y)
  {
  	  if(evt.id == Event.MOUSE_DOWN)
      {
          // Right mouse button
      		if( evt.metaDown() == true )
      		{
      				System.out.println( "Right MouseDown" );
      				mouse_x_right = x;
     					mouse_y_right = y;
     					
     					//m_x0 = x;
     					//m_y0 = y;
      		}
      		else // right mouse button
      		{
      				System.out.println( "Left MouseDown" );
      				mouse_x_left = x;
     					mouse_y_left = y;
     					
     					//m_x1 = x;
     					//m_y1 = y;
      		}
      }// End if
    
		 repaint();
		 return true;
	}// End of mouseDown(..)

/*********************************************************************************************/
/*                                                                                           */
/*  Capture mouse release                                                                    */
/*    Parameters:                                                                            */
/*    evt is The event...                                                                    */
/*    x is the x mouse position                                                              */
/*    y is the y mouse position                                                              */
/*    return whether or not we handled the event                                             */
/*                                                                                           */
/*********************************************************************************************/

	public boolean mouseUp(Event evt, int x, int y)
	{

		return false;
	}// End of mouseUp(..)
	
/*********************************************************************************************/
/*                                                                                           */
/*  Capture mouse drags                                                                      */
/*    Parameters:                                                                            */
/*    evt is The event...                                                                    */
/*    x is the x mouse position                                                              */
/*    y is the y mouse position                                                              */
/*    return whether or not we handled the event                                             */
/*                                                                                           */
/*********************************************************************************************/

	public boolean mouseDrag(Event evt, int x, int y)
	{
  	return true;
	}// End of mouseDrag(..)


}// End of our Applet
