/******************************************************************************
* Cagd_aux.c - auxiliary routine to interface to different free from types.   *
*******************************************************************************
* Written by Gershon Elber, July. 90.					      *
******************************************************************************/

#include "cagd_loc.h"

/******************************************************************************
* Given a curve, returns its parametric domain.				      *
******************************************************************************/
void CagdCrvDomain(CagdCrvStruct *Crv, CagdRType *TMin, CagdRType *TMax)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    *TMin = 0.0;
	    *TMax = 1.0;
	    break;
	case CAGD_CBSPLINE_TYPE:
	    BspCrvDomain(Crv, TMin, TMax);
	    break;
	case CAGD_CPOWER_TYPE:
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    break;
    }
}

/******************************************************************************
* Given a crv and parameter value t, evaluate the curve at t.		      *
******************************************************************************/
CagdRType *CagdCrvEval(CagdCrvStruct *Crv, CagdRType t)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrvEvalAtParam(Crv, t);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvEvalAtParam(Crv, t);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Given a surface, returns its parametric domain.			      *
******************************************************************************/
void CagdSrfDomain(CagdSrfStruct *Srf, CagdRType *UMin, CagdRType *UMax,
				       CagdRType *VMin, CagdRType *VMax)
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    *UMin = 0.0;
	    *UMax = 1.0;
	    *VMin = 0.0;
	    *VMax = 1.0;
	    break;
	case CAGD_SBSPLINE_TYPE:
	    BspSrfDomain(Srf, UMin, UMax, VMin, VMax);
	    break;
	case CAGD_SPOWER_TYPE:
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    break;
    }
}

/******************************************************************************
* Given a srf and parameter values u,v, evaluate the surface at u,v.	      *
******************************************************************************/
CagdRType *CagdSrfEval(CagdSrfStruct *Srf, CagdRType u, CagdRType v)
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrfEvalAtParam(Srf, u, v);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrfEvalAtParam(Srf, u, v);
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/******************************************************************************
* Given a curve, compute its derivative curve.				      *
******************************************************************************/
CagdCrvStruct *CagdCrvDerive(CagdCrvStruct *Crv)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrvDerive(Crv);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvDerive(Crv);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Given a curve, compute its integral as a curve.			      *
******************************************************************************/
CagdCrvStruct *CagdCrvIntegrate(CagdCrvStruct *Crv)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrvIntegrate(Crv);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvIntegrate(Crv);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Given a planar curve, compute its enclosed area as a curve.		      *
******************************************************************************/
CagdCrvStruct *CagdCrvEnclosedArea(CagdCrvStruct *Crv)
{
    CagdVType Trans;
    CagdCrvStruct *CrvX, *CrvY, *CrvZ, *CrvW, *DCrvX, *DCrvY,
	*CTmp1, *CTmp2, *CTmp3;

    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	case CAGD_CBSPLINE_TYPE:
	    break;
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }

    /* Using Green's theorem, if C(t1) = C(t2) the area enclosed is equal to */
    /*									     */
    /*	   t2								     */
    /*	    /								     */
    /*	1  |								     */
    /*  -  | -x'(t) y(t) + x(t) y'(t) dt				     */
    /*	2  |								     */
    /*    /								     */
    /*    t1								     */
    /*									     */
    CagdCrvSplitScalar(Crv, &CrvW, &CrvX, &CrvY, &CrvZ);
    if (CrvZ)
        CagdCrvFree(CrvZ);
    if (CrvW) {
	FATAL_ERROR(CAGD_ERR_RATIONAL_NO_SUPPORT);
	CagdCrvFree(CrvW);
    }

    DCrvX = CagdCrvDerive(CrvX);
    DCrvY = CagdCrvDerive(CrvY);
    CTmp1 = CagdCrvMult(CrvX, DCrvY);
    CTmp2 = CagdCrvMult(DCrvX, CrvY);
    CagdCrvFree(CrvX);
    CagdCrvFree(CrvY);
    CagdCrvFree(DCrvX);
    CagdCrvFree(DCrvY);
    CTmp3 = CagdCrvSub(CTmp1, CTmp2);
    CagdCrvFree(CTmp1);
    CagdCrvFree(CTmp2);
    CTmp1 = CagdCrvIntegrate(CTmp3);
    CagdCrvFree(CTmp3);

    /* Scale by a half. */
    Trans[0] = Trans[1] = Trans[2] = 0.0;
    CagdCrvTransform(CTmp1, Trans, 0.5);

    return CTmp1;
}

/******************************************************************************
* Given a surface, compute its partial derivative surface in direction Dir.   *
******************************************************************************/
CagdSrfStruct *CagdSrfDerive(CagdSrfStruct *Srf, CagdSrfDirType Dir)
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrfDerive(Srf, Dir);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrfDerive(Srf, Dir);
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/******************************************************************************
* Given a curve - subdivide it into two at the given parametric value.        *
* Returns pointer to first curve in a list of two curves (subdivided ones).   *
******************************************************************************/
CagdCrvStruct *CagdCrvSubdivAtParam(CagdCrvStruct *Crv, CagdRType t)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrvSubdivAtParam(Crv, t);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvSubdivAtParam(Crv, t);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Given a curve - returns a sub region within the specified domain.           *
******************************************************************************/
CagdCrvStruct *CagdCrvRegionFromCrv(CagdCrvStruct *Crv, CagdRType t1,
							CagdRType t2)
{
    CagdRType TMin, TMax;
    CagdCrvStruct *Crvs;
    CagdBType
	NewCrv = FALSE;

    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    TMin = 0.0;
	    TMax = 1.0;
	    break;
	case CAGD_CBSPLINE_TYPE:
    	    BspCrvDomain(Crv, &TMin, &TMax);
	    break;
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }

    if (t1 > t2)
	SWAP(CagdRType, t1, t2);

    if (!APX_EQ(t1, TMin)) {
    	Crvs = CagdCrvSubdivAtParam(Crv, t1);
	Crv = Crvs -> Pnext;
	Crvs -> Pnext = NULL;
	CagdCrvFree(Crvs);			   /* Free the first region. */
	NewCrv = TRUE;
    }

    if (APX_EQ(t2, TMax))
	return NewCrv ? Crv : CagdCrvCopy(Crv);
    else {
	Crvs = CagdCrvSubdivAtParam(Crv, t2);

	if (NewCrv)
	    CagdCrvFree(Crv);

    	CagdCrvFree(Crvs -> Pnext);		  /* Free the second region. */
    	Crvs -> Pnext = NULL;
    	return Crvs;				/* Returns the first region. */
    }
}

/******************************************************************************
* Given a curve - refine it at the given n knots as defined by vector t.      *
* If Replace is TRUE, the values replaces current vector instead.	      *
* Returns pointer to refined crv (Note Bezier will be converted into spline). *
******************************************************************************/
CagdCrvStruct *CagdCrvRefineAtParams(CagdCrvStruct *Crv, CagdBType Replace,
							 CagdRType *t, int n)
{
    CagdCrvStruct *BspCrv, *TCrv;

    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
    	    BspCrv = CnvrtBezier2BsplineCrv(Crv);
	    TCrv = BspCrvKnotInsertNDiff(BspCrv, Replace, t, n);
	    CagdCrvFree(BspCrv);
	    return TCrv;
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvKnotInsertNDiff(Crv, Replace, t, n);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Return a new curve reversed by reversing the ctl polygon.                   *
******************************************************************************/
CagdCrvStruct *CagdCrvReverse(CagdCrvStruct *Crv)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, Len, Col,
	Length = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdCrvStruct
        *ReversedCrv = CagdCrvCopy(Crv);
    CagdRType *KV,
	**Points = ReversedCrv -> Points;

    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	case CAGD_CBSPLINE_TYPE:
	    break;
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }

    /* Reverse the Ctl Polygon: */
    Len = Length / 2;
    for (Col = 0; Col < Len; Col++)
	for (i = IsNotRational; i <= MaxCoord; i++)
	    SWAP(CagdRType,
		 Points[i][Col],
		 Points[i][Length - Col - 1]);

    /* Reverse the knot vector if it exists: */
    if (Crv -> GType == CAGD_CBSPLINE_TYPE &&
	Crv -> KnotVector != NULL) {
	KV = BspKnotReverse(Crv -> KnotVector, Crv -> Order + Length);
	IritFree((VoidPtr) ReversedCrv -> KnotVector);
	ReversedCrv -> KnotVector = KV;
    }

    return ReversedCrv;
}

/******************************************************************************
* Return a new curve representing the same curve but with degree raised.      *
******************************************************************************/
CagdCrvStruct *CagdCrvDegreeRaise(CagdCrvStruct *Crv)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrvDegreeRaise(Crv);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvDegreeRaise(Crv);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Return a new curve representing the same curve but with degree raised.      *
******************************************************************************/
CagdCrvStruct *CagdCrvDegreeRaiseN(CagdCrvStruct *Crv, int NewOrder)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrvDegreeRaiseN(Crv, NewOrder);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvDegreeRaiseN(Crv, NewOrder);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Return a new surface representing the same surface but with degree raised.  *
******************************************************************************/
CagdSrfStruct *CagdSrfDegreeRaise(CagdSrfStruct *Srf, CagdSrfDirType Dir)
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrfDegreeRaise(Srf, Dir);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrfDegreeRaise(Srf, Dir);
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/******************************************************************************
* Extracts an iso curve from the surface as spcified by Dir and t.	      *
******************************************************************************/
CagdCrvStruct *CagdCrvFromSrf(CagdSrfStruct *Srf, CagdRType t,
							CagdSrfDirType Dir)
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrfCrvFromSrf(Srf, t, Dir);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrfCrvFromSrf(Srf, t, Dir);
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/******************************************************************************
* Extracts a curve from the surface as spcified by Dir and Index.	      *
*   The curve is constructed as a single ROW/COL of the original surface.     *
*   Note this curve may be not on the surface!				      *
******************************************************************************/
CagdCrvStruct *CagdCrvFromMesh(CagdSrfStruct *Srf, int Index,
							CagdSrfDirType Dir)
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrfCrvFromMesh(Srf, Index, Dir);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrfCrvFromMesh(Srf, Index, Dir);
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/******************************************************************************
* Substitue a row/col from the given curve into the surface as spcified by    *
* Dir and Index. Curve must have same PtType/Length and the surface row/col.  *
******************************************************************************/
void CagdCrvToMesh(CagdCrvStruct *Crv, int Index,
					CagdSrfDirType Dir, CagdSrfStruct *Srf)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_SRF(Srf);
    int i, j,
	Length = Crv -> Length,
	ULength = Srf -> ULength,
	VLength = Srf -> VLength,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Srf -> PType);
    CagdRType *CrvP, *SrfP;

    if (Crv -> PType != Srf -> PType ||
	Length != (Dir == CAGD_CONST_U_DIR ? VLength : ULength))
	FATAL_ERROR(CAGD_ERR_PT_OR_LEN_MISMATCH);

    switch (Dir) {
	case CAGD_CONST_U_DIR:
	    if (Index + 1 > ULength)
		FATAL_ERROR(CAGD_ERR_INDEX_NOT_IN_MESH);

	    for (i = IsNotRational; i <= MaxCoord; i++) {
		CrvP = Crv -> Points[i];
		SrfP = Srf -> Points[i] + Index * CAGD_NEXT_U(Srf);
		for (j = 0; j < Length; j++) {
		    *SrfP = *CrvP++;
		    SrfP += CAGD_NEXT_V(Srf);
		}
	    }
	    break;
	case CAGD_CONST_V_DIR:
	    if (Index + 1 > VLength)
		FATAL_ERROR(CAGD_ERR_INDEX_NOT_IN_MESH);

	    for (i = IsNotRational; i <= MaxCoord; i++) {
		CrvP = Crv -> Points[i];
		SrfP = Srf -> Points[i] + Index * CAGD_NEXT_V(Srf);;
		for (j = 0; j < Length; j++) {
		    *SrfP = *CrvP++;
		    SrfP += CAGD_NEXT_U(Srf);
		}
	    }
	    break;
	default:
	    FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
	    break;
    }
}

/******************************************************************************
* Given a srf - subdivide it into two at given parametric value in given dir. *
* Returns pointer to first srf in a list of two srfs (subdivided ones).       *
******************************************************************************/
CagdSrfStruct *CagdSrfSubdivAtParam(CagdSrfStruct *Srf, CagdRType t,
							    CagdSrfDirType Dir)
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrfSubdivAtParam(Srf, t, Dir);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrfSubdivAtParam(Srf, t, Dir);
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/******************************************************************************
* Given a surface - returns a sub region within the specified domain, dir.    *
******************************************************************************/
CagdSrfStruct *CagdSrfRegionFromSrf(CagdSrfStruct *Srf, CagdRType t1,
					      CagdRType t2, CagdSrfDirType Dir)
{
    CagdRType TMin, TMax, R1, R2;
    CagdSrfStruct *Srfs;
    CagdBType
	NewSrf = FALSE;

    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    TMin = 0.0;
	    TMax = 1.0;
	    break;
	case CAGD_SBSPLINE_TYPE:
    	    if (Dir == CAGD_CONST_U_DIR)
		BspSrfDomain(Srf, &TMin, &TMax, &R1, &R2);
	    else
		BspSrfDomain(Srf, &R1, &R2, &TMin, &TMax);
	    break;
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }

    if (t1 > t2)
	SWAP(CagdRType, t1, t2);

    if (!APX_EQ(t1, TMin)) {
	Srfs = CagdSrfSubdivAtParam(Srf, t1, Dir);
	Srf = Srfs -> Pnext;
	Srfs -> Pnext = NULL;
	CagdSrfFree(Srfs);			   /* Free the first region. */
	NewSrf = TRUE;
    }

    if (APX_EQ(t2, TMax))
	return NewSrf ? Srf : CagdSrfCopy(Srf);
    else {
	Srfs = CagdSrfSubdivAtParam(Srf, t2, Dir);

	if (NewSrf)
	    CagdSrfFree(Srf);

    	CagdSrfFree(Srfs -> Pnext);		  /* Free the second region. */
    	Srfs -> Pnext = NULL;
	return Srfs;				/* Returns the first region. */
    }
}

/******************************************************************************
* Given a surface - refine it at the given n knots as defined by vector t.    *
* If Replace is TRUE, the values replaces current vector instead.	      *
* Returns pointer to refined srf (Note Bezier will be converted into spline). *
******************************************************************************/
CagdSrfStruct *CagdSrfRefineAtParams(CagdSrfStruct *Srf, CagdSrfDirType Dir,
					CagdBType Replace, CagdRType *t, int n)
{
    CagdSrfStruct *BspSrf, *TSrf;

    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
    	    BspSrf = CnvrtBezier2BsplineSrf(Srf);
	    TSrf = BspSrfKnotInsertNDiff(BspSrf, Dir, Replace, t, n);
	    CagdSrfFree(BspSrf);
	    return TSrf;
	case CAGD_SBSPLINE_TYPE:
	    return BspSrfKnotInsertNDiff(Srf, Dir, Replace, t, n);
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/******************************************************************************
* Given a curve and a parameter value t, returns the tangent direction at t.  *
******************************************************************************/
CagdVecStruct *CagdCrvTangent(CagdCrvStruct *Crv, CagdRType t)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrvTangent(Crv, t);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvTangent(Crv, t);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Given a curve and a parameter value t, returns the binormal direction at t. *
******************************************************************************/
CagdVecStruct *CagdCrvBiNormal(CagdCrvStruct *Crv, CagdRType t)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrvBiNormal(Crv, t);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvBiNormal(Crv, t);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Given a curve and a parameter value t, returns the normal direction at t.   *
******************************************************************************/
CagdVecStruct *CagdCrvNormal(CagdCrvStruct *Crv, CagdRType t)
{
    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    return BzrCrvNormal(Crv, t);
	case CAGD_CBSPLINE_TYPE:
	    return BspCrvNormal(Crv, t);
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }
}

/******************************************************************************
* Given a srf and parameter value u,v, returns the tangent direction in Dir.  *
******************************************************************************/
CagdVecStruct *CagdSrfTangent(CagdSrfStruct *Srf, CagdRType u, CagdRType v,
							    CagdSrfDirType Dir)
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrfTangent(Srf, u, v, Dir);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrfTangent(Srf, u, v, Dir);
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/******************************************************************************
* Given a srf and parameter values u,v, returns the surface normal at u,v.    *
******************************************************************************/
CagdVecStruct *CagdSrfNormal(CagdSrfStruct *Srf, CagdRType u, CagdRType v)
{
    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	    return BzrSrfNormal(Srf, u, v);
	case CAGD_SBSPLINE_TYPE:
	    return BspSrfNormal(Srf, u, v);
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }
}

/******************************************************************************
* Return a new reversed surface by reversing in U direction the control mesh. *
******************************************************************************/
CagdSrfStruct *CagdSrfReverse(CagdSrfStruct *Srf)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_SRF(Srf);
    int i, Len, Row, Col,
	ULength = Srf -> ULength,
	VLength = Srf -> VLength,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Srf -> PType);
    CagdSrfStruct
	*ReversedSrf = CagdSrfCopy(Srf);
    CagdRType *KV,
	**Points = ReversedSrf -> Points;

    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	case CAGD_SBSPLINE_TYPE:
	    break;
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }

    /* Reverse the Mesh: */
    Len = ULength / 2;
    for (Row = 0; Row < VLength; Row++)
	for (Col = 0; Col < Len; Col++)
	    for (i = IsNotRational; i <= MaxCoord; i++)
		SWAP(CagdRType,
		     Points[i][Row * ULength + Col],
		     Points[i][Row * ULength + ULength - Col - 1]);

    /* Reverse the U knot vector if it exists: */
    if (Srf -> GType == CAGD_SBSPLINE_TYPE &&
	Srf -> UKnotVector != NULL) {
	KV = BspKnotReverse(Srf -> UKnotVector, Srf -> UOrder + ULength);
	IritFree((VoidPtr) ReversedSrf -> UKnotVector);
	ReversedSrf -> UKnotVector = KV;
    }

    return ReversedSrf;
}

/******************************************************************************
* Return a new reversed surface by flipping the U and V directions.           *
******************************************************************************/
CagdSrfStruct *CagdSrfReverse2(CagdSrfStruct *Srf)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_SRF(Srf);
    int i, Row, Col,
	ULength = Srf -> ULength,
	VLength = Srf -> VLength,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Srf -> PType);
    CagdSrfStruct
	*ReversedSrf = CagdSrfCopy(Srf);
    CagdRType
	**Points = Srf -> Points,
	**RevPoints = ReversedSrf -> Points;

    switch (Srf -> GType) {
	case CAGD_SBEZIER_TYPE:
	case CAGD_SBSPLINE_TYPE:
	    break;
	case CAGD_SPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_SRF);
	    return NULL;
    }

    /* Reverse the Mesh: */
    for (Row = 0; Row < VLength; Row++)
	for (Col = 0; Col < ULength; Col++)
	    for (i = IsNotRational; i <= MaxCoord; i++)
		RevPoints[i][Col * VLength + Row] =
		    Points[i][Row * ULength + Col];

    /* Swap the U and the V knot vectors if the exists: */
    if (Srf -> GType == CAGD_SBSPLINE_TYPE) {
	SWAP(CagdRType *,
	     ReversedSrf -> UKnotVector, ReversedSrf -> VKnotVector);
    }

    /* And swap the orders and lengths. */
    SWAP(int, ReversedSrf -> UOrder, ReversedSrf -> VOrder);
    SWAP(int, ReversedSrf -> ULength, ReversedSrf -> VLength);

    return ReversedSrf;
}
