/******************************************************************************
* SBsp-Aux.c - Bspline surface auxilary routines.			      *
*******************************************************************************
* Written by Gershon Elber, July. 90.					      *
******************************************************************************/

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "cagd_loc.h"

/* Define some marcos to make some of the routines below look better. They  */
/* calculate the index of the U, V point of the control mesh in Points.	    */
#define DERIVED_SRF(U, V)	CAGD_MESH_UV(DerivedSrf, U, V)
#define RAISED_SRF(U, V)	CAGD_MESH_UV(RaisedSrf, U, V)
#define SRF(U, V)		CAGD_MESH_UV(Srf, U, V)

/******************************************************************************
* Given a bspline surface - subdivide it into two at given parametric value.  *
* Returns pointer to first surface in a list of two srfs (subdivided ones).   *
* The subdivision is exact result of evaluating the surface int a curve at t  *
* using the recursive algorithm - the left resulting points is left surface,  *
* and the right resulting points is right surface (left is below t).	      *
******************************************************************************/
CagdSrfStruct *BspSrfSubdivAtParam(CagdSrfStruct *Srf, CagdRType t,
							CagdSrfDirType Dir)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Srf);
    int i, j, Row, Col, KVLen, Index1, Index2, Mult,
	LULength, RULength, LVLength, RVLength,
	ULength = Srf -> ULength,
	VLength = Srf -> VLength,
	UOrder = Srf -> UOrder,
	VOrder = Srf -> VOrder,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Srf -> PType);
    CagdRType *RefKV, **Pts, **LPts, **RPts, *LOnePts, *ROnePts, *OnePts;
    CagdSrfStruct *RSrf, *LSrf;
    BspKnotAlphaCoeffType *A;

    switch (Dir) {
	case CAGD_CONST_U_DIR:
	    RefKV = Srf -> UKnotVector;
	    KVLen = UOrder + ULength;
	    Index1 = BspKnotLastIndexL(RefKV, KVLen, t);
	    Index2 = BspKnotFirstIndexG(RefKV, KVLen, t);
	    LSrf = BspSrfNew(Index1 + 1, VLength,
			     UOrder, VOrder, Srf -> PType);
	    RSrf = BspSrfNew(ULength - Index2 + UOrder, VLength,
			     UOrder, VOrder, Srf -> PType);
	    Mult = UOrder - 1 - (Index2 - Index1 - 1);

	    /* Update the new knot vectors. */
	    CAGD_GEN_COPY(LSrf -> UKnotVector,
			  Srf -> UKnotVector,
			  sizeof(CagdRType) * (Index1 + 1));
	    /* Close the knot vector with multiplicity Order: */
	    for (j = Index1 + 1; j <= Index1 + UOrder; j++)
		LSrf -> UKnotVector[j] = t;
	    CAGD_GEN_COPY(&RSrf -> UKnotVector[UOrder],
			  &Srf -> UKnotVector[Index2],
			  sizeof(CagdRType) * (ULength + UOrder - Index2));
	    /* Make sure knot vector starts with multiplicity Order: */
	    for (j = 0; j < UOrder; j++)
		RSrf -> UKnotVector[j] = t;
	    
	    /* And copy the other direction knot vectors. */
	    CAGD_GEN_COPY(LSrf -> VKnotVector,
			  Srf -> VKnotVector,
			  sizeof(CagdRType) * (VOrder + VLength));
	    CAGD_GEN_COPY(RSrf -> VKnotVector,
			  Srf -> VKnotVector,
			  sizeof(CagdRType) * (VOrder + VLength));
	    break;
	case CAGD_CONST_V_DIR:
	    RefKV = Srf -> VKnotVector;
	    KVLen = VOrder + VLength;
	    Index1 = BspKnotLastIndexL(RefKV, KVLen, t);
	    Index2 = BspKnotFirstIndexG(RefKV, KVLen, t);
	    LSrf = BspSrfNew(ULength, Index1 + 1,
			     UOrder, VOrder, Srf -> PType);
	    RSrf = BspSrfNew(ULength, VLength - Index2 + VOrder,
			     UOrder, VOrder, Srf -> PType);
	    Mult = VOrder - 1 - (Index2 - Index1 - 1);

	    /* Update the new knot vectors. */
	    CAGD_GEN_COPY(LSrf -> VKnotVector,
			  Srf -> VKnotVector,
			  sizeof(CagdRType) * (Index1 + 1));
	    /* Close the knot vector with multiplicity Order: */
	    for (j = Index1 + 1; j <= Index1 + VOrder; j++)
		LSrf -> VKnotVector[j] = t;
	    CAGD_GEN_COPY(&RSrf -> VKnotVector[VOrder],
			  &Srf -> VKnotVector[Index2],
			  sizeof(CagdRType) * (VLength + VOrder - Index2));
	    /* Make sure knot vector starts with multiplicity Order: */
	    for (j = 0; j < VOrder; j++)
		RSrf -> VKnotVector[j] = t;
	    
	    /* And copy the other direction knot vectors. */
	    CAGD_GEN_COPY(LSrf -> UKnotVector,
			  Srf -> UKnotVector,
			  sizeof(CagdRType) * (UOrder + ULength));
	    CAGD_GEN_COPY(RSrf -> UKnotVector,
			  Srf -> UKnotVector,
			  sizeof(CagdRType) * (UOrder + ULength));
	    break;
	default:
	    Mult = 1;
	    RefKV = NULL;
	    LSrf = RSrf = NULL;
	    FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
	    break;
    }

    Pts = Srf -> Points;
    LPts = LSrf -> Points;
    RPts = RSrf -> Points;
    LULength = LSrf -> ULength,
    RULength = RSrf -> ULength;
    LVLength = LSrf -> VLength,
    RVLength = RSrf -> VLength;

    switch (Dir) {
	case CAGD_CONST_U_DIR:
	    /* Compute the Alpha refinement matrix. */
	    if (Mult > 0) {
		CagdRType
		    *NewKV = (CagdRType *) IritMalloc(sizeof(CagdRType) * Mult);

		for (i = 0; i < Mult; i++)
		    NewKV[i] = t;
		A = BspKnotEvalAlphaCoefMerge(UOrder, RefKV, ULength,
								NewKV, Mult);
		IritFree((VoidPtr) NewKV);
	    }
	    else
		A = BspKnotEvalAlphaCoefMerge(UOrder, RefKV, ULength, NULL, 0);

	    /* Now work on the two new surfaces meshes. */

	    /* Note that Mult can be negative in cases where original       */
	    /* multiplicity was order or more and we need to compensate     */
	    /* here, since Alpha matrix will be just a unit matrix then.    */
	    Mult = Mult >= 0 ? 0 : -Mult;

	    for (Row = 0; Row < VLength; Row++) {
	        /* Blend Srf into LSrf. */
	        for (j = IsNotRational; j <= MaxCoord; j++) {
		    LOnePts = &LPts[j][Row * LULength];
		    OnePts = &Pts[j][Row * ULength];
		    for (i = 0; i < LULength; i++, LOnePts++)
		        CAGD_ALPHA_BLEND(A, i, OnePts, LOnePts);
		}

	        /* Blend Srf into LSrf. */
		for (j = IsNotRational; j <= MaxCoord; j++) {
		    ROnePts = &RPts[j][Row * RULength];
		    OnePts = &Pts[j][Row * ULength];
		    for (i = LSrf -> ULength - 1 + Mult;
			 i < LSrf -> ULength + RSrf -> ULength - 1 + Mult;
			 i++, ROnePts++)
			CAGD_ALPHA_BLEND(A, i, OnePts, ROnePts);
		}
	    }

	    BspKnotFreeAlphaCoef(A);
	    break;
	case CAGD_CONST_V_DIR:
	    /* Compute the Alpha refinement matrix. */
	    if (Mult > 0) {
		CagdRType
		    *NewKV = (CagdRType *) IritMalloc(sizeof(CagdRType) * Mult);

		for (i = 0; i < Mult; i++)
		    NewKV[i] = t;
		A = BspKnotEvalAlphaCoefMerge(VOrder, RefKV, VLength,
						  NewKV, Mult);
		IritFree((VoidPtr) NewKV);
	    }
	    else
		A = BspKnotEvalAlphaCoefMerge(VOrder, RefKV, VLength, NULL, 0);

	    /* Now work on the two new surfaces meshes. */

	    /* Note that Mult can be negative in cases where original       */
	    /* multiplicity was order or more and we need to compensate     */
	    /* here, since Alpha matrix will be just a unit matrix then.    */
	    Mult = Mult >= 0 ? 0 : -Mult;

	    for (Col = 0; Col < ULength; Col++) {
		LULength = LSrf -> ULength;
		RULength = RSrf -> ULength;

		/* Blend Srf into LSrf. */
		for (j = IsNotRational; j <= MaxCoord; j++) {
		    LOnePts = &LPts[j][Col];
		    OnePts = &Pts[j][Col];
		    for (i = 0;
		         i < LVLength;
		         i++, LOnePts += LULength)
		        CAGD_ALPHA_BLEND_STEP(A, i, OnePts, LOnePts, ULength);
		}

		/* Blend Srf into RSrf. */
		for (j = IsNotRational; j <= MaxCoord; j++) {
		    ROnePts = &RPts[j][Col];
		    OnePts = &Pts[j][Col];
		    for (i = LVLength - 1 + Mult;
			 i < LVLength + RVLength - 1 + Mult;
			 i++, ROnePts += RULength)
			CAGD_ALPHA_BLEND_STEP(A, i, OnePts, ROnePts, ULength);
		}
	    }

	    BspKnotFreeAlphaCoef(A);
	    break;
	default:
	    FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
	    break;
    }

    BspKnotMakeRobustKV(RSrf -> UKnotVector,
			RSrf -> UOrder + RSrf -> ULength);
    BspKnotMakeRobustKV(RSrf -> VKnotVector,
			RSrf -> VOrder + RSrf -> VLength);

    BspKnotMakeRobustKV(LSrf -> UKnotVector,
			LSrf -> UOrder + LSrf -> ULength);
    BspKnotMakeRobustKV(LSrf -> VKnotVector,
			LSrf -> VOrder + LSrf -> VLength);

    LSrf -> Pnext = RSrf;
    return LSrf;
}

/******************************************************************************
*  Insert n knot all with the value t in direction Dir. In no case will the   *
* multiplicity of knot be greater or equal to the curve order.		      *
******************************************************************************/
CagdSrfStruct *BspSrfKnotInsertNSame(CagdSrfStruct *BspSrf, CagdSrfDirType Dir,
							    CagdRType t, int n)
{
    int i, CrntMult, Mult;
    CagdSrfStruct *RefinedSrf;

    switch (Dir) {
	case CAGD_CONST_U_DIR:
	    CrntMult = BspKnotFindMult(BspSrf -> UKnotVector, BspSrf -> UOrder,
							 BspSrf -> ULength, t),
	    Mult = MIN(n, BspSrf -> UOrder - CrntMult - 1);
	    break;
	case CAGD_CONST_V_DIR:
	    CrntMult = BspKnotFindMult(BspSrf -> VKnotVector, BspSrf -> VOrder,
							 BspSrf -> VLength, t),
	    Mult = MIN(n, BspSrf -> VOrder - CrntMult - 1);
	    break;
	default:
	    Mult = 0;
	    FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
	    break;
    }

    if (Mult > 0) {
	CagdRType
	    *NewKV = (CagdRType *) IritMalloc(sizeof(CagdRType) * Mult);

	for (i = 0; i < Mult; i++)
	    NewKV[i] = t;

	RefinedSrf = BspSrfKnotInsertNDiff(BspSrf, Dir, FALSE, NewKV, Mult);

	IritFree((VoidPtr) NewKV);
    }
    else {
	RefinedSrf = CagdSrfCopy(BspSrf);
    }

    return RefinedSrf;
}

/******************************************************************************
*  Insert n knot with different values as defined by t. If however Replace is *
* TRUE, the knot are simply replacing the current ones.			      *
******************************************************************************/
CagdSrfStruct *BspSrfKnotInsertNDiff(CagdSrfStruct *Srf, CagdSrfDirType Dir,
					      int Replace, CagdRType *t, int n)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Srf);
    int i, Row, Col,
	ULength = Srf -> ULength,
	VLength = Srf -> VLength,
	UOrder = Srf -> UOrder,
	VOrder = Srf -> VOrder,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Srf -> PType);
    CagdSrfStruct
	*RefSrf = NULL;

    if (Replace) {
	for (i = 1; i < n; i++)
	    if (t[i] < t[i - 1])
		FATAL_ERROR(CAGD_ERR_KNOT_NOT_ORDERED);

    	switch (Dir) {
	    case CAGD_CONST_U_DIR:
		if (Srf -> UOrder + Srf -> ULength != n)
		    FATAL_ERROR(CAGD_ERR_NUM_KNOT_MISMATCH);

		RefSrf = CagdSrfCopy(Srf);
		for (i = 0; i < n; i++)
		    RefSrf -> UKnotVector[i] = *t++;
		break;
	    case CAGD_CONST_V_DIR:
		if (Srf -> VOrder + Srf -> VLength != n)
		    FATAL_ERROR(CAGD_ERR_NUM_KNOT_MISMATCH);

		RefSrf = CagdSrfCopy(Srf);
		for (i = 0; i < n; i++)
		    RefSrf -> VKnotVector[i] = *t++;
		break;
	    default:
		FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
		break;
	}
    }
    else if (n == 0) {
	RefSrf = CagdSrfCopy(Srf);
    }
    else {
	int j, LengthKVt, RULength, RVLength;
	BspKnotAlphaCoeffType *A;
	CagdRType *MergedKVt,
	    *UKnotVector = Srf -> UKnotVector,
	    *VKnotVector = Srf -> VKnotVector;

	switch (Dir) {
	    case CAGD_CONST_U_DIR:
		/* Compute the Alpha refinement matrix. */
		MergedKVt = BspKnotMergeTwo(UKnotVector, ULength + UOrder,
					    t, n, 0, &LengthKVt);
		A = BspKnotEvalAlphaCoef(UOrder, UKnotVector, ULength,
					 MergedKVt, LengthKVt - UOrder);

	        RefSrf = BspSrfNew(ULength + n, VLength, UOrder, VOrder,
							      Srf -> PType);
		IritFree((VoidPtr) RefSrf -> UKnotVector);
		IritFree((VoidPtr) RefSrf -> VKnotVector);
		RefSrf -> UKnotVector = MergedKVt;
		RefSrf -> VKnotVector = BspKnotCopy(Srf -> VKnotVector,
					Srf -> VLength + Srf -> VOrder);

		RULength = RefSrf -> ULength;

		/* Update the control mesh */
		for (Row = 0; Row < VLength; Row++) {
		    for (j = IsNotRational; j <= MaxCoord; j++) {
			CagdRType
			    *ROnePts = &RefSrf -> Points[j][Row * RULength],
			    *OnePts = &Srf -> Points[j][Row * ULength];

			for (i = 0; i < RULength; i++, ROnePts++)
			    CAGD_ALPHA_BLEND( A, i, OnePts, ROnePts );
		    }
		}

		BspKnotFreeAlphaCoef(A);
		break;
	    case CAGD_CONST_V_DIR:
		/* Compute the Alpha refinement matrix. */
		MergedKVt = BspKnotMergeTwo(VKnotVector, VLength + VOrder,
					    t, n, 0, &LengthKVt);
		A = BspKnotEvalAlphaCoef(VOrder, VKnotVector, VLength,
					 MergedKVt, LengthKVt - VOrder);

	        RefSrf = BspSrfNew(ULength, VLength + n, UOrder, VOrder,
							      Srf -> PType);
		IritFree((VoidPtr) RefSrf -> UKnotVector);
		IritFree((VoidPtr) RefSrf -> VKnotVector);
		RefSrf -> UKnotVector = BspKnotCopy(Srf -> UKnotVector,
					Srf -> ULength + Srf -> UOrder);
		RefSrf -> VKnotVector = MergedKVt;

		RULength = RefSrf -> ULength;
		RVLength = RefSrf -> VLength;

		/* Update the control mesh */
		for (Col = 0; Col < ULength; Col++) {
		    for (j = IsNotRational; j <= MaxCoord; j++) {
			CagdRType
			    *ROnePts = &RefSrf -> Points[j][Col],
			    *OnePts = &Srf -> Points[j][Col];

			for (i = 0; i < RVLength; i++, ROnePts += RULength)
			    CAGD_ALPHA_BLEND_STEP(A, i, OnePts,
						  ROnePts, ULength);
		    }
		}

		BspKnotFreeAlphaCoef(A);
		break;
	    default:
		FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
		break;
	}
    }

    BspKnotMakeRobustKV(RefSrf -> UKnotVector,
			RefSrf -> UOrder + RefSrf -> ULength);
    BspKnotMakeRobustKV(RefSrf -> VKnotVector,
			RefSrf -> VOrder + RefSrf -> VLength);

    return RefSrf;
}

/******************************************************************************
* Return a new surface, identical to the original but with one degree higher  *
* in the given direction.						      *
******************************************************************************/
CagdSrfStruct *BspSrfDegreeRaise(CagdSrfStruct *Srf, CagdSrfDirType Dir)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_SRF(Srf);
    int i, i2, j, RaisedLen, Row, Col,
	Order = Dir == CAGD_CONST_V_DIR ? Srf -> UOrder : Srf -> VOrder,
	Length = Dir == CAGD_CONST_V_DIR ? Srf -> ULength : Srf -> VLength,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Srf -> PType);
    CagdSrfStruct
	*RaisedSrf = NULL;

    if (Order > 2) {
	CagdSrfStruct *UnitSrf;
	int UKvLen1 = Srf -> UOrder + Srf -> ULength - 1,
	    VKvLen1 = Srf -> VOrder + Srf -> VLength - 1;
	CagdRType
	    *UKv = Srf -> UKnotVector,
	    *VKv = Srf -> VKnotVector;

	/* Degree raise by multiplying by a constant 1 linear surface in the */
	/* raised direction and constant 1 constant surface in the other.    */

	switch (Dir) {
	    case CAGD_CONST_U_DIR:
		UnitSrf = BspSrfNew(1, 2, 1, 2,
				    CAGD_MAKE_PT_TYPE(FALSE, MaxCoord));
		for (i = 0; i < 2; i++)
		    UnitSrf -> UKnotVector[i] = i > 0 ? UKv[UKvLen1] : UKv[0];
		for (i = 0; i < 4; i++)
		    UnitSrf -> VKnotVector[i] = i > 1 ? VKv[VKvLen1] : VKv[0];
		break;
	    case CAGD_CONST_V_DIR:
		UnitSrf = BspSrfNew(2, 1, 2, 1,
				    CAGD_MAKE_PT_TYPE(FALSE, MaxCoord));
		for (i = 0; i < 4; i++)
		    UnitSrf -> UKnotVector[i] = i > 1 ? UKv[UKvLen1] : UKv[0];
		for (i = 0; i < 2; i++)
		    UnitSrf -> VKnotVector[i] = i > 0 ? VKv[VKvLen1] : VKv[0];
		break;
	    default:
		UnitSrf = NULL;
		FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
		break;
	}
	for (i = 1; i <= MaxCoord; i++)
	    UnitSrf -> Points[i][0] = UnitSrf -> Points[i][1] = 1.0;

	RaisedSrf = BspSrfMult(Srf, UnitSrf);

	CagdSrfFree(UnitSrf);

	return RaisedSrf;
    }

    /* If surface is linear, degree raising means basically to increase the  */
    /* knot multiplicity of each segment by one and add a middle point for   */
    /* each such segment.						     */
    RaisedLen = Length * 2 - 1;

    switch (Dir) {
	case CAGD_CONST_U_DIR:
	    RaisedSrf = BspSrfNew(Srf -> ULength, RaisedLen,
				  Srf -> UOrder, Order + 1, Srf -> PType);

	    /* Update the knot vectors. */
	    CAGD_GEN_COPY(RaisedSrf -> UKnotVector,
			  Srf -> UKnotVector,
			  sizeof(CagdRType) * (Srf -> ULength + Srf -> UOrder));
	    for (i = 0; i < 3; i++)
		RaisedSrf -> VKnotVector[i] = Srf -> VKnotVector[0];
	    for (i = 2, j = 3; i < Length; i++, j += 2)
		RaisedSrf -> VKnotVector[j] = RaisedSrf -> VKnotVector[j + 1] = 
		    Srf -> VKnotVector[i];
	    for (i = j; i < j + 3; i++)
		RaisedSrf -> VKnotVector[i] = Srf -> VKnotVector[Length];

	    /* Update the mesh. */
       	    for (Col = 0; Col < Srf -> ULength; Col++) {
		for (j = IsNotRational; j <= MaxCoord; j++)   /* First point. */
		    RaisedSrf -> Points[j][RAISED_SRF(Col, 0)] =
			Srf -> Points[j][SRF(Col, 0)];

		for (i = 1, i2 = 1; i < Length; i++, i2 += 2)
		    for (j = IsNotRational; j <= MaxCoord; j++) {
			RaisedSrf -> Points[j][RAISED_SRF(Col, i2)] =
				Srf -> Points[j][SRF(Col, i - 1)] * 0.5 +
				Srf -> Points[j][SRF(Col, i)] * 0.5;
			RaisedSrf -> Points[j][RAISED_SRF(Col, i2 + 1)] =
				Srf -> Points[j][SRF(Col, i)];
		    }
	    }
	    break;
	case CAGD_CONST_V_DIR:
	    RaisedSrf = BspSrfNew(RaisedLen, Srf -> VLength,
				  Order + 1, Srf -> VOrder, Srf -> PType);

	    /* Update the knot vectors. */
	    CAGD_GEN_COPY(RaisedSrf -> VKnotVector,
			  Srf -> VKnotVector,
			  sizeof(CagdRType) * (Srf -> VLength + Srf -> VOrder));
	    for (i = 0; i < 3; i++)
		RaisedSrf -> UKnotVector[i] = Srf -> UKnotVector[0];
	    for (i = 2, j = 3; i < Length; i++, j += 2)
		RaisedSrf -> UKnotVector[j] = RaisedSrf -> UKnotVector[j + 1] = 
		    Srf -> UKnotVector[i];
	    for (i = j; i < j + 3; i++)
		RaisedSrf -> UKnotVector[i] = Srf -> UKnotVector[Length];

	    /* Update the mesh. */
       	    for (Row = 0; Row < Srf -> VLength; Row++) {
		for (j = IsNotRational; j <= MaxCoord; j++)   /* First point. */
		    RaisedSrf -> Points[j][RAISED_SRF(0, Row)] =
			Srf -> Points[j][SRF(0, Row)];

		for (i = 1, i2 = 1; i < Length; i++, i2 += 2)
		    for (j = IsNotRational; j <= MaxCoord; j++) {
			RaisedSrf -> Points[j][RAISED_SRF(i2, Row)] =
				Srf -> Points[j][SRF(i - 1, Row)] * 0.5 +
				Srf -> Points[j][SRF(i, Row)] * 0.5;
			RaisedSrf -> Points[j][RAISED_SRF(i2 + 1, Row)] =
				Srf -> Points[j][SRF(i, Row)];
		    }
	    }
	    break;
	default:
	    FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
	    break;
    }

    return RaisedSrf;
}

/******************************************************************************
* Return a new surface equal to the derived surface in the direction Dir.     *
* Let old control polygon be P(i), i = 0 to k-1, and Q(i) be new one then:    *
* Q(i) = (k - 1) * P(i+1) - P(i), i = 0 to k-2.				      *
* This is applied to all rows/cols of the surface.			      *
******************************************************************************/
CagdSrfStruct *BspSrfDerive(CagdSrfStruct *Srf, CagdSrfDirType Dir)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_SRF(Srf);
    int i, j, Row, Col,
	ULength = Srf -> ULength,
	VLength = Srf -> VLength,
	UOrder = Srf -> UOrder,
	VOrder = Srf -> VOrder,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Srf -> PType);
    CagdRType **DPoints,
	*UKv = Srf -> UKnotVector,
	*VKv = Srf -> VKnotVector,
	**Points = Srf -> Points;
    CagdSrfStruct
        *DerivedSrf = NULL;

    if (!IsNotRational)
	return BspSrfDeriveRational(Srf, Dir);

    switch (Dir) {
	case CAGD_CONST_V_DIR:
	    if (UOrder < 2)
		FATAL_ERROR(CAGD_ERR_LIN_NO_SUPPORT);

	    DerivedSrf = BspSrfNew(ULength - 1, VLength,
				   UOrder - 1, VOrder, Srf -> PType);
	    CAGD_GEN_COPY(DerivedSrf -> UKnotVector, &UKv[1],
			  sizeof(CagdRType) * (ULength + UOrder - 2));
	    CAGD_GEN_COPY(DerivedSrf -> VKnotVector, VKv,
			  sizeof(CagdRType) * (VLength + VOrder));
	    DPoints = DerivedSrf -> Points;

       	    for (Row = 0; Row < VLength; Row++)
		for (i = 0; i < ULength - 1; i++) {
		    CagdRType
			Denom = UKv[i + UOrder] - UKv[i + 1];

		    for (j = IsNotRational; j <= MaxCoord; j++) {
			if (APX_EQ(Denom, 0.0))
			    Denom = INFINITY;

			DPoints[j][DERIVED_SRF(i, Row)] = (UOrder - 1) *
			    (Points[j][SRF(i + 1, Row)] -
			     Points[j][SRF(i, Row)]) / Denom;
		    }
		}
	    break;
	case CAGD_CONST_U_DIR:
	    if (VOrder < 2)
		FATAL_ERROR(CAGD_ERR_LIN_NO_SUPPORT);

	    DerivedSrf = BspSrfNew(ULength, VLength - 1,
	    			   UOrder, VOrder - 1, Srf -> PType);
	    CAGD_GEN_COPY(DerivedSrf -> UKnotVector, UKv,
			  sizeof(CagdRType) * (ULength + UOrder));
	    CAGD_GEN_COPY(DerivedSrf -> VKnotVector, &VKv[1],
			  sizeof(CagdRType) * (VLength + VOrder - 2));
	    DPoints = DerivedSrf -> Points;

	    for (Col = 0; Col < ULength; Col++)
		for (i = 0; i < VLength - 1; i++) {
		    CagdRType
			Denom = VKv[i + VOrder] - VKv[i + 1];

		    for (j = IsNotRational; j <= MaxCoord; j++) {
			if (APX_EQ(Denom, 0.0))
			    Denom = INFINITY;

			DPoints[j][DERIVED_SRF(Col, i)] = (VOrder - 1) *
			    (Points[j][SRF(Col, i + 1)] -
			     Points[j][SRF(Col, i)]) / Denom;
		    }
		}
	    break;
	default:
	    FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
	    break;
    }

    return DerivedSrf;
}

/******************************************************************************
* Evaluate the tangent to a surface at a given point and given direction.     *
******************************************************************************/
CagdVecStruct *BspSrfTangent(CagdSrfStruct *Srf, CagdRType u, CagdRType v,
							 CagdSrfDirType Dir)
{
    CagdVecStruct
	*Tangent = NULL;
    CagdCrvStruct *Crv;

    switch (Dir) {
	case CAGD_CONST_V_DIR:
	    Crv = BspSrfCrvFromSrf(Srf, v, Dir);
	    Tangent = BspCrvTangent(Crv, u);
	    CagdCrvFree(Crv);
	    break;
	case CAGD_CONST_U_DIR:
	    Crv = BspSrfCrvFromSrf(Srf, u, Dir);
	    Tangent = BspCrvTangent(Crv, v);
	    CagdCrvFree(Crv);
	    break;
	default:
	    FATAL_ERROR(CAGD_ERR_DIR_NOT_CONST_UV);
	    break;
    }

    return Tangent;
}

/******************************************************************************
* Evaluate the normal of a surface at a given point.			      *
* If we fail to compute the normal at given location we try by moving a tad.  *
******************************************************************************/
CagdVecStruct *BspSrfNormal(CagdSrfStruct *Srf, CagdRType u, CagdRType v)
{
    static CagdVecStruct Normal;
    CagdVecStruct *V, T1, T2;
    CagdRType UMin, UMax, VMin, VMax;

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

    V = BspSrfTangent(Srf, u, v, CAGD_CONST_U_DIR);
    if (CAGD_LEN_VECTOR(*V) < EPSILON)
	V = BspSrfTangent(Srf,
			  u > UMin + EPSILON ? u - EPSILON : u + EPSILON,
			  v > VMin + EPSILON ? v - EPSILON : v + EPSILON,
			  CAGD_CONST_U_DIR);
    CAGD_COPY_VECTOR(T1, *V);

    V = BspSrfTangent(Srf, u, v, CAGD_CONST_V_DIR);
    if (CAGD_LEN_VECTOR(*V) < EPSILON)
	V = BspSrfTangent(Srf,
			  u > UMin + EPSILON ? u - EPSILON : u + EPSILON,
			  v > VMin + EPSILON ? v - EPSILON : v + EPSILON,
			  CAGD_CONST_V_DIR);
    CAGD_COPY_VECTOR(T2, *V);

    /* The normal is the cross product of T1 and T2: */
    Normal.Vec[0] = T1.Vec[1] * T2.Vec[2] - T1.Vec[2] * T2.Vec[1];
    Normal.Vec[1] = T1.Vec[2] * T2.Vec[0] - T1.Vec[0] * T2.Vec[2];
    Normal.Vec[2] = T1.Vec[0] * T2.Vec[1] - T1.Vec[1] * T2.Vec[0];

    CAGD_NORMALIZE_VECTOR(Normal);		    /* Normalize the vector. */

    return &Normal;
}

/******************************************************************************
* Evaluate the normals of a surface at a mesh defined by subdividing the      *
* parametric space into a grid of size UFineNess by VFineNess.		      *
*   The normals are saved in a linear CagdVecStruct vector which is allocated *
* dynamically. Data is saved u inc. first.				      *
* This routine is much faster than evaluating normal per each point.	      *
******************************************************************************/
CagdVecStruct *BspSrfMeshNormals(CagdSrfStruct *Srf, int UFineNess,
								int VFineNess)
{
    int i, j;
    CagdRType u, v, UMin, UMax, VMin, VMax;
    CagdVecStruct *Normals, *NPtr, *T, T1, T2;
    CagdCrvStruct **UCurves, **VCurves, *UCrv, *VCrv;

    BspSrfDomain(Srf, &UMin, &UMax, &VMin, &VMax);
    Normals = (CagdVecStruct *) IritMalloc(sizeof(CagdVecStruct) * UFineNess *
								   VFineNess);

    UCurves = (CagdCrvStruct **) IritMalloc(sizeof(CagdCrvStruct *) *
								   UFineNess);
    VCurves = (CagdCrvStruct **) IritMalloc(sizeof(CagdCrvStruct *) *
								   VFineNess);

    UFineNess--;
    VFineNess--;
    for (i = 0; i <= UFineNess; i++)		    /* Prepare Iso U curves. */
	UCurves[i] = BspSrfCrvFromSrf(Srf,
    			UMin + (UMax - UMin) * ((CagdRType) i) / UFineNess,
			CAGD_CONST_U_DIR);
    for (j = 0; j <= VFineNess; j++)	            /* Prepare Iso V curves. */
	VCurves[j] = BspSrfCrvFromSrf(Srf,
			VMin + (VMax - VMin) * ((CagdRType) j) / VFineNess,
			CAGD_CONST_V_DIR);

    NPtr = Normals;
    for (i = 0; i <= UFineNess; i++) {
	UCrv = UCurves[i];
	u = UMin + (UMax - UMin) * ((CagdRType) i) / UFineNess;

	for (j = 0; j <= VFineNess; j++) {
	    VCrv = VCurves[j];
	    v = VMin + (VMax - VMin) * ((CagdRType) j) / VFineNess;

	    /* We need to copy the tangents as BspCrvTangent save it in as   */
	    /* static so the second call will overwrite first call value.    */
	    /* If we fail to compute the tangent, we try adjacent isoline.   */
	    T = BspCrvTangent(UCrv, v);
	    if (CAGD_LEN_VECTOR(*T) < EPSILON)
		T = BspCrvTangent(UCurves[i == 0 ? i + 1 : i - 1], v);
	    CAGD_COPY_VECTOR(T1, *T);

	    T = BspCrvTangent(VCrv, u);
	    if (CAGD_LEN_VECTOR(*T) < EPSILON)
		T = BspCrvTangent(VCurves[j == 0 ? j + 1 : j - 1], u);
	    CAGD_COPY_VECTOR(T2, *T);

	    /* The normal is the cross product of T1 and T2: */
	    NPtr -> Vec[0] = T1.Vec[1] * T2.Vec[2] - T1.Vec[2] * T2.Vec[1];
	    NPtr -> Vec[1] = T1.Vec[2] * T2.Vec[0] - T1.Vec[0] * T2.Vec[2];
	    NPtr -> Vec[2] = T1.Vec[0] * T2.Vec[1] - T1.Vec[1] * T2.Vec[0];

	    CAGD_NORMALIZE_VECTOR(*NPtr);           /* Normalize the vector. */
	    NPtr++;
	}
    }

    for (i = 0; i <= UFineNess; i++)
	CagdCrvFree(UCurves[i]);
    IritFree(UCurves);
    for (j = 0; j <= VFineNess; j++)
	CagdCrvFree(VCurves[j]);
    IritFree(VCurves);

    return Normals;
}
