/******************************************************************************
* Bzr2Poly.c - Bezier to polygon/polylines conversion routines.		      *
*******************************************************************************
* Written by Gershon Elber, Mar. 90.					      *
******************************************************************************/

#include "cagd_loc.h"

/*****************************************************************************
* Routine to convert a single bezier surface to set of triangles	     *
* approximating it. FineNess is a finess control on result and the bigger it *
* is more triangles may result. a value of 10 is a good start value.	     *
* NULL is returned in case of an error, otherwise list of CagdPolygonStruct. *
*****************************************************************************/
CagdPolygonStruct *BzrSrf2Polygons(CagdSrfStruct *Srf, int FineNess,
	 CagdBType ComputeNormals, CagdBType FourPerFlat, CagdBType ComputeUV)
{
    int i, j, FineNessU1, FineNessV1, FineNessU, FineNessV, BaseIndex;
    CagdRType *Pt;
    CagdPointType
	PType = Srf -> PType;
    CagdPtStruct PtCenter, *Pt1, *Pt2, *Pt3, *Pt4, *PtMesh, *PtMeshPtr;
    CagdUVStruct UVCenter, *UVMeshPtr,
	*UV1 = NULL,
	*UV2 = NULL,
	*UV3 = NULL,
	*UV4 = NULL,
	*UVMesh = NULL;
    CagdVecStruct NlCenter, *PtNrmlPtr,
	*Nl1 = NULL,
	*Nl2 = NULL,
	*Nl3 = NULL,
	*Nl4 = NULL,
	*PtNrml = NULL;
    CagdPolygonStruct *Poly,
	*PolyHead = NULL;

    if (!CAGD_IS_BEZIER_SRF(Srf))
	return NULL;

    /* Simple heuristic to estimate how many samples to compute. */
    FineNessU = Srf -> UOrder * FineNess / 10;
    FineNessV = Srf -> VOrder * FineNess / 10;

    if (FineNessU < 2)
	FineNessU = 2;
    if (FineNessV < 2)
	FineNessV = 2;

    switch (_CagdLin2Poly) {
	case CAGD_REG_POLY_PER_LIN:
	    break;
    	case CAGD_ONE_POLY_PER_LIN:
	    if (Srf -> UOrder == 2)
		FineNessU = 2;
	    if (Srf -> VOrder == 2)
		FineNessV = 2;
	    break;
    	case CAGD_ONE_POLY_PER_COLIN:
	    break;
    }

    FineNessU1 = FineNessU - 1;
    FineNessV1 = FineNessV - 1;

    /* Allocate a mesh to hold all vertices so common vertices need not be   */
    /* Evaluated twice, and evaluate the surface at these mesh points.	     */
    PtMeshPtr = PtMesh = (CagdPtStruct *) IritMalloc(FineNessU * FineNessV *
							sizeof(CagdPtStruct));

    for (i = 0; i < FineNessU; i++)
	for (j = 0; j < FineNessV; j++) {
	    Pt = BzrSrfEvalAtParam(Srf, ((CagdRType) i) / FineNessU1,
				        ((CagdRType) j) / FineNessV1);
	    CagdCoerceToE3(PtMeshPtr -> Pt, &Pt, -1, PType);
	    PtMeshPtr++;
	}

    if (ComputeNormals) {
	PtNrmlPtr = PtNrml = (CagdVecStruct *)
	    IritMalloc(FineNessU * FineNessV * sizeof(CagdVecStruct));
	for (i = 0; i < FineNessU; i++)
	    for (j = 0; j < FineNessV; j++) {
		Nl1 = BzrSrfNormal(Srf, ((CagdRType) i) / FineNessU1,
					((CagdRType) j) / FineNessV1);
		CAGD_COPY_VECTOR(*PtNrmlPtr, *Nl1);
		PtNrmlPtr++;
	    }
    }

    if (ComputeUV) {
	UVMeshPtr = UVMesh = (CagdUVStruct *)
	    IritMalloc(FineNessU * FineNessV * sizeof(CagdUVStruct));
	for (i = 0; i < FineNessU; i++)
	    for (j = 0; j < FineNessV; j++) {
		UVMeshPtr -> UV[0] = ((CagdRType) i) / FineNessU1;
		UVMeshPtr -> UV[1] = ((CagdRType) j) / FineNessU1;
		UVMeshPtr++;
	    }
    }

    /* Now that we have the mesh, create the polygons. */
    for (i = 0; i < FineNessU1; i++)
	for (j = 0; j < FineNessV1; j++) {
	    BaseIndex = i * FineNessV + j;
	    Pt1 = &PtMesh[BaseIndex];        /* Cache the four flat corners. */
	    Pt2 = &PtMesh[BaseIndex + 1];
	    Pt3 = &PtMesh[BaseIndex + FineNessV + 1];
	    Pt4 = &PtMesh[BaseIndex + FineNessV];

	    if (ComputeNormals) {
		Nl1 = &PtNrml[BaseIndex];
		Nl2 = &PtNrml[BaseIndex + 1];
		Nl3 = &PtNrml[BaseIndex + FineNessV + 1];
		Nl4 = &PtNrml[BaseIndex + FineNessV];
	    }
	    if (ComputeUV) {
		UV1 = &UVMesh[BaseIndex];
		UV2 = &UVMesh[BaseIndex + 1];
		UV3 = &UVMesh[BaseIndex + FineNessV + 1];
		UV4 = &UVMesh[BaseIndex + FineNessV];
	    }

	    if (FourPerFlat) {  /* Eval middle point and create 4 triangles. */
		Pt = BzrSrfEvalAtParam(Srf, (i + 0.5) / FineNessU1,
				            (j + 0.5) / FineNessV1);
		CagdCoerceToE3(PtCenter.Pt, &Pt, -1, PType);

		if (ComputeNormals) {
		    /* Average the four normals to find the middle one. */
		    CAGD_COPY_VECTOR(NlCenter, *Nl1);
		    CAGD_ADD_VECTOR(NlCenter, *Nl2);
		    CAGD_ADD_VECTOR(NlCenter, *Nl3);
		    CAGD_ADD_VECTOR(NlCenter, *Nl4);
		    CAGD_NORMALIZE_VECTOR(NlCenter);
		}


		if (ComputeUV) {
		    UVCenter.UV[0] = (UV1 -> UV[0] + UV2 -> UV[0] +
				      UV3 -> UV[0] + UV4 -> UV[0]) / 4.0;
		    UVCenter.UV[1] = (UV1 -> UV[1] + UV2 -> UV[1] +
				      UV3 -> UV[1] + UV4 -> UV[1]) / 4.0;
		}

		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt1, Pt2, &PtCenter,
					Nl1, Nl2, &NlCenter,
					UV1, UV2, &UVCenter);
		CAGD_LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt2, Pt3, &PtCenter,
					Nl2, Nl3, &NlCenter,
					UV2, UV3, &UVCenter);
		CAGD_LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt3, Pt4, &PtCenter,
					Nl3, Nl4, &NlCenter,
					UV3, UV4, &UVCenter);
		CAGD_LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt4, Pt1, &PtCenter,
					Nl4, Nl1, &NlCenter,
					UV4, UV1, &UVCenter);
		CAGD_LIST_PUSH(Poly, PolyHead);
	    }
	    else {			   /* Only two along the diagonal... */
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt1, Pt2, Pt3,
					Nl1, Nl2, Nl3,
					UV1, UV2, UV3);
		CAGD_LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt3, Pt4, Pt1,
					Nl3, Nl4, Nl1,
					UV3, UV4, UV1);
		CAGD_LIST_PUSH(Poly, PolyHead);
	    }
	}

    IritFree((VoidPtr) PtMesh);
    if (ComputeNormals)
	IritFree((VoidPtr) PtNrml);
    if (ComputeUV)
	IritFree((VoidPtr) UVMesh);

    return PolyHead;
}

/*****************************************************************************
* Routine to convert a single bezier surface to NumOfIsoline polyline list   *
* in each param. direction with SamplesPerCurve in each isoparametric curve. *
* Polyline are always E3 of CagdPolylineStruct type.			     *
* Iso parametric curves are sampled equally spaced in parametric space.	     *
* NULL is returned in case of an error, otherwise list of CagdPolylineStruct.*
*****************************************************************************/
CagdPolylineStruct *BzrSrf2Polylines(CagdSrfStruct *Srf, int NumOfIsocurves[2],
							 int SamplesPerCurve)
{
    int i;
    CagdRType t;
    CagdCrvStruct *Crv;
    CagdPolylineStruct *PolyList = NULL, *Poly;

    if (!CAGD_IS_BEZIER_SRF(Srf))
	return NULL;

    /* Make sure requested format is something reasonable. */
    if (SamplesPerCurve < 1)
	SamplesPerCurve = 1;
    if (SamplesPerCurve > CAGD_MAX_BEZIER_CACHE_ORDER)
	SamplesPerCurve = CAGD_MAX_BEZIER_CACHE_ORDER;
    if (NumOfIsocurves[0] < 2)
	NumOfIsocurves[0] = 2;
    if (NumOfIsocurves[1] <= 0)
	NumOfIsocurves[1] = NumOfIsocurves[0];
    else if (NumOfIsocurves[1] < 2)
	NumOfIsocurves[1] = 2;

    for (i = 0; i < NumOfIsocurves[0]; i++) {
	t = ((CagdRType) i) / (NumOfIsocurves[0] - 1);
	if (t > 1.0)
	    t = 1.0;		   	      /* In case of round off error. */

	Crv = BzrSrfCrvFromSrf(Srf, t, CAGD_CONST_U_DIR);
	Poly = BzrCrv2Polyline(Crv, SamplesPerCurve);
	Poly -> Pnext = PolyList;
	PolyList = Poly;
	CagdCrvFree(Crv);
    }

    for (i = 0; i < NumOfIsocurves[1]; i++) {
	t = ((CagdRType) i) / (NumOfIsocurves[1] - 1);
	if (t > 1.0)
	    t = 1.0;		   	      /* In case of round off error. */

	Crv = BzrSrfCrvFromSrf(Srf, t, CAGD_CONST_V_DIR);
	Poly = BzrCrv2Polyline(Crv, SamplesPerCurve);
	Poly -> Pnext = PolyList;
	PolyList = Poly;
	CagdCrvFree(Crv);
    }

    return PolyList;
}

/*****************************************************************************
* Routine to convert a single bezier surface to NumOfIsoline isocurve list   *
* in each param. direction.						     *
* Iso parametric curves are sampled equally spaced in parametric space.	     *
* NULL is returned in case of an error, otherwise list of CagdCrvStruct.     *
*****************************************************************************/
CagdCrvStruct *BzrSrf2Curves(CagdSrfStruct *Srf, int NumOfIsocurves[2])
{
    int i;
    CagdRType t;
    CagdCrvStruct *Crv,
	*CrvList = NULL;

    if (!CAGD_IS_BEZIER_SRF(Srf))
	return NULL;

    /* Make sure requested format is something reasonable. */
    if (NumOfIsocurves[0] < 2)
	NumOfIsocurves[0] = 2;
    if (NumOfIsocurves[1] <= 0)
	NumOfIsocurves[1] = NumOfIsocurves[0];
    else if (NumOfIsocurves[1] < 2)
	NumOfIsocurves[1] = 2;

    for (i = 0; i < NumOfIsocurves[0]; i++) {
	t = ((CagdRType) i) / (NumOfIsocurves[0] - 1);
	if (t > 1.0)
	    t = 1.0;		   	      /* In case of round off error. */

	Crv = CagdCrvFromSrf(Srf, t, CAGD_CONST_U_DIR);
	Crv -> Pnext = CrvList;
	CrvList = Crv;
    }

    for (i = 0; i < NumOfIsocurves[1]; i++) {
	t = ((CagdRType) i) / (NumOfIsocurves[1] - 1);
	if (t > 1.0)
	    t = 1.0;		   	      /* In case of round off error. */

	Crv = CagdCrvFromSrf(Srf, t, CAGD_CONST_V_DIR);
	Crv -> Pnext = CrvList;
	CrvList = Crv;
    }

    return CrvList;
}

/*****************************************************************************
* Routine to convert a single bezier curve to polyline with SamplesPerCurve  *
* samples. Polyline is always E3 of CagdPolylineStruct type.		     *
* Curve is sampled equally spaced in parametric space.			     *
* NULL is returned in case of an error, otherwise CagdPolylineStruct.	     *
*****************************************************************************/
CagdPolylineStruct *BzrCrv2Polyline(CagdCrvStruct *Crv, int SamplesPerCurve)
{
    int i, j,
	n = 1 << SamplesPerCurve,
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv),
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType *Polyline[CAGD_MAX_PT_SIZE], Scaler;
    CagdPtStruct *NewPolyline;
    CagdPolylineStruct *P;

    if (!CAGD_IS_BEZIER_CRV(Crv))
	return NULL;

    /* Make sure requested format is something reasonable. */
    if (SamplesPerCurve < 1)
	SamplesPerCurve = 1;
    if (SamplesPerCurve > CAGD_MAX_BEZIER_CACHE_ORDER)
	SamplesPerCurve = CAGD_MAX_BEZIER_CACHE_ORDER;

    P = CagdPolylineNew(n);
    NewPolyline = P -> Polyline;

    /* Allocate temporary memory to hold evaluated curve. */
    for (i = 0; i < CAGD_MAX_PT_SIZE; i++)
	Polyline[i] = (CagdRType *) IritMalloc(sizeof(CagdRType) * n);

    if (MaxCoord > 3)
	MaxCoord = 3;

    BzrCrvEvalToPolyline(Crv, SamplesPerCurve, Polyline);

    for (i = n - 1; i >= 0; i--) {	          /* Convert to E3 polyline. */
	if (IsNotRational)
	    Scaler = 1.0;
	else
	    Scaler = Polyline[0][i];

	for (j = 0; j < MaxCoord; j++)
	    NewPolyline[i].Pt[j] = Polyline[j+1][i] / Scaler;
	for (j = MaxCoord; j < 3; j++)
	    NewPolyline[i].Pt[j] = 0.0;
    }
    for (i = 0; i < CAGD_MAX_PT_SIZE; i++)
	IritFree((VoidPtr) Polyline[i]);

    return P;
}
