/*======================================================================*/
/* 3DLib								*/
/*									*/
/* Face-related functions						*/
/*									*/
/* AUTHOR:	Gabor Nagy						*/
/* DATE:	1996-Sep-27 23:02:54					*/
/*									*/
/* 3DLib(TM) Copyright (C) 1995 by Gabor Nagy. All rights reserved.	*/
/*======================================================================*/
#include <math.h>
#include <float.h>
#include <stdio.h>

#include <EMalloc.h>
#include <EStrings.h>

#ifndef _E3D_h
#include <E3D/E3D.h>
#endif

#ifndef _E3DMaterial_h
#include <E3D/Material.h>
#endif

#ifndef _E3DMatrix_h
#include <E3D/Matrix.h>
#endif

#ifndef _E3DMesh_h
#include <E3D/Mesh.h>
#endif

#ifndef _E3DFace_h
#include <E3D/Face.h>
#endif

#ifndef _E3DPolygon_h
#include <E3D/Polygon.h>
#endif

#include <E3D/Scene.h>


/*==============================================================================================*/
/* Allocate a Face										*/
/*												*/
/* Description											*/
/*  This function allocates and initializes an E3dFace structure.				*/
/*												*/
/* Return value											*/
/*  Pointer to the allocated E3dFace structure or NULL in case of an error.			*/
/*												*/
/* See also											*/
/*  Geometry/E3d_GeometryFree									*/
/*==============================================================================================*/
E3dFace* E3d_FaceAllocate(char* LName)
{
 E3dFace*	LFace;

 if((LFace=(E3dFace*)EMalloc(sizeof(E3dFace)))!=NULL)
 {
  E3dM_GeometryDefault((E3dGeometry*)LFace);

  if(LName) LFace->Name=EStrDup(LName);
  else LFace->Name=NULL;

  LFace->GeoType=E3dGEO_FACE;

  LFace->Material=NULL;
  LFace->DrawingMaterial=NULL;

  LFace->NumOfHoles=0;
  LFace->Exterior=NULL;
  LFace->Holes=NULL;

  LFace->NumOfVertices=0;
  LFace->Vertices=NULL;
  LFace->NumOfTriangles=0;
  LFace->Triangles=NULL;
 }
 return(LFace);
}


/*======================================================================================*/
/* Free a Face structure								*/
/*											*/
/* Argument										*/
/*  E3dFace* LFace         Pointer to the Face structure to be freed			*/
/*											*/
/* Description										*/
/*  This function first decrements the reference count (RefCnt) of the given Face	*/
/*  structure (if it's not already zero).						*/
/*  After that, if RefCnt is still greater than zero, it means that this Face is	*/
/*  being referred to somewhere else, so E3d_FaceFree() will simply return.		*/
/*  If RefCnt is zero, E3d_FaceFree() will free all the contrours and finally the	*/
/*  Face structure itself.								*/
/*											*/
/* See also										*/
/*  E3d_FaceAllocate									*/
/*======================================================================================*/
void E3d_FaceFree(E3dFace* LFace)
{
 unsigned int	LC, LN;

 if(LFace->RefCnt>0) LFace->RefCnt-=1;
 if(LFace->RefCnt>0) return;

 if(LFace->Name) EFree(LFace->Name);

 if(LFace->Material) E3d_MaterialFree(LFace->Material);
 if(LFace->DrawingMaterial) E3d_MaterialFree(LFace->DrawingMaterial);

 if(LFace->Exterior) E3d_SplineFree(LFace->Exterior);
 if(LFace->Holes)
 {
  LN=LFace->NumOfHoles;
  for(LC=0;LC<LN;LC++) E3d_SplineFree(LFace->Holes[LC]);
  EFree(LFace->Holes);
 }

 if(LFace->Vertices) EFree(LFace->Vertices);

 EFree(LFace);
}


/*==============================================*/
/* Clone a Face					*/
/*==============================================*/
E3dFace* E3d_FaceClone(E3dFace* LFace, E3dFace* LDFace, int LFlags)
{
 if(LDFace==NULL) LDFace=E3d_FaceAllocate(NULL);

 if(LDFace)
 {
  unsigned int	LHC, LHN=LFace->NumOfHoles;

  if(LFlags&E3dCLONE_MATERIALS) LDFace->Material=E3d_SceneMaterialClone(E3d_Scene, LFace->Material);
  else LDFace->Material=LFace->Material;

  if(LDFace->Material) LDFace->Material->RefCnt+=1;

  LDFace->Exterior=E3d_SplineClone(LFace->Exterior);

  if(LHN)
  {
   LDFace->Holes=(E3dSpline**)EMalloc(sizeof(E3dSpline*)*LHN);
   if(LDFace->Holes)
   {
    for(LHC=0;LHC<LHN;LHC++)
    {
     LDFace->Holes[LHC]=E3d_SplineClone(LFace->Holes[LHC]);
    }
   }
   LDFace->NumOfHoles=LHN;
  }
 }

 return(LDFace);
}


/*==============================================*/
/* Convert a Face to a Polygon			*/
/*==============================================*/
void E3d_FaceConvertToPolygon(E3dFace* LFace, E3dPolygon* LPolygon, int LVertexOffset, EBool LInverse)
{
 E3dVertexNode*	LVertexNode;
 unsigned int	LVC, LVID;
 unsigned int	LHC, LHN=LFace->NumOfHoles, LVN;

 LVertexNode=E3d_PolygonVertexNodesAllocate(LPolygon, LFace->NumOfVertices+LHN);

 LVID=LVertexOffset;
 LVN=LFace->Exterior->NumOfLinSegments;

 if(LInverse)
 {
  unsigned int	LVIDc=LVID+LVN-1;

  LPolygon->Normal.X=-LFace->Normal.X;
  LPolygon->Normal.Y=-LFace->Normal.Y;
  LPolygon->Normal.Z=-LFace->Normal.Z;
  for(LVC=0;LVC<LVN;LVC++, LVertexNode++, LVIDc--)
  {
   LVertexNode->VertexID=LVIDc;
   LVertexNode->Normal.X=-LFace->Normal.X;
   LVertexNode->Normal.Y=-LFace->Normal.Y;
   LVertexNode->Normal.Z=-LFace->Normal.Z;
  }
  LVID+=LVN;

  for(LHC=0;LHC<LHN;LHC++)
  {
   LVN=LFace->Holes[LHC]->NumOfLinSegments;
   LVIDc=LVID+LVN-1;

   LVertexNode->VertexID=-1;LVertexNode++;

   for(LVC=0;LVC<LVN;LVC++, LVertexNode++, LVIDc--)
   {
    LVertexNode->VertexID=LVIDc;
    LVertexNode->Normal.X=-LFace->Normal.X;
    LVertexNode->Normal.Y=-LFace->Normal.Y;
    LVertexNode->Normal.Z=-LFace->Normal.Z;
   }
   LVID+=LVN;
  }

  LPolygon->NumOfExteriorVertices=LFace->Exterior->NumOfLinSegments;
  LPolygon->NumOfVertexNodes=LFace->NumOfVertices+LHN;
  LPolygon->NumOfVertices=LFace->NumOfVertices;
 }
 else
 {
  LPolygon->Normal=LFace->Normal;
  for(LVC=0;LVC<LVN;LVC++, LVertexNode++, LVID++)
  {
   LVertexNode->VertexID=LVID;
   LVertexNode->Normal=LFace->Normal;
  }

  for(LHC=0;LHC<LHN;LHC++)
  {
   LVN=LFace->Holes[LHC]->NumOfLinSegments;

   LVertexNode->VertexID=-1;LVertexNode++;

   for(LVC=0;LVC<LVN;LVC++, LVertexNode++, LVID++)
   {
    LVertexNode->VertexID=LVID;
    LVertexNode->Normal=LFace->Normal;
   }
  }

  LPolygon->NumOfExteriorVertices=LFace->Exterior->NumOfLinSegments;
  LPolygon->NumOfVertexNodes=LFace->NumOfVertices+LHN;
  LPolygon->NumOfVertices=LFace->NumOfVertices;
 }
}


/*==============================================*/
/* Tesselate a Face for drawing			*/
/*==============================================*/
int E3d_FaceTesselate(E3dFace* LFace)
{
 E3dSpline*	LExterior=LFace->Exterior;
 E3dSpline*	LHole;
 E3dSpline**	LHoles=LFace->Holes;
 E3dVertex*	LVertices=NULL;
 E3dVertex*	LVertex;
 E3dMatrix	LMatrix;
 E3d2DPosition*	LTVertices;
 E3dCoordinate	LNormalX, LNormalY, LNormalZ;
 unsigned int	LC, LN, LNumOfPolyVertices=0, LNumOfContours=LFace->NumOfHoles+1;
 int		LNumOfTriangles=0;


 if(LExterior)
 {
  if(LExterior->NumOfLinSegments==0) E3d_SplineCreateLinearSegments(LExterior);
  LNumOfPolyVertices+=LExterior->NumOfLinSegments;
 }

 if(LHoles)
 {
  LN=LFace->NumOfHoles;
  for(LC=0;LC<LN;LC++)
  {
   LHole=LHoles[LC];
   if(LHole->NumOfLinSegments==0) E3d_SplineCreateLinearSegments(LHole);
   LNumOfPolyVertices+=LHole->NumOfLinSegments;
  }
 }

 if(LFace->Vertices)
 {
  if(LFace->NumOfVertices!=LNumOfPolyVertices)
  {
   EFree(LFace->Vertices);LFace->Vertices=NULL;LFace->NumOfVertices=0;
  }
 }

 if(LNumOfPolyVertices)
 {
  if(LFace->Vertices==NULL)
  {
   LVertices=E3d_VerticesAllocate(LNumOfPolyVertices, FALSE);
   LFace->Vertices=LVertices;
   LFace->NumOfVertices=LNumOfPolyVertices;
  }
  else LVertices=LFace->Vertices;

  if(LVertices)
  {
   E3dGLCoordinate*	LGLLinSegment;

   LVertex=LVertices;
   LN=LExterior->NumOfLinSegments;LGLLinSegment=LExterior->GLLinSegments;
   for(LC=0;LC<LN;LC++, LGLLinSegment+=3, LVertex++)
   {
    LVertex->X=LGLLinSegment[E3dX];
    LVertex->Y=LGLLinSegment[E3dY];
    LVertex->Z=LGLLinSegment[E3dZ];
   }

   if(LHoles)
   {
    unsigned int	LHC, LHN;

    LHN=LFace->NumOfHoles;
    for(LHC=0;LHC<LHN;LHC++)
    {
     LHole=LHoles[LHC];

     LN=LHole->NumOfLinSegments;LGLLinSegment=LHole->GLLinSegments+(LN-1)*3;
     for(LC=0;LC<LN;LC++, LGLLinSegment-=3, LVertex++)
     {
      LVertex->X=LGLLinSegment[E3dX];
      LVertex->Y=LGLLinSegment[E3dY];
      LVertex->Z=LGLLinSegment[E3dZ];
     }
    }
   }
  }
 }




// Compute the Normal of the Face
//
 if(LVertices)
 {
  unsigned int	LVertNum, LVCnt;

  if((LVertNum=LExterior->NumOfLinSegments)>2)
  {
   E3dCoordinate	LX0, LY0, LZ0, LX1, LY1, LZ1, LX2, LY2, LZ2,
			r;

   LVertex=LVertices;

   if(LVertNum==3)
   {
    LX0=LVertex->X;LY0=LVertex->Y;LZ0=LVertex->Z;LVertex++;
    LX1=LVertex->X;LY1=LVertex->Y;LZ1=LVertex->Z;LVertex++;
    LX2=LVertex->X;LY2=LVertex->Y;LZ2=LVertex->Z;

    LNormalX=-((LY2-LY1)*(LZ2-LZ0)-(LZ2-LZ1)*(LY2-LY0));
    LNormalY=(LX2-LX1)*(LZ2-LZ0)-(LZ2-LZ1)*(LX2-LX0);
    LNormalZ=-((LX2-LX1)*(LY2-LY0)-(LY2-LY1)*(LX2-LX0));
   }
   else
/*--------------------------------------------------------------------------------------------------------------*/
/* If the Exterior has more than 3 vertices, it can be concave, so to make sure we don't pick 3 Vertices for	*/
/* the Normal calculation which determine a reverse Face, we have to run trough all the triangles of 3		*/
/* connected Vertices and sum the normals of them, because even if a Face is "strongly" concave, there are	*/
/* always more such triangles with the right orientation than ones with the wrong.				*/
/*--------------------------------------------------------------------------------------------------------------*/
   {
    LNormalX=0.0, LNormalY=0.0, LNormalZ=0.0;

    LN=LVertNum-1;
    for(LVCnt=0;LVCnt<LN;LVCnt++)
    {
     LX0=LVertex->X;LY0=LVertex->Y;LZ0=LVertex->Z;LVertex++;
     LX1=LVertex->X;LY1=LVertex->Y;LZ1=LVertex->Z;

     LNormalX-=(LZ0+LZ1)*(LY1-LY0);
     LNormalY+=(LZ0+LZ1)*(LX1-LX0);
     LNormalZ-=(LY0+LY1)*(LX1-LX0);
    }
    LX0=LVertex->X;LY0=LVertex->Y;LZ0=LVertex->Z;
    LX1=LVertices->X;LY1=LVertices->Y;LZ1=LVertices->Z;

    LNormalX-=(LZ0+LZ1)*(LY1-LY0);
    LNormalY+=(LZ0+LZ1)*(LX1-LX0);
    LNormalZ-=(LY0+LY1)*(LX1-LX0);
   }

   r=sqrt(LNormalX*LNormalX+LNormalY*LNormalY+LNormalZ*LNormalZ);
   if(r>0.0) { r=1.0/r;LNormalX*=r;LNormalY*=r;LNormalZ*=r; }
  }
  else { LNormalX=0.0;LNormalY=0.0;LNormalZ=0.0; }
 }
 else return(0);

 LFace->Normal.X=LNormalX;
 LFace->Normal.Y=LNormalY;
 LFace->Normal.Z=LNormalZ;
 LFace->Normal.Length=1.0;

#ifdef USEOpenGL
 LFace->GLNormal[E3dX]=LNormalX;
 LFace->GLNormal[E3dY]=LNormalY;
 LFace->GLNormal[E3dZ]=LNormalZ;
#endif	// USEOpenGL


 if(LFace->Triangles) { EFree(LFace->Triangles);LFace->Triangles=NULL;LFace->NumOfTriangles=0; }


// Special case for triangle
//
 if(LNumOfPolyVertices==3)
 {
  E3dTriangle*	LTriangle;

  if((LTriangle=(E3dTriangle*)EMalloc(sizeof(E3dTriangle)))!=NULL)
  {
   E3dVertexNode*	LVertexNode=LTriangle->VertexNodes;

   LFace->Triangles=LTriangle;
   LFace->NumOfTriangles=1;

   LVertexNode->VertexID=0;
#ifdef USEOpenGL
   LVertex=LVertices;
   LVertexNode->GLVertex[E3dX]=LVertex->X;
   LVertexNode->GLVertex[E3dY]=LVertex->Y;
   LVertexNode->GLVertex[E3dZ]=LVertex->Z;
#endif	// USEOpenGL
   LVertexNode++;

   LVertexNode->VertexID=1;
#ifdef USEOpenGL
   LVertex++;
   LVertexNode->GLVertex[E3dX]=LVertex->X;
   LVertexNode->GLVertex[E3dY]=LVertex->Y;
   LVertexNode->GLVertex[E3dZ]=LVertex->Z;
#endif	// USEOpenGL
   LVertexNode++;

   LVertexNode->VertexID=2;
#ifdef USEOpenGL
   LVertex++;
   LVertexNode->GLVertex[E3dX]=LVertex->X;
   LVertexNode->GLVertex[E3dY]=LVertex->Y;
   LVertexNode->GLVertex[E3dZ]=LVertex->Z;
#endif	// USEOpenGL

   return(1);
  }
  return(0);
 }

//Printf("Face Normal: %f %f %f\n", LNormalX, LNormalY, LNormalZ);fflush(stdout);

// Create a Matrix that rotates the Normal of the Face into the (0,0,1)
// vector, (i.e. aligning the Face with the X-Y plane)
//
 E3d_NormalGetDefRotMatrix(LNormalX, LNormalY, LNormalZ, LMatrix);




// Transform the Vertices to create a 2D Polygon
//
 LNumOfTriangles=0;
 if((LTVertices=(E3d2DPosition*)EMalloc(sizeof(E3d2DPosition)*LNumOfPolyVertices))!=NULL)
 {
  E3dTTriangle*		LTTriangles;
  E3dTTriangle*		LTTriangle;
  E3dTriangle*		LTriangle;
  E3dCoordinate		LX, LY, LZ,
			LCtrX, LCtrY, LCtrZ,
			LScaler;
  int*			LSizes;
  unsigned int		LC1, LMaxNumOfTriangles;
  EBool			LFailed;


  if((LSizes=(int*)EMalloc(sizeof(int)*(LNumOfContours+2)))!=NULL)
  {
   LC1=0;

   LSizes[LC1++]=LNumOfPolyVertices;
   LSizes[LC1++]=LNumOfContours;		// # of contours

   LSizes[LC1++]=LExterior->NumOfLinSegments;

   if(LHoles)
   {
    LN=LFace->NumOfHoles;
    for(LC=0;LC<LN;LC++) LSizes[LC1++]=LHoles[LC]->NumOfLinSegments;
   }

//if(LC1>(LNumOfContours+2)) { printf("Overrun: %d %d   %d, %d!!!!!!\n", LC1, LPolygon->NumOfVertexNodes-LNumOfPolyVertices+1+2, LPolygon->NumOfVertexNodes, LNumOfPolyVertices);fflush(stdout); }

// Compute center of the Face
//
   LCtrX=LCtrY=LCtrZ=0.0;
   LN=LExterior->NumOfLinSegments;
   for(LC=0, LVertex=LVertices;LC<LN;LC++, LVertex++)
   {
    LCtrX+=LVertex->X;
    LCtrY+=LVertex->Y;
    LCtrZ+=LVertex->Z;
   }
   LScaler=1.0/((double)LN);
   LCtrX*=LScaler;LCtrY*=LScaler;LCtrZ*=LScaler;

   LScaler=100.0;


// The maximum number of triangles in the solution is (# of Vertices)+2*(# of contours)-4
//
   LMaxNumOfTriangles=LNumOfPolyVertices+2*LNumOfContours-4;

// Transform Face's Vertices to the X-Y plane
//
   do
   {
    LFailed=FALSE;

    for(LC=0, LVertex=LVertices;LC<LNumOfPolyVertices;LC++, LVertex++)
    {
     LX=(LVertex->X-LCtrX)*LScaler;		// For accuracy...
     LY=(LVertex->Y-LCtrY)*LScaler;
     LZ=(LVertex->Z-LCtrZ)*LScaler;

// Do a simplified Matrix transform
//
     LTVertices[LC].X=LX*LMatrix[M00]+LY*LMatrix[M10]+LZ*LMatrix[M20];
     LTVertices[LC].Y=LX*LMatrix[M01]+LY*LMatrix[M11]+LZ*LMatrix[M21];
    }


    if((LTTriangles=(E3dTTriangle*)EMalloc(sizeof(E3dTTriangle)*LMaxNumOfTriangles))!=NULL)
    {
     if((LNumOfTriangles=E3d_Triangulate2DPolygon(LTVertices, LTTriangles, LSizes))>0)
     {
      if((LTriangle=(E3dTriangle*)EMalloc(sizeof(E3dTriangle)*LNumOfTriangles))!=NULL)
      {
       E3dVertexNode*	LVertexNode;

       LFace->Triangles=LTriangle;LFace->NumOfTriangles=LNumOfTriangles;
       LTTriangle=LTTriangles;
       for(LC=0;LC<LNumOfTriangles;LC++, LTTriangle++, LTriangle++)
       {
	LVertexNode=LTriangle->VertexNodes;
	LVertexNode->VertexID=LTTriangle->A;
#ifdef USEOpenGL
	LVertex=LVertices+LVertexNode->VertexID;
	LVertexNode->GLVertex[E3dX]=LVertex->X;
	LVertexNode->GLVertex[E3dY]=LVertex->Y;
	LVertexNode->GLVertex[E3dZ]=LVertex->Z;
#endif	// USEOpenGL
	LVertexNode++;

	LVertexNode->VertexID=LTTriangle->B;
#ifdef USEOpenGL
	LVertex=LVertices+LVertexNode->VertexID;
	LVertexNode->GLVertex[E3dX]=LVertex->X;
	LVertexNode->GLVertex[E3dY]=LVertex->Y;
	LVertexNode->GLVertex[E3dZ]=LVertex->Z;
#endif	// USEOpenGL
	LVertexNode++;

	LVertexNode->VertexID=LTTriangle->C;
#ifdef USEOpenGL
	LVertex=LVertices+LVertexNode->VertexID;
	LVertexNode->GLVertex[E3dX]=LVertex->X;
	LVertexNode->GLVertex[E3dY]=LVertex->Y;
	LVertexNode->GLVertex[E3dZ]=LVertex->Z;
#endif	// USEOpenGL
       }
      }
     }
     else LFailed=TRUE;
     EFree(LTTriangles);
    }

    LScaler*=10.0;
   } while(LFailed&&(LScaler<10000000.0));

   if(LFailed)
   {
//     printf("TessErr failed after 5 iterations ExteriorVertices: %d\n", LPolygon->NumOfExteriorVertices);fflush(stdout);

//printf("Ctr: %f,%f,%f    Vtx: ", LCtrX, LCtrY, LCtrZ);

    for(LC=0, LVertex=LFace->Vertices;LC<LNumOfPolyVertices;LC++, LVertex++)
    {
//printf("%f, %f, %f ", LVertex->X, LVertex->Y, LVertex->Z);
    }
//printf("\n");fflush(stdout);
   }
   EFree(LSizes);
  }

  EFree(LTVertices);
 }

 return(LNumOfTriangles);
}
