/******************************************************************************
* SymbPoly.c - Generic Curve/Surface to polygon/polylines conversion routines.*
*******************************************************************************
* Written by Gershon Elber, July. 90.					      *
******************************************************************************/

#include "symb_loc.h"
#include "dist_pts.h"

#define CURVE_PTS_DIST_RESOLUTION 10

static CagdCrvStruct
    *GlblDeriv1MagSqrCrv = NULL,
    *GlblCrvtrCrv = NULL;
static CagdRType GlblCrvtrCrvTMin, GlblCrvtrCrvTMax;

static CagdPolylineStruct *SymbCrv2OptimalPolyline(CagdCrvStruct *Crv,
						   int SamplesPerCurve);
static RealType CrvCurvatureEvalFunc(RealType t);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Routine to convert a single surface to set of triangles approximating it.  M
*   FineNess is a finess control on result and the larger it is more         M
* triangles may result.							     M
*   A value of 10 is a good starting value.				     M
* NULL is returned in case of an error, otherwise list of CagdPolygonStruct. M
*                                                                            *
* PARAMETERS:                                                                M
*   Srf:              To approximate into triangles.                         M
*   FineNess:         Control on accuracy, the higher the finer.             M
*   ComputeNormals:   If TRUE, normal information is also computed.          M
*   FourPerFlat:      If TRUE, four triangles are created per flat surface.  M
*                     If FALSE, only 2 triangles are created.                M
*   ComputeUV:        If TRUE, UV values are stored and returned as well.    M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdPolygonStruct *:   A list of polygons with optional normal and/or    M
*                         UV parametric information.                         M
*                         NULL is returned in case of an error.              M
*                                                                            *
* KEYWORDS:                                                                  M
*   SymbSrf2Polygons, polygonization, surface approximation                  M
*****************************************************************************/
CagdPolygonStruct *SymbSrf2Polygons(CagdSrfStruct *Srf,
				    int FineNess,
				    CagdBType ComputeNormals,
				    CagdBType FourPerFlat,
				    CagdBType ComputeUV)
{
    /* Make sure we do not deal with constant surfaces. */
    if (Srf -> UOrder < 2 || Srf -> VOrder < 2) {
        SYMB_FATAL_ERROR(SYMB_ERR_POLY_CONST_SRF);
	return NULL;
    }


    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrf2Polygons(Srf, FineNess, ComputeNormals, FourPerFlat,
								   ComputeUV);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrf2Polygons(Srf, FineNess, ComputeNormals, FourPerFlat,
								   ComputeUV);
	case CAGD_SPOWER_TYPE:
	    SYMB_FATAL_ERROR(SYMB_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    SYMB_FATAL_ERROR(SYMB_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to convert a single surface to NumOfIsolines polylines in each   M
* parametric direction with SamplesPerCurve in each isoparametric curve.     M
*   Polyline are always E3 of CagdPolylineStruct type.			     M
*   NULL is returned in case of an error, otherwise list of                  M
* CagdPolylineStruct. Attempt is made to extract isolines along C1           M
* discontinuities first.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   Srf:                 Srf to extract isoparametric curves from.           M
*   NumOfIsocurves:      To extarct from Srf in each (U or V) direction.     M
*   SamplesPerCurve:     Fineness control on piecewise linear curve          M
*                        approximation.                                      M
*   Optimal:		 Use optimal approximation of isocurves.	     M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdPolylineStruct *: List of polylines representing a piecewise linear  M
*                         approximation of the extracted isoparamteric       M
*                         curves or NULL is case of an error.                M
*                                                                            *
* KEYWORDS:                                                                  M
*   SymbSrf2Polylines, polylines, isoparametric curves                       M
*****************************************************************************/
CagdPolylineStruct *SymbSrf2Polylines(CagdSrfStruct *Srf,
				      int NumOfIsocurves[2],
				      int SamplesPerCurve,
				      int Optimal)
{
    if (Optimal) {
	CagdCrvStruct *Crv,
	    *Crvs = SymbSrf2Curves(Srf, NumOfIsocurves);
	CagdPolylineStruct *Poly,
	    *Polys = NULL;

	for (Crv = Crvs; Crv != NULL; Crv = Crv -> Pnext) {
	    Poly = SymbCrv2OptimalPolyline(Crv, SamplesPerCurve);
	    Poly -> Pnext = Polys;
	    Polys = Poly;
	}

	CagdCrvFreeList(Crvs);
	return Polys;	
    }
	

    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrf2Polylines(Srf, NumOfIsocurves, SamplesPerCurve);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrf2Polylines(Srf, NumOfIsocurves, SamplesPerCurve);
	case CAGD_SPOWER_TYPE:
	    SYMB_FATAL_ERROR(SYMB_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    SYMB_FATAL_ERROR(SYMB_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to extract from a surface NumOfIsoline isocurve list	     M
* in each param. direction.						     M
*   Iso parametric curves are sampled equally spaced in parametric space.    M
*   NULL is returned in case of an error, otherwise list of CagdCrvStruct.   M
*                                                                            *
* PARAMETERS:                                                                M
*   Srf:             To extract isoparametric curves from.                   M
*   NumOfIsocurves:  In each (U or V) direction.                             M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdCrvStruct *:  List of extracted isoparametric curves. These curves   M
*                     inherit the order and continuity of the original Srf.  M
*                     NULL is returned in case of an error.                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   SymbSrf2Curves, curves, isoparametric curves                             M
*****************************************************************************/
CagdCrvStruct *SymbSrf2Curves(CagdSrfStruct *Srf, int NumOfIsocurves[2])
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrf2Curves(Srf, NumOfIsocurves);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrf2Curves(Srf, NumOfIsocurves);
	case CAGD_SPOWER_TYPE:
	    SYMB_FATAL_ERROR(SYMB_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    SYMB_FATAL_ERROR(SYMB_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to approx. a single curve as a polyline with SamplesPerCurve     M
* samples. Polyline is always E3 CagdPolylineStruct type.		     M
*   NULL is returned in case of an error, otherwise CagdPolylineStruct.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   Crv:              To approximate as a polyline.                          M
*   SamplesPerCurve:  Number of samples to approximate with.                 M
*   Optimal:	      If TRUE, yse optimal approximation of isocurves.	     M
*                     Otherwise, the curve is sampled using equally spaced   M
*		      samples in parametric space.			     M
*   OptiLin:          If TRUE, optimize linear curves.			     M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdPolylineStruct *:  A polyline representing the piecewise linear      M
*                          approximation from, or NULL in case of an error.  M
*                                                                            *
* KEYWORDS:                                                                  M
*   SymbCrv2Polyline, piecewise linear approximation, polyline               M
*****************************************************************************/
CagdPolylineStruct *SymbCrv2Polyline(CagdCrvStruct *Crv,
				     int SamplesPerCurve,
				     CagdBType Optimal,
				     CagdBType OptiLin)
{
    if (Optimal)
	return SymbCrv2OptimalPolyline(Crv, SamplesPerCurve);

    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrv2Polyline(Crv, SamplesPerCurve);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrv2Polyline(Crv, SamplesPerCurve, NULL, OptiLin);
	case CAGD_CPOWER_TYPE:
	    SYMB_FATAL_ERROR(SYMB_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    SYMB_FATAL_ERROR(SYMB_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Routine to convert a single curve to polyline with SamplesPerCurve	     *
* samples.								     *
*   Polyline is always E3 of CagdPolylineStruct type.		 	     *
*   Curve is sampled at optimal locations as to minimize the L-infinity	     *
* error between the curve and its polyline approximation.		     *
*   NULL is returned in case of an error, otherwise CagdPolylineStruct.	     *
*                                                                            *
* PARAMETERS:                                                                *
*   Crv:                To approiximate as a polyline.                       *
*   SamplesPerCurve:    Number of samples one can take off the curve.        *
*                                                                            *
* RETURN VALUE:                                                              *
*   CagdPolylineStruct *:   A polyline with SamplesPerCurve samples that     *
*                           approixmates Crv.                                *
*****************************************************************************/
static CagdPolylineStruct *SymbCrv2OptimalPolyline(CagdCrvStruct *Crv,
						   int SamplesPerCurve)
{
    int i;
    CagdRType *TVals;
    CagdPolylineStruct
	*P = CagdPolylineNew(SamplesPerCurve);
    CagdCrvStruct *CTmp;
    CagdPolylnStruct
	*NewPolyline = P -> Polyline;

    GlblCrvtrCrv = SymbCrv3DCurvatureSqr(Crv);
    CTmp = CagdCrvDerive(Crv);
    GlblDeriv1MagSqrCrv = SymbCrvDotProd(CTmp, CTmp);
    CagdCrvDomain(Crv, &GlblCrvtrCrvTMin, &GlblCrvtrCrvTMax);

    TVals = DistPoint1DWithEnergy(SamplesPerCurve,
				  GlblCrvtrCrvTMin, GlblCrvtrCrvTMax,
				  CURVE_PTS_DIST_RESOLUTION,
				  CrvCurvatureEvalFunc);

    for (i = 0; i < SamplesPerCurve; i++) {	  /* Convert to E3 polyline. */
	RealType
	    *R = CagdCrvEval(Crv, TVals[i]);

	CagdCoerceToE3(NewPolyline[i].Pt, &R, -1, Crv -> PType);
    }

    CagdCrvFree(GlblCrvtrCrv);
    CagdCrvFree(GlblDeriv1MagSqrCrv);
    IritFree((VoidPtr) TVals);

    return P;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Evaluation function of curvature square. This function should return the   *
* square root of the curvature, scaled by the arclength using GlblCrvtrCrv   *
* GlblDeriv1MagSqrCrv that contain curvature square and arclength sqaure.    *
*                                                                            *
* PARAMETERS:                                                                *
*   t:          Parameter at which to evalate the global curvature field.    *
*                                                                            *
* RETURN VALUE:                                                              *
*   RealType:    Rstimated curvature square.                                 *
*****************************************************************************/
static RealType CrvCurvatureEvalFunc(RealType t)
{
    CagdRType CrvtrSqr, MagSqr, *R;

    if (t < GlblCrvtrCrvTMin)
	t = GlblCrvtrCrvTMin;
    if (t > GlblCrvtrCrvTMax)
	t = GlblCrvtrCrvTMax;

    R = CagdCrvEval(GlblCrvtrCrv, t);
    CrvtrSqr = fabs( R[1] / R[0] );
    R = CagdCrvEval(GlblDeriv1MagSqrCrv, t);
    MagSqr = GlblDeriv1MagSqrCrv -> PType == CAGD_PT_E1_TYPE ? R[1]
							     : R[1] / R[0];

    return sqrt( sqrt( CrvtrSqr ) * MagSqr );
}
