/******************************************************************************
* Bsp2Poly.c - Bezier to polygon/polylines conversion routines.		      *
*******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                 *
*******************************************************************************
* Written by Gershon Elber, Mar. 90.					      *
******************************************************************************/

#include "cagd_loc.h"

static CagdSrfErrorFuncType
    BspSrf2PolygonErrFunc = NULL;

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Sets the surface approximation error function.  The error function       M
* will return a negative value if this patch must be purged or otherwise a   M
* non negative error measure.                                                M
*                                                                            *
* PARAMETERS:                                                                M
*   Func:        New function to use, NULL to disable.                       M
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdPlErrorFuncType:  Old value of function.                             M
*                                                                            *
* SEE ALSO:                                                                  M
*   BspSrf2Polygons                                                          M
*                                                                            *
* KEYWORDS:                                                                  M
*   BspSrf2PolygonSetErrFunc                                                 M
*****************************************************************************/
CagdSrfErrorFuncType BspSrf2PolygonSetErrFunc(CagdSrfErrorFuncType Func)
{
    CagdSrfErrorFuncType
	OldFunc = BspSrf2PolygonErrFunc;

    BspSrf2PolygonErrFunc = Func;

    return OldFunc;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to convert a single Bspline surface to set of triangles	     M
* approximating it. FineNess is a fineness control on result and the larger  M
t is more triangles may result. A value of 10 is a good start value.	     M
* NULL is returned in case of an error, otherwise list of CagdPolygonStruct. M
*   This routine looks for C1 discontinuities in the surface and splits it   M
* into C1 continuous patches to invoke BspC1Srf2Polygons to gen. polygons.   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
*                                                                            *
* SEE ALSO:                                                                  M
*   BspSrf2PolygonSetErrFunc, BzrSrf2Polygons, IritSurface2Polygons,	     M
*   IritTrimSrf2Polygons, SymbSrf2Polygons, TrimSrf2Polygons,		     M
*   BspC1Srf2Polygons, CagdSrf2Polygons					     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BspSrf2Polygons, polygonization, surface approximation                   M
*****************************************************************************/
CagdPolygonStruct *BspSrf2Polygons(CagdSrfStruct *Srf,
				   int FineNess,
				   CagdBType ComputeNormals,
				   CagdBType FourPerFlat,
				   CagdBType ComputeUV)
{
    CagdBType HasUDiscont, HasVDiscont,
	NewSrf = FALSE;
    int ULength, VLength,
	UOrder = Srf -> UOrder,
	VOrder = Srf -> VOrder;
    CagdRType u, v;
    CagdPolygonStruct *Poly;

    if (CAGD_IS_PERIODIC_SRF(Srf)) {
        NewSrf = TRUE;
        Srf = CnvrtPeriodic2FloatSrf(Srf);
    }

    ULength = Srf -> ULength;
    VLength = Srf -> VLength;
    HasUDiscont = BspKnotC1Discont(Srf -> UKnotVector, UOrder, ULength, &u);
    HasVDiscont = BspKnotC1Discont(Srf -> VKnotVector, VOrder, VLength, &v);

    if (HasUDiscont || HasVDiscont) {
	CagdSrfStruct
	    *Srf1 = HasUDiscont ? BspSrfSubdivAtParam(Srf, u,
						      CAGD_CONST_U_DIR)
				: BspSrfSubdivAtParam(Srf, v,
						      CAGD_CONST_V_DIR),
	    *Srf2 = Srf1 -> Pnext;
	CagdPolygonStruct *Poly1, *Poly2;

	Srf1 -> Attr = AttrCopyAttributes(Srf -> Attr);
	Srf2 -> Attr = AttrCopyAttributes(Srf -> Attr);

	Poly1 = BspSrf2Polygons(Srf1, FineNess,
				ComputeNormals, FourPerFlat, ComputeUV),
	Poly2 = BspSrf2Polygons(Srf2, FineNess,
				ComputeNormals, FourPerFlat, ComputeUV);

	CagdSrfFreeList(Srf1);

	/* Chain the two lists together: */
	if (Poly1 == NULL)
	    Poly = Poly2;
	else if (Poly2 == NULL)
	    Poly = Poly1;
	else {
	    for (Poly = Poly1; Poly -> Pnext != NULL; Poly = Poly -> Pnext);
	    Poly -> Pnext = Poly2;
	    Poly = Poly1;
	}
    }
    else {
	if (BspSrf2PolygonErrFunc != NULL &&
	    BspSrf2PolygonErrFunc(Srf) < 0.0)
	    Poly = NULL;
	else
	    Poly = BspC1Srf2Polygons(Srf, FineNess, ComputeNormals,
				     FourPerFlat, ComputeUV);
    }

    if (NewSrf)
	CagdSrfFree(Srf);

    return Poly;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to convert a single C1 continuouse Bspline srf to a set of       M
* triangles approximating it. FineNess is a finess control on result and the M
* larger it is more triangles may result. A value of 10 is a good starting   M
* value. NULL is returned in case of an error, otherwise list of             M
* 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
*                                                                            *
* SEE ALSO:                                                                  M
*   BzrSrf2Polygons, IritSurface2Polygons, IritTrimSrf2Polygons,             M
*   SymbSrf2Polygons, TrimSrf2Polygons			                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   BspC1Srf2Polygons, polygonization, surface approximation                 M
*****************************************************************************/
CagdPolygonStruct *BspC1Srf2Polygons(CagdSrfStruct *Srf,
				     int FineNess,
				     CagdBType ComputeNormals,
				     CagdBType FourPerFlat,
				     CagdBType ComputeUV)
{
    int i, j, FineNessU1, FineNessV1, FineNessU, FineNessV, BaseIndex;
    CagdRType t, u, v, UMin, UMax, VMin, VMax, *Pt;
    CagdPointType
	PType = Srf -> PType;
    CagdPtStruct PtCenter, *Pt1, *Pt2, *Pt3, *Pt4, *PtMesh, *PtMeshPtr;
    CagdUVStruct UVCenter,
	*UVMeshPtr = NULL,
	*UV1 = NULL,
	*UV2 = NULL,
	*UV3 = NULL,
	*UV4 = NULL,
	*UVMesh = NULL;
    CagdVecStruct NlCenter,
	*Nl1 = NULL,
	*Nl2 = NULL,
	*Nl3 = NULL,
	*Nl4 = NULL,
	*PtNrml = NULL;
    CagdCrvStruct *Crv;
    CagdPolygonStruct *Poly,
	*PolyHead = NULL;

    if (!CAGD_IS_BSPLINE_SRF(Srf))
	return NULL;

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

    if ((t = AttrGetRealAttrib(Srf -> Attr, "u_resolution"))
							< IP_ATTR_BAD_REAL)
        FineNessU = (int) (FineNessU * t);
    if ((t = AttrGetRealAttrib(Srf -> Attr, "v_resolution"))
							< IP_ATTR_BAD_REAL)
        FineNessV = (int) (FineNessV * t);

    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;

    /* Current to surface property such as curvature is used as subdivison   */
    /* criterion and the surface is subdivided, equally spaced in parametric */
    /* space, using FineNess as number of subdivisions per axis.	     */

    /* 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 = CagdPtArrayNew(FineNessU * FineNessV);

    if (ComputeUV)
	UVMeshPtr = UVMesh = CagdUVArrayNew(FineNessU * FineNessV);

    BspSrfDomain(Srf, &UMin, &UMax, &VMin, &VMax);

    for (i = 0; i < FineNessU; i++) {
	u = UMin + (UMax - UMin) * i / ((CagdRType) FineNessU1);
	if (u > UMax)                    /* Due to floating point round off. */
	    u = UMax;
	Crv = BspSrfCrvFromSrf(Srf, u, CAGD_CONST_U_DIR);

	for (j = 0; j < FineNessV; j++, PtMeshPtr++) {
	    v = VMin + (VMax - VMin) * j / ((CagdRType) FineNessV1);
	    if (v > VMax)                /* Due to floating point round off. */
	        v = VMax;
	    Pt = BspCrvEvalAtParam(Crv, v);
	    CagdCoerceToE3(PtMeshPtr -> Pt, &Pt, -1, PType);

	    if (ComputeUV) {
		UVMeshPtr -> UV[0] = u;
		UVMeshPtr -> UV[1] = v;
		UVMeshPtr++;
	    }
	}

	CagdCrvFree(Crv);
    }

    if (ComputeNormals) {
	if ((PtNrml = BspSrfMeshNormals(Srf, FineNessU, FineNessV)) == NULL)
	    ComputeNormals = FALSE;
    }

    /* 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. */
		CAGD_COPY_POINT(PtCenter, *Pt1);
		CAGD_ADD_POINT(PtCenter, *Pt2);
		CAGD_ADD_POINT(PtCenter, *Pt3);
		CAGD_ADD_POINT(PtCenter, *Pt4);
		CAGD_MULT_POINT(PtCenter, 0.25);

		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);
		if (Poly)
		    LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt2, Pt3, &PtCenter,
					Nl2, Nl3, &NlCenter,
					UV2, UV3, &UVCenter);
		if (Poly)
		    LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt3, Pt4, &PtCenter,
					Nl3, Nl4, &NlCenter,
					UV3, UV4, &UVCenter);
		if (Poly)
		    LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt4, Pt1, &PtCenter,
					Nl4, Nl1, &NlCenter,
					UV4, UV1, &UVCenter);
		if (Poly)
		    LIST_PUSH(Poly, PolyHead);
	    }
	    else {			   /* Only two along the diagonal... */
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt1, Pt2, Pt3,
					Nl1, Nl2, Nl3,
					UV1, UV2, UV3);
		if (Poly)
		    LIST_PUSH(Poly, PolyHead);
		Poly = _CagdMakePolygon(ComputeNormals, ComputeUV,
					Pt3, Pt4, Pt1,
					Nl3, Nl4, Nl1,
					UV3, UV4, UV1);
		if (Poly)
		    LIST_PUSH(Poly, PolyHead);
	    }
	}

    CagdPtArrayFree(PtMesh, FineNessU * FineNessV);
    if (ComputeNormals)
	CagdVecArrayFree(PtNrml, FineNessU * FineNessV);
    if (ComputeUV)
	CagdUVArrayFree(UVMesh, FineNessU * FineNessV);

    return PolyHead;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to convert a single Bspline surface to NumOfIsolines polylines   M
* in each parametric direction with SamplesPerCurve in each isoparametric    M
* curve.                                                                     M
*   Polyline are always E3 of CagdPolylineStruct type.			     M
*   Iso parametric curves are sampled equally spaced in parametric space.    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
*                                                                            *
* RETURN VALUE:                                                              M
*   CagdPolylineStruct *: List of polygons representing a piecewise linear   M
*                         approximation of the extracted isoparamteric       M
*                         curves or NULL is case of an error.                M
*                                                                            *
* SEE ALSO:                                                                  M
*   BspCrv2Polyline, BzrSrf2Polylines, IritSurface2Polylines,                M
*   IritTrimSrf2Polylines, SymbSrf2Polylines, TrimSrf2Polylines              M
*   CagdSrf2Polylines                                                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   BspSrf2Polylines, polylines, isoparametric curves                        M
*****************************************************************************/
CagdPolylineStruct *BspSrf2Polylines(CagdSrfStruct *Srf,
				     int NumOfIsocurves[2],
				     int SamplesPerCurve)
{
    CagdBType
	NewSrf = FALSE;
    int i, NumC1Disconts, NumOfIsos, ULength, VLength,
	UOrder = Srf -> UOrder,
	VOrder = Srf -> VOrder;
    CagdRType u, v, UMin, UMax, VMin, VMax, *C1Disconts, *IsoParams, *RefKV,
	*UKV, *VKV;
    CagdCrvStruct *Crv;
    CagdPolylineStruct *Poly,
	*PolyList = NULL;
    BspKnotAlphaCoeffType *A;

    if (!CAGD_IS_BSPLINE_SRF(Srf))
	return NULL;

    if (CAGD_IS_PERIODIC_SRF(Srf)) {
        NewSrf = TRUE;
        Srf = CnvrtPeriodic2FloatSrf(Srf);
    }

    UKV = Srf -> UKnotVector;
    VKV = Srf -> VKnotVector;
    ULength = Srf -> ULength;
    VLength = Srf -> VLength;

    /* Make sure the curve is open. We move 2 Epsilons to make sure region  */
    /* extraction will occur. Otherwise the curve will be copied as is.     */
    if (!BspKnotHasOpenEC(UKV, ULength, UOrder) ||
	!BspKnotHasOpenEC(VKV, VLength, VOrder)) {
	CagdSrfStruct
	    *TSrf = CagdSrfRegionFromSrf(Srf,
					 UKV[UOrder - 1],
					 UKV[ULength],
					 CAGD_CONST_U_DIR);

	if (NewSrf)
	    CagdSrfFree(Srf);

	Srf = CagdSrfRegionFromSrf(TSrf,
				   VKV[VOrder - 1],
				   VKV[VLength],
				   CAGD_CONST_V_DIR);
	NewSrf = TRUE;

	CagdSrfFree(TSrf);
    }

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

    BspSrfDomain(Srf, &UMin, &UMax, &VMin, &VMax);

    /* Compute discontinuities along the u axis and use that to determine    */
    /* where to extract isolines along u.				     */
    /* Note C1Disconts is freed by BspKnotParamValues.			     */

    /* Add heuristically more samples if surface has interior knots.         */
    NumOfIsos = NumOfIsocurves[0];
    if (UOrder > 2)
	NumOfIsos += (ULength - UOrder) / 2;

    C1Disconts = BspKnotAllC1Discont(Srf -> UKnotVector, UOrder,
				     ULength, &NumC1Disconts);
    IsoParams = BspKnotParamValues(UMin, UMax, NumOfIsos, C1Disconts,
								NumC1Disconts);
    RefKV = BspKnotPrepEquallySpaced(MAX(SamplesPerCurve - VLength, 1),
				     VMin, VMax);
    A = BspKnotEvalAlphaCoefMerge(VOrder, Srf -> VKnotVector, VLength, RefKV,
				  MAX(SamplesPerCurve - VLength, 1));
    IritFree((VoidPtr) RefKV);

    for (i = 0; i < NumOfIsos; i++) {
	u = IsoParams[i];

	Crv = BspSrfCrvFromSrf(Srf, u, CAGD_CONST_U_DIR);
	Poly = BspCrv2Polyline(Crv, SamplesPerCurve, A, TRUE);
	Poly -> Pnext = PolyList;
	PolyList = Poly;
	CagdCrvFree(Crv);
    }
    IritFree((VoidPtr) IsoParams);
    BspKnotFreeAlphaCoef(A);

    /* Compute discontinuities along the v axis and use that to determine    */
    /* where to extract isolines along v.				     */
    /* Note C1Disconts is freed by BspKnotParamValues.			     */

    /* Add heuristically more samples if surface has interior knots.         */
    NumOfIsos = NumOfIsocurves[1];
    if (VOrder > 2)
	NumOfIsos += (VLength - VOrder) / 2;

    C1Disconts = BspKnotAllC1Discont(Srf -> VKnotVector, VOrder,
				     VLength, &NumC1Disconts);
    IsoParams = BspKnotParamValues(VMin, VMax, NumOfIsos, C1Disconts,
								NumC1Disconts);
    RefKV = BspKnotPrepEquallySpaced(MAX(SamplesPerCurve - ULength, 1),
				     UMin, UMax);
    A = BspKnotEvalAlphaCoefMerge(UOrder, Srf -> UKnotVector, ULength, RefKV,
				  MAX(SamplesPerCurve - ULength, 1));
    IritFree((VoidPtr) RefKV);

    for (i = 0; i < NumOfIsos; i++) {
	v = IsoParams[i];

	Crv = BspSrfCrvFromSrf(Srf, v, CAGD_CONST_V_DIR);
	Poly = BspCrv2Polyline(Crv, SamplesPerCurve, A, TRUE);
	Poly -> Pnext = PolyList;
	PolyList = Poly;
	CagdCrvFree(Crv);
    }
    IritFree((VoidPtr) IsoParams);
    BspKnotFreeAlphaCoef(A);

    if (NewSrf)
	CagdSrfFree(Srf);

    return PolyList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to extract from a Bspline 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
* SEE ALSO:                                                                  M
*   BspSrf22Polylines, BzrSrf2PCurves, SymbSrf2Curves                        M
*                                                                            *
* KEYWORDS:                                                                  M
*   BspSrf2Curves, curves, isoparametric curves                              M
*****************************************************************************/
CagdCrvStruct *BspSrf2Curves(CagdSrfStruct *Srf, int NumOfIsocurves[2])
{
    int i, NumC1Disconts,
	ULength = Srf -> ULength,
	VLength = Srf -> VLength,
	UOrder = Srf -> UOrder,
	VOrder = Srf -> VOrder;
    CagdRType u, v, UMin, UMax, VMin, VMax, *C1Disconts, *IsoParams;
    CagdCrvStruct *Crv,
	*CrvList = NULL;

    if (!CAGD_IS_BSPLINE_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;

    BspSrfDomain(Srf, &UMin, &UMax, &VMin, &VMax);

    /* Compute discontinuities along the u axis and use that to determine    */
    /* where to extract isolines along u.				     */
    /* Note C1Disconts is freed by BspKnotParamValues.			     */
    C1Disconts = BspKnotAllC1Discont(Srf -> UKnotVector, UOrder,
				     ULength, &NumC1Disconts);
    IsoParams = BspKnotParamValues(UMin, UMax, NumOfIsocurves[0], C1Disconts,
								NumC1Disconts);

    for (i = 0; i < NumOfIsocurves[0]; i++) {
	u = IsoParams[i];

	Crv = BspSrfCrvFromSrf(Srf, u, CAGD_CONST_U_DIR);
	Crv -> Pnext = CrvList;
	CrvList = Crv;
    }
    IritFree((VoidPtr) IsoParams);

    /* Compute discontinuities along the v axis and use that to determine    */
    /* where to extract isolines along v.				     */
    /* Note C1Disconts is freed by BspKnotParamValues.			     */
    C1Disconts = BspKnotAllC1Discont(Srf -> VKnotVector, VOrder,
				     VLength, &NumC1Disconts);
    IsoParams = BspKnotParamValues(VMin, VMax, NumOfIsocurves[1], C1Disconts,
								NumC1Disconts);

    for (i = 0; i < NumOfIsocurves[1]; i++) {
	v = IsoParams[i];

	Crv = BspSrfCrvFromSrf(Srf, v, CAGD_CONST_V_DIR);
	Crv -> Pnext = CrvList;
	CrvList = Crv;
    }
    IritFree((VoidPtr) IsoParams);

    return CrvList;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Routine to approx. a single Bspline curve as a polyline with	     M
* SamplesPerCurve samples. Polyline is always E3 CagdPolylineStruct type.    M
*   Curve is refined equally spaced in parametric space, unless the curve is M
* linear in which the control polygon is simply being copied.		     M
*   If A is specified, it is used to refine the curve.			     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
*   A:                Alpha matrix (Oslo algorithm) if precumputed.          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
*                                                                            *
* SEE ALSO:                                                                  M
*   BzrCrv2Polyline, BspSrf2Polylines, IritCurve2Polylines,                  M
*   SymbCrv2Polyline						             M
*                                                                            *
* KEYWORDS:                                                                  M
*   BspCrv2Polyline, piecewise linear approximation, polyline                M
*****************************************************************************/
CagdPolylineStruct *BspCrv2Polyline(CagdCrvStruct *Crv,
				    int SamplesPerCurve,
				    BspKnotAlphaCoeffType *A,
				    CagdBType OptiLin)
{
    CagdBType
	NewCrv = FALSE;
    int i, j, n,
	Order = Crv -> Order,
	Len = Crv -> Length,
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv),
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType *Polyline[CAGD_MAX_PT_SIZE],
	*KV = Crv -> KnotVector;
    CagdPolylnStruct *NewPolyline;
    CagdPolylineStruct *P;

    if (!CAGD_IS_BSPLINE_CRV(Crv))
	return NULL;

    if (CAGD_IS_PERIODIC_CRV(Crv)) {
        Crv = CnvrtPeriodic2FloatCrv(Crv);
	Len += Order - 1;
	KV = Crv -> KnotVector;
	NewCrv = TRUE;
    }

    /* Make sure the curve is open. We move 2 Epsilons to make sure region  */
    /* extraction will occur. Otherwise the curve will be copied as is.     */
    if (!BspKnotHasOpenEC(KV, Len, Order)) {
	CagdCrvStruct
	    *TCrv = CagdCrvRegionFromCrv(Crv, KV[Order - 1], KV[Len]);

	if (NewCrv)
	    CagdCrvFree(Crv);
	Crv = TCrv;
	NewCrv = TRUE;
    }

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

    if (SamplesPerCurve <= Len || (Order == 2 && OptiLin)) {
	/* Make sure SamplesPerCurve can hold the entire control polygon. */
	SamplesPerCurve = Len + 1;
    }

    n = MAX(A ? A -> RefLength : 0, SamplesPerCurve);

    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;

    n = P -> Length = CagdCrvEvalToPolyline(Crv,
					    A == NULL ? n : 0,
					    Polyline, A, OptiLin);
    if (IsNotRational) {
	for (i = n - 1; i >= 0; i--) {	          /* Convert to E3 polyline. */
	    for (j = 0; j < MaxCoord; j++)
		NewPolyline[i].Pt[j] = Polyline[j + 1][i];
	    for (j = MaxCoord; j < 3; j++)
		NewPolyline[i].Pt[j] = 0.0;
	}
    }
    else {
	for (i = n - 1; i >= 0; i--) {	          /* Convert to E3 polyline. */
	    CagdRType
		PtW = Polyline[0][i] == 0 ? IRIT_UEPS : Polyline[0][i];

	    for (j = 0; j < MaxCoord; j++)
		NewPolyline[i].Pt[j] = Polyline[j + 1][i] / PtW;
	    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]);

    if (NewCrv)
	CagdCrvFree(Crv);

    return P;
}
