/******************************************************************************
* CBsp-Aux.c - Bspline curve auxilary routines.				      *
*******************************************************************************
* Written by Gershon Elber, Aug. 90.					      *
******************************************************************************/

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

/******************************************************************************
* Given a bspline curve - subdivide it into two at the given parametric value.*
* Returns pointer to first curve in a list of two curves (subdivided ones).   *
* The subdivision is achieved by inserting (order-1) knot at the given param. *
* value t and spliting the control polygon and knot vector at that point.     *
******************************************************************************/
CagdCrvStruct *BspCrvSubdivAtParam(CagdCrvStruct *Crv, CagdRType t)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, j,
	k = Crv -> Order,
	Len = Crv -> Length,
        KVLen = k + Len,
	Index1 = BspKnotLastIndexL(Crv -> KnotVector, KVLen, t),
	Index2 = BspKnotFirstIndexG(Crv -> KnotVector, KVLen, t),
	Mult = k - 1 - (Index2 - Index1 - 1),
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType TMin, TMax, *LOnePts, *ROnePts, *OnePts, *NewKV,
	**Pts, **LPts, **RPts;
    CagdCrvStruct *LCrv, *RCrv;
    BspKnotAlphaCoeffType *A;

    CagdCrvDomain(Crv, &TMin, &TMax);
    if (t < TMin || APX_EQ(t, TMin) ||
	t > TMax || APX_EQ(t, TMax))
	FATAL_ERROR(CAGD_ERR_T_NOT_IN_CRV);

    LCrv = BspCrvNew(Index1 + 1, k, Crv -> PType);
    RCrv = BspCrvNew(Len - Index2 + k, k, Crv -> PType);

    /* Update the new knot vectors. */
    CAGD_GEN_COPY(LCrv -> KnotVector,
	     Crv -> KnotVector,
	     sizeof(CagdRType) * (Index1 + 1));
    /* Close the knot vector with multiplicity Order: */
    for (j = Index1 + 1; j <= Index1 + k; j++)
	LCrv -> KnotVector[j] = t;
    CAGD_GEN_COPY(&RCrv -> KnotVector[k],
		  &Crv -> KnotVector[Index2],
		  sizeof(CagdRType) * (Len + k - Index2));
    /* Make sure knot vector starts with multiplicity Order: */
    for (j = 0; j < k; j++)
	RCrv -> KnotVector[j] = t;

    /* Now handle the control polygon refinement. */
    Pts = Crv -> Points,
    LPts = LCrv -> Points,
    RPts = RCrv -> Points;

    if (Mult > 0) {
	NewKV = IritMalloc(sizeof(CagdRType) * Mult);
	for (i = 0; i < Mult; i++)
	    NewKV[i] = t;
	A = BspKnotEvalAlphaCoefMerge(k, Crv -> KnotVector, Len, NewKV,
					  Mult);
	IritFree((VoidPtr) NewKV);
    }
    else {
	A = BspKnotEvalAlphaCoef(k, Crv -> KnotVector, Len,
				    Crv -> KnotVector, Len);
    }

    /* 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;

    /* Blend Crv into LCrv. */
    for (j = IsNotRational; j <= MaxCoord; j++) {
	LOnePts = LPts[j];
	OnePts = Pts[j];
	for (i = 0; i < LCrv -> Length; i++, LOnePts++)
	    CAGD_ALPHA_BLEND( A, i, OnePts, LOnePts );
    }

    /* Blend Crv into RCrv. */
    for (j = IsNotRational; j <= MaxCoord; j++) {
	ROnePts = RPts[j];
	OnePts = Pts[j];
	for (i = LCrv -> Length - 1 + Mult;
	     i < LCrv -> Length + RCrv -> Length - 1 + Mult;
	     i++, ROnePts++)
	    CAGD_ALPHA_BLEND( A, i, OnePts, ROnePts );
    }

    BspKnotFreeAlphaCoef(A);

    BspKnotMakeRobustKV(RCrv -> KnotVector,
			RCrv -> Order + RCrv -> Length);
    BspKnotMakeRobustKV(LCrv -> KnotVector,
			LCrv -> Order + LCrv -> Length);

    LCrv -> Pnext = RCrv;

    return LCrv;
}

/******************************************************************************
*  Insert n knot all with the value t. In no case will the multiplicity of    *
* knot be greater or equal to the curve order.				      *
******************************************************************************/
CagdCrvStruct *BspCrvKnotInsertNSame(CagdCrvStruct *Crv, CagdRType t, int n)
{
    int i,
	CrntMult = BspKnotFindMult(Crv -> KnotVector, Crv -> Order,
							   Crv -> Length, t),
	Mult = MIN(n, Crv -> Order - CrntMult - 1);
    CagdCrvStruct *RefinedCrv;

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

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

	RefinedCrv = BspCrvKnotInsertNDiff(Crv, FALSE, NewKV, Mult);

	IritFree((VoidPtr) NewKV);
    }
    else {
	RefinedCrv = CagdCrvCopy(Crv);
    }

    return RefinedCrv;
}

/******************************************************************************
*  Insert n knot with different values as defined by t. If however Replace is *
* TRUE, the knot are simply replacing the current ones.			      *
******************************************************************************/
CagdCrvStruct *BspCrvKnotInsertNDiff(CagdCrvStruct *Crv, CagdBType Replace,
							   CagdRType *t, int n)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    CagdRType
	*KnotVector = Crv -> KnotVector;
    int Order = Crv -> Order,
	Length = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdCrvStruct *RefinedCrv;

    if (Replace) {
	int i;

	if (Order + Length != n)
	    FATAL_ERROR(CAGD_ERR_NUM_KNOT_MISMATCH);
	for (i = 1; i < n; i++)
	    if (t[i] < t[i - 1])
		FATAL_ERROR(CAGD_ERR_KNOT_NOT_ORDERED);

    	RefinedCrv = CagdCrvCopy(Crv);
	for (i = 0; i < n; i++)
	    RefinedCrv -> KnotVector[i] = *t++;
    }
    else if (n == 0) {
	RefinedCrv = CagdCrvCopy(Crv);
    }
    else {
	int i, j, LengthKVt;
	BspKnotAlphaCoeffType *A;
	CagdRType *MergedKVt;

	/* Compute the Alpha refinement matrix. */
	MergedKVt = BspKnotMergeTwo(KnotVector, Length + Order,
				    t, n, 0, &LengthKVt);
	A = BspKnotEvalAlphaCoef(Order, KnotVector, Length,
				 MergedKVt, LengthKVt - Order);

	RefinedCrv = BspCrvNew(Crv -> Length + n, Order, Crv -> PType);

	/* Update the knot vector. */
	IritFree((VoidPtr) RefinedCrv -> KnotVector);
	RefinedCrv -> KnotVector = MergedKVt;

	/* Update the control mesh */
	for (j = IsNotRational; j <= MaxCoord; j++) {
	    CagdRType
		*ROnePts = RefinedCrv -> Points[j],
		*OnePts = Crv -> Points[j];
	    for (i = 0; i < RefinedCrv -> Length; i++, ROnePts++)
		CAGD_ALPHA_BLEND( A, i, OnePts, ROnePts );
	}
	BspKnotFreeAlphaCoef(A);
    }

    BspKnotMakeRobustKV(RefinedCrv -> KnotVector,
			RefinedCrv -> Order + RefinedCrv -> Length);

    return RefinedCrv;
}

/******************************************************************************
* Returns a new curve, identical to the original but with order N.	      *
*   Degree raise by multiplying by a constant 1 curve of order	              *
* (NewOrder - Order).							      *
******************************************************************************/
CagdCrvStruct *BspCrvDegreeRaiseN(CagdCrvStruct *Crv, int NewOrder)
{
    int i, j, RaisedOrder,
	Order = Crv -> Order,
	Length = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType),
	KvLen1 = Order + Length - 1;
    CagdCrvStruct *RaisedCrv, *UnitCrv;
    CagdRType
	*Kv = Crv -> KnotVector;

    if (NewOrder < Order) {
	FATAL_ERROR(CAGD_ERR_WRONG_ORDER);
	return NULL;
    }
    RaisedOrder = NewOrder - Order + 1;

    UnitCrv = BspCrvNew(RaisedOrder, RaisedOrder,
			CAGD_MAKE_PT_TYPE(FALSE, MaxCoord));
    for (i = 0; i < RaisedOrder * 2; i++)
	UnitCrv -> KnotVector[i] = i >= RaisedOrder ? Kv[KvLen1] : Kv[0];
    for (i = 1; i <= MaxCoord; i++)
	for (j = 0; j < RaisedOrder; j++)
	    UnitCrv -> Points[i][j] = 1.0;

    RaisedCrv = BspCrvMult(Crv, UnitCrv);

    CagdCrvFree(UnitCrv);

    return RaisedCrv;
}

/******************************************************************************
* Return a new curve, identical to the original but with one degree higher    *
******************************************************************************/
CagdCrvStruct *BspCrvDegreeRaise(CagdCrvStruct *Crv)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, i2, j, RaisedLen,
	Order = Crv -> Order,
	Length = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdCrvStruct *RaisedCrv;

    if (Order > 2)
	return BspCrvDegreeRaiseN(Crv, Order + 1);

    /* If curve 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;
    RaisedCrv = BspCrvNew(RaisedLen, Order + 1, Crv -> PType);

    /* Update the control polygon; */
    for (j = IsNotRational; j <= MaxCoord; j++)		     /* First point. */
	RaisedCrv -> Points[j][0] = Crv -> Points[j][0];

    for (i = 1, i2 = 1; i < Length; i++, i2 += 2)
	for (j = IsNotRational; j <= MaxCoord; j++) {
	    RaisedCrv -> Points[j][i2] =
		Crv -> Points[j][i-1] * 0.5 + Crv -> Points[j][i] * 0.5;
	    RaisedCrv -> Points[j][i2 + 1] = Crv -> Points[j][i];
	}

    /* Update the knot vector. */
    for (i = 0; i < 3; i++)
	RaisedCrv -> KnotVector[i] = Crv -> KnotVector[0];

    for (i = 2, j = 3; i < Length; i++, j += 2)
	RaisedCrv -> KnotVector[j] = RaisedCrv -> KnotVector[j + 1] = 
	    Crv -> KnotVector[i];

    for (i = j; i < j + 3; i++)
	RaisedCrv -> KnotVector[i] = Crv -> KnotVector[Length];

    return RaisedCrv;
}

/******************************************************************************
* Return a normalized vector, equal to the tangent to Crv at parameter t.     *
* Algorithm: insert (order - 1) knots and return control polygon tangent.     *
******************************************************************************/
CagdVecStruct *BspCrvTangent(CagdCrvStruct *Crv, CagdRType t)
{
    static CagdVecStruct P2;
    CagdVecStruct P1, *T;
    CagdRType TMin, TMax;
    int k = Crv -> Order,
	Len = Crv -> Length,
	Index = BspKnotLastIndexL(Crv -> KnotVector, k + Len, t);
    CagdPointType
	PType = Crv -> PType;
    CagdCrvStruct *RefinedCrv;

    if (!BspKnotParamInDomain(Crv -> KnotVector, Len, k, t))
	FATAL_ERROR(CAGD_ERR_T_NOT_IN_CRV);

    BspCrvDomain(Crv, &TMin, &TMax);

    if (APX_EQ(t, TMin)) {
	/* Use Crv starting tangent direction. */
	CagdCoerceToE3(P1.Vec, Crv -> Points, 0, PType);
	CagdCoerceToE3(P2.Vec, Crv -> Points, 1, PType);
    }
    else if (APX_EQ(t, TMax)) {
	/* Use Crv ending tangent direction. */
	CagdCoerceToE3(P1.Vec, Crv -> Points, Len - 2, PType);
	CagdCoerceToE3(P2.Vec, Crv -> Points, Len - 1, PType);
    }
    else {
	RefinedCrv = BspCrvKnotInsertNSame(Crv, t, k - 1);

	CagdCoerceToE3(P1.Vec, RefinedCrv -> Points, Index, PType);
	CagdCoerceToE3(P2.Vec, RefinedCrv -> Points, Index + 1, PType);

	CagdCrvFree(RefinedCrv);
    }

    CAGD_SUB_VECTOR(P2, P1);

    if (CAGD_LEN_VECTOR(P2) < SQR(EPSILON)) {
	if (AttrGetIntAttrib(Crv -> Attr, "_tan") != TRUE) {
	    /* Try to move a little. This location has zero speed. However,  */
	    /* do it only once since we can be here forever. The "_tan"      */
	    /* attribute guarantee we will try to move EPSILON only once.    */
	    AttrSetIntAttrib(&Crv -> Attr, "_tan", TRUE);

	    T = BspCrvTangent(Crv, t - TMin < TMax - t ? t + EPSILON
						       : t - EPSILON);

	    AttrFreeOneAttribute(&Crv -> Attr, "_tan");

	    return T;
	}
	else {
	    /* A zero length vector signals failure to compute tangent. */
	    return &P2;
	}
    }
    else {
	CAGD_NORMALIZE_VECTOR(P2);		    /* Normalize the vector. */

	return &P2;
    }
}

/******************************************************************************
* Return a normalized vector, equal to the binormal to Crv at parameter t.    *
* Algorithm: insert (order - 1) knots and using 3 consecutive control points  *
* at the refined location (p1, p2, p3), compute to binormal to be the cross   *
* product of the two vectors (p1 - p2) and (p2 - p3).			      *
******************************************************************************/
CagdVecStruct *BspCrvBiNormal(CagdCrvStruct *Crv, CagdRType t)
{
    static CagdVecStruct P3;
    CagdVecStruct P1, P2;
    CagdRType TMin, TMax;
    int k = Crv -> Order,
	Len = Crv -> Length,
	Index = BspKnotLastIndexL(Crv -> KnotVector, k + Len, t);
    CagdPointType
	PType = Crv -> PType;
    CagdCrvStruct *RefinedCrv;

    if (!BspKnotParamInDomain(Crv -> KnotVector, Len, k, t))
	FATAL_ERROR(CAGD_ERR_T_NOT_IN_CRV);

    /* Can not compute for linear curves. */
    if (k <= 2)
	return NULL;

    /* For planar curves, B is trivially the Z axis. */
    if (CAGD_NUM_OF_PT_COORD(Crv -> PType) == 2) {
	P3.Vec[0] = P3.Vec[1] = 0.0;
	P3.Vec[2] = 1.0;
	return &P3;
    }

    BspCrvDomain(Crv, &TMin, &TMax);

    if (APX_EQ(t, TMin)) {
	/* Use Crv starting tangent direction. */
	CagdCoerceToE3(P1.Vec, Crv -> Points, 0, PType);
	CagdCoerceToE3(P2.Vec, Crv -> Points, 1, PType);
	CagdCoerceToE3(P3.Vec, Crv -> Points, 2, PType);
    }
    else if (APX_EQ(t, TMax)) {
	/* Use Crv ending tangent direction. */
	CagdCoerceToE3(P1.Vec, Crv -> Points, Len - 3, PType);
	CagdCoerceToE3(P2.Vec, Crv -> Points, Len - 2, PType);
	CagdCoerceToE3(P3.Vec, Crv -> Points, Len - 1, PType);
    }
    else {
	RefinedCrv = BspCrvKnotInsertNSame(Crv, t, k - 1);

	CagdCoerceToE3(P1.Vec, RefinedCrv -> Points, Index, PType);
	CagdCoerceToE3(P2.Vec, RefinedCrv -> Points, Index + 1, PType);
	CagdCoerceToE3(P3.Vec, RefinedCrv -> Points, Index + 2, PType);

	CagdCrvFree(RefinedCrv);
    }

    CAGD_SUB_VECTOR(P1, P2);
    CAGD_SUB_VECTOR(P2, P3);

    CROSS_PROD(P3.Vec, P1.Vec, P2.Vec);

    if ((t = CAGD_LEN_VECTOR(P3)) < EPSILON)
	return NULL;
    else
	CAGD_DIV_VECTOR(P3, t);			    /* Normalize the vector. */

    return &P3;
}

/******************************************************************************
* Return a normalized vector, equal to the normal to Crv at parameter t.      *
* Algorithm: returns the cross product of the curve tangent and binormal.     *
******************************************************************************/
CagdVecStruct *BspCrvNormal(CagdCrvStruct *Crv, CagdRType t)
{
    static CagdVecStruct N, *T, *B;

    T = BspCrvTangent(Crv, t);
    B = BspCrvBiNormal(Crv, t);

    if (T == NULL || B == NULL)
	return NULL;

    CROSS_PROD(N.Vec, T -> Vec, B -> Vec);

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

    return &N;
}

/******************************************************************************
* Return a new curve, equal to the derived curve.			      *
* 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)) / (Kv(i + k) - Kv(i + 1)), i = 0 to k-2.   *
******************************************************************************/
CagdCrvStruct *BspCrvDerive(CagdCrvStruct *Crv)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, j,
	k = Crv -> Order,
	Len = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType
	*Kv = Crv -> KnotVector;
    CagdCrvStruct *DerivedCrv;

    if (!IsNotRational)
	return BspCrvDeriveRational(Crv);
    else if (k < 2)
	FATAL_ERROR(CAGD_ERR_LIN_NO_SUPPORT);

    DerivedCrv = BspCrvNew(Len - 1, k - 1, Crv -> PType);

    for (i = 0; i < Len - 1; i++) {
	CagdRType
	    Denom = Kv[i + k] - Kv[i + 1];

	for (j = IsNotRational; j <= MaxCoord; j++)
	    DerivedCrv -> Points[j][i] = (k - 1) *
		(Crv -> Points[j][i + 1] - Crv -> Points[j][i]) / Denom;
    }

    CAGD_GEN_COPY(DerivedCrv -> KnotVector,
		  &Crv -> KnotVector[1],
		  sizeof(CagdRType) * (k + Len - 2));

    return DerivedCrv;
}

/******************************************************************************
* Return a new Bspline curve, equal to the integral of the given Bspline crv. *
* The given Bspline curve should be nonrational.			      *
*									      *
* 	      l		   l		   l	   l+1			      *
*   /	     /-		   -	  /	   -   P    -			      *
*  |	    | \	    n	   \	 |  n	   \    i   \              n+1	      *
*  | C(t) = | / P  B (t) = / P   | B (t) = / -----  / ( t   - t ) B   (t) =   *
* / 	   /  -	 i  i	   -  i /   i	   - n + 1  -    j+n   j   j	      *
*            i=0          i=0             i=0     j=i+1			      *
*									      *
*        l+1 j-1							      *
*         -   -								      *
*     1   \   \			n+1					      *
* = ----- /   / P  ( t   - t ) B   (t)					      *
*   n + 1 -   -  i    j+n   j	j					      *
*        j=1 i=0							      *
*									      *
******************************************************************************/
CagdCrvStruct *BspCrvIntegrate(CagdCrvStruct *Crv)
{
    int i, j, k,
	n = Crv -> Order,
	Len = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdCrvStruct *IntCrv;
    CagdRType
	*Kv = Crv -> KnotVector;

    if (CAGD_IS_RATIONAL_CRV(Crv))
	FATAL_ERROR(CAGD_ERR_RATIONAL_NO_SUPPORT);

    IntCrv = BspCrvNew(Len + 1, n + 1, Crv -> PType);

    /* Copy the knot vector and duplicate the two end knots. */
    CAGD_GEN_COPY(&IntCrv -> KnotVector[1], Kv, sizeof(CagdRType) * (Len + n));
    IntCrv -> KnotVector[0] = Kv[0];
    IntCrv -> KnotVector[Len + n + 1] = Kv[Len + n - 1];
    Kv = IntCrv -> KnotVector;

    for (k = 1; k <= MaxCoord; k++) {
	CagdRType
	    *Points = Crv -> Points[k],
	    *IntPoints = IntCrv -> Points[k];

	for (j = 0; j < Len + 1; j++) {
	    IntPoints[j] = 0.0;
	    for (i = 0; i < j; i++)
	        IntPoints[j] += Points[i] * (Kv[i + n + 1] - Kv[i + 1]);
	    IntPoints[j] /= n;
	}
    }

    return IntCrv;
}

/******************************************************************************
* Return a new linear Bspline curve constructed from the given polyline.      *
******************************************************************************/
CagdCrvStruct *CnvrtPolyline2LinBsplineCrv(CagdPolylineStruct *Poly)
{
    int i,
	Length = Poly -> Length;
    CagdCrvStruct
	*Crv = BspCrvNew(Length, 2, CAGD_PT_E3_TYPE);
    CagdRType
	**Points = Crv -> Points;
    CagdPtStruct
	*Pts = Poly -> Polyline;

    BspKnotUniformOpen(Length, 2, Crv -> KnotVector);

    for (i = 0; i < Length; i++, Pts++) {
	Points[1][i] = Pts -> Pt[0];
	Points[2][i] = Pts -> Pt[1];
	Points[3][i] = Pts -> Pt[2];
    }

    return Crv;
}
