/*****************************************************************************
* Conversion routines from curves and surfaces to polygons and polylines.    *
*									     *
* Written by:  Gershon Elber				Ver 1.0, Apr 1992    *
*****************************************************************************/

#include "irit_sm.h"
#include "iritprsr.h"
#include "allocate.h"
#include "ip_cnvrt.h"

static RealType CubicBzrTol = 0.01;

static IPPolygonStruct *CagdPolylines2IritPolylines(CagdPolylineStruct *Polys);

/*****************************************************************************
* Routine to convert a curve into a polyline with SamplesPerCurve samples.   *
*****************************************************************************/
static IPPolygonStruct *CagdPolylines2IritPolylines(CagdPolylineStruct *Polys)
{
    int i, j, n;
    IPVertexStruct *V,
	*VHead = NULL,
	*VTail = NULL;
    IPPolygonStruct *P,
	*PHead = NULL;
    CagdPolylineStruct *CagdPoly;

    for (CagdPoly = Polys;
	 CagdPoly != NULL;
	 CagdPoly = CagdPoly -> Pnext) {
	n = CagdPoly -> Length;

	for (i = 0, VHead = NULL; i < n; i++) {	     /* Convert to vertices. */
	    V = IPAllocVertex(0, 0, NULL, NULL);

	    for (j = 0; j < 3; j++)	   	   /* Convert to our format. */
		V -> Coord[j] = CagdPoly -> Polyline[i].Pt[j];

	    if (VHead) {
		VTail -> Pnext = V;
		VTail = V;
	    }
	    else
		VHead = VTail = V;
	}

	P = IPAllocPolygon(0, 0, VHead, PHead);
	PHead = P;
    }

    CagdPolylineFreeList(Polys);

    return PHead;
}

/*****************************************************************************
* Routine to convert one curve into a polyline with SamplesPerCurve samples. *
*****************************************************************************/
IPPolygonStruct *IritCurve2Polylines(CagdCrvStruct *Crv, int SamplesPerCurve)
{
    CagdPolylineStruct *CagdPoly;

    if (SamplesPerCurve < 1)
	SamplesPerCurve = 1;

    CagdPoly = CagdCrv2Polyline(Crv, SamplesPerCurve);

    return CagdPolylines2IritPolylines(CagdPoly);
}

/*****************************************************************************
* Routine to convert a single curve's control polygon into a polyline.	     *
*****************************************************************************/
IPPolygonStruct *IritCurve2CtlPoly(CagdCrvStruct *Crv)
{
    CagdPolylineStruct
	*CagdPoly = CagdCrv2CtrlPoly(Crv);

    return CagdPolylines2IritPolylines(CagdPoly);
}

/*****************************************************************************
* Routine to convert a single surface into a polylines with SamplesPerCurve  *
* samples, NumOfIsolines isolines into a polyline object list.		     *
*   if NumOfIsolines has negative value, its absolute value is heuristically *
* used to derive a new NumOfIsolines number for it.			     *
*****************************************************************************/
IPPolygonStruct *IritSurface2Polylines(CagdSrfStruct *Srf, int NumOfIsolines[2],
				       int SamplesPerCurve)
{
    CagdPolylineStruct *CagdPoly;

    if (NumOfIsolines[0] < 0) {
	if (Srf -> UOrder > 2)
	    NumOfIsolines[0] = (Srf -> ULength - NumOfIsolines[0]) / 2;
	else
	    NumOfIsolines[0] = -NumOfIsolines[0];
    }
    if (NumOfIsolines[0] < 2)
	NumOfIsolines[0] = 2;

    if (NumOfIsolines[1] < 0) {
	if (Srf -> VOrder > 2)
	    NumOfIsolines[1] = (Srf -> VLength - NumOfIsolines[0]) / 2;
	else
	    NumOfIsolines[1] = -NumOfIsolines[1];
    }
    if (NumOfIsolines[0] < 2)
	NumOfIsolines[0] = 2;

    if (SamplesPerCurve < 1)
	SamplesPerCurve = 1;

    CagdPoly = CagdSrf2Polylines(Srf, NumOfIsolines, SamplesPerCurve);

    return CagdPolylines2IritPolylines(CagdPoly);
}

/*****************************************************************************
* Routine to convert a single surface into a polylines with SamplesPerCurve  *
* samples, NumOfIsolines isolines into a polyline object list.		     *
*****************************************************************************/
IPPolygonStruct *IritSurface2CtlMesh(CagdSrfStruct *Srf)
{
    CagdPolylineStruct
	*CagdPoly = CagdSrf2CtrlMesh(Srf);

    return CagdPolylines2IritPolylines(CagdPoly);
}

/*****************************************************************************
* Routine to convert a single surface into a polygons			     *
*****************************************************************************/
IPPolygonStruct *IritSurface2Polygons(CagdSrfStruct *Srf, int FourPerFlat,
						  int FineNess, int ComputeUV)
{
    int i, j;
    char UVStr[LINE_LEN];
    VectorType Vin;
    IPVertexStruct *V, *VHead,
	*VTail = NULL;
    IPPolygonStruct *P,
	*PHead = NULL;
    CagdPolygonStruct *CagdPolygon,
	*CagdPolygonHead = CagdSrf2Polygons(Srf, FineNess, TRUE, FourPerFlat,
								    ComputeUV);

    for (CagdPolygon = CagdPolygonHead, VHead = NULL;
	 CagdPolygon != NULL;
	 CagdPolygon = CagdPolygon -> Pnext) {
	/* All polygons are triangles! */

	for (i = 0, VHead = NULL; i < 3; i++) {	     /* Convert to vertices. */
	    V = IPAllocVertex(0, 0, NULL, NULL);
	    IP_SET_NORMAL_VRTX(V);       	  /* This vertex has normal. */

	    for (j = 0; j < 3; j++)	     	   /* Convert to our format. */
		V -> Coord[j] = CagdPolygon -> Polygon[i].Pt[j];
	    for (j = 0; j < 3; j++)
		V -> Normal[j] = CagdPolygon -> Normal[i].Vec[j];

	    if (ComputeUV) {
		sprintf(UVStr, "%lf %lf",
			CagdPolygon -> UV[i].UV[0],
			CagdPolygon -> UV[i].UV[1]);
		AttrSetStrAttrib(&V -> Attrs, "uvvals", UVStr);
	    }

	    if (VHead) {
		VTail -> Pnext = V;
		VTail = V;
	    }
	    else
		VHead = VTail = V;
	}

	P = IPAllocPolygon(0, 0, VHead, PHead);
	IP_SET_CONVEX_POLY(P);

	VTail -> Pnext = VHead;
	PT_ADD(Vin, CagdPolygon -> Polygon[0].Pt,
	            CagdPolygon -> Normal[0].Vec);
	IritPrsrUpdatePolyPlane2(P, Vin);          /* Update plane equation. */

	if (!_IritPrsrPolyListCirc)
	    VTail -> Pnext = NULL;

	PHead = P;
    }

    CagdPolygonFreeList(CagdPolygonHead);

    return PHead;
}

/*****************************************************************************
* Sets the tolerance that is used by the Bezier to Cubic Bezier.	     *
*****************************************************************************/
void IritSetCurvesToCubicBzrTol(RealType Tolerance)
{
    CubicBzrTol = Tolerance;
}

/*****************************************************************************
* Converts a list of arbitrary curves into cubic beziers (approximation).    *
*****************************************************************************/
CagdCrvStruct *IritCurvesToCubicBzrCrvs(CagdCrvStruct *Crvs,
					IPPolygonStruct **CtlPolys,
					CagdBType DrawCurve,
					CagdBType DrawCtlPoly,
					CagdRType MaxArcLen)
{
    CagdCrvStruct *BzrCrvs, *BzrCrv, *Crv, *CubicBzrCrvs, *TCrv,
	*AllCubicBzrCrvs = NULL;

    if (DrawCtlPoly)
	*CtlPolys = NULL;

    for (Crv = Crvs; Crv != NULL; Crv = Crv -> Pnext) {
	if (DrawCtlPoly) {
	    IPPolygonStruct
		*CagdPoly = CagdPolylines2IritPolylines(CagdCrv2CtrlPoly(Crv));

	    CagdPoly -> Pnext = *CtlPolys;
	    *CtlPolys = CagdPoly;
	}

	if (DrawCurve) {
	    if (CAGD_IS_BEZIER_CRV(Crv)) {
		CubicBzrCrvs = BzrApproxBzrCrvAsCubics(Crv, CubicBzrTol,
						       MaxArcLen, TRUE);
		for (TCrv = CubicBzrCrvs;
		     TCrv -> Pnext != NULL;
		     TCrv = TCrv -> Pnext);
		TCrv -> Pnext = AllCubicBzrCrvs;
		AllCubicBzrCrvs = CubicBzrCrvs;
	    }
	    else if (CAGD_IS_BSPLINE_CRV(Crv)) {
		BzrCrvs = CnvrtBspline2BezierCrv(Crv);
		for (BzrCrv = BzrCrvs;
		     BzrCrv != NULL;
		     BzrCrv = BzrCrv -> Pnext) {
		    CubicBzrCrvs = BzrApproxBzrCrvAsCubics(BzrCrv, CubicBzrTol,
							   MaxArcLen, TRUE);
		    for (TCrv = CubicBzrCrvs;
			 TCrv -> Pnext != NULL;
			 TCrv = TCrv -> Pnext);
		    TCrv -> Pnext = AllCubicBzrCrvs;
		    AllCubicBzrCrvs = CubicBzrCrvs;
		}
		CagdCrvFreeList(BzrCrvs);
	    }
	}
    }
    return AllCubicBzrCrvs;
}

/*****************************************************************************
* Converts a list of arbitrary surfaces into cubic beziers (approximation).  *
*****************************************************************************/
CagdCrvStruct *IritSurfacesToCubicBzrCrvs(CagdSrfStruct *Srfs,
					  IPPolygonStruct **CtlMeshes,
					  CagdBType DrawSurface,
					  CagdBType DrawMesh,
					  int NumOfIsolines[2],
					  CagdRType MaxArcLen)
{
    CagdCrvStruct
	*AllCubicBzrCrvs = NULL;
    CagdSrfStruct *Srf;

    if (DrawMesh)
	*CtlMeshes = NULL;

    for (Srf = Srfs; Srf != NULL; Srf = Srf -> Pnext) {
	if (DrawMesh) {
	    IPPolygonStruct
		*CagdPoly = CagdPolylines2IritPolylines(CagdSrf2CtrlMesh(Srf)),
		*CagdPolyTemp = CagdPoly;

	    if (CagdPolyTemp)
		while (CagdPolyTemp -> Pnext)
		    CagdPolyTemp = CagdPolyTemp -> Pnext;
	    CagdPolyTemp -> Pnext = *CtlMeshes;
	    *CtlMeshes = CagdPoly;
	}

	if (DrawSurface) {
	    CagdCrvStruct *CubicBzrCrvs, *TCrv,
		*Crvs = CagdSrf2Curves(Srf, NumOfIsolines);

	    CubicBzrCrvs = IritCurvesToCubicBzrCrvs(Crvs, NULL, TRUE, FALSE,
						    MaxArcLen);
	    CagdCrvFreeList(Crvs);
	    for (TCrv = CubicBzrCrvs;
		 TCrv -> Pnext != NULL;
		 TCrv = TCrv -> Pnext);
	    TCrv -> Pnext = AllCubicBzrCrvs;
	    AllCubicBzrCrvs = CubicBzrCrvs;
	}
    }
    return AllCubicBzrCrvs;
}
