/******************************************************************************
* CBzr-Aux.c - Bezier curve auxilary routines.				      *
*******************************************************************************
* Written by Gershon Elber, Mar. 90.					      *
******************************************************************************/

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

/******************************************************************************
* Given a bezier 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 exact result of evaluating the curve at t using the      *
* recursive algorithm - the left resulting points are the left curve, and the *
* right resulting points are the right curve (left is below t).		      *
******************************************************************************/
CagdCrvStruct *BzrCrvSubdivAtParam(CagdCrvStruct *Crv, CagdRType t)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, j, l,
	k = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType
	t1 = 1.0 - t;
    CagdCrvStruct *LCrv, *RCrv;

    if (t < 0.0 || APX_EQ(t, 0.0) ||
	t > 1.0 || APX_EQ(t, 1.0))
	FATAL_ERROR(CAGD_ERR_T_NOT_IN_CRV);

    LCrv = BzrCrvNew(k, Crv -> PType);
    RCrv = BzrCrvNew(k, Crv -> PType);

    /* Copy Curve into RCrv, so we can apply the recursive algo. to it.      */
    for (i = 0; i < k; i++)
	for (j = IsNotRational; j <= MaxCoord; j++)
	    RCrv -> Points[j][i] = Crv -> Points[j][i];

    for (j = IsNotRational; j <= MaxCoord; j++)
	LCrv -> Points[j][0] = Crv -> Points[j][0];

    /* Apply the recursive algorithm to RCrv, and update LCrv with the	     */
    /* temporary results. Note we updated the first point of LCrv above.     */
    for (i = 1; i < k; i++) {
	for (l = 0; l < k - i; l++)
	    for (j = IsNotRational; j <= MaxCoord; j++)
		RCrv -> Points[j][l] = RCrv -> Points[j][l] * t1 +
					 RCrv -> Points[j][l+1] * t;
	/* Copy temporary result to LCrv: */
	for (j = IsNotRational; j <= MaxCoord; j++)
	    LCrv -> Points[j][i] = RCrv -> Points[j][0];
    }
    LCrv -> Pnext = RCrv;

    return LCrv;
}

/******************************************************************************
* 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 *BzrCrvDegreeRaiseN(CagdCrvStruct *Crv, int NewOrder)
{
    int i, j, RaisedOrder,
	Order = Crv -> Order,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdCrvStruct *RaisedCrv, *UnitCrv;

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

    UnitCrv = BzrCrvNew(RaisedOrder, CAGD_MAKE_PT_TYPE(FALSE, MaxCoord));
    for (i = 1; i <= MaxCoord; i++)
	for (j = 0; j < RaisedOrder; j++)
	    UnitCrv -> Points[i][j] = 1.0;

    RaisedCrv = BzrCrvMult(Crv, UnitCrv);

    CagdCrvFree(UnitCrv);

    return RaisedCrv;
}

/******************************************************************************
* Return a new curve, identical to the original but with one degree higher    *
* Let old control polygon be P(i), i = 0 to k-1, and Q(i) be new one then:    *
*		       i	    k-i					      *
* Q(0) = P(0), Q(i) = --- P(i-1) + (---) P(i), Q(k) = P(k-1).		      *
*		       k	     k					      *
******************************************************************************/
CagdCrvStruct *BzrCrvDegreeRaise(CagdCrvStruct *Crv)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, j,
	k = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdCrvStruct
	*RaisedCrv = BzrCrvNew(k + 1, Crv -> PType);

    for (j = IsNotRational; j <= MaxCoord; j++)			    /* Q(0). */
	RaisedCrv -> Points[j][0] = Crv -> Points[j][0];

    for (i = 1; i < k; i++)					    /* Q(i). */
	for (j = IsNotRational; j <= MaxCoord; j++)
	    RaisedCrv -> Points[j][i] =
		Crv -> Points[j][i-1] * (i / ((CagdRType) k)) +
		Crv -> Points[j][i] * ((k - i) / ((CagdRType) k));

    for (j = IsNotRational; j <= MaxCoord; j++)			    /* Q(k). */
	RaisedCrv -> Points[j][k] = Crv -> Points[j][k-1];

    return RaisedCrv;
}

/******************************************************************************
* Return a normalized vector, equal to the tangent to Crv at parameter t.     *
* Algorithm: pseudo subdivide Crv at t and using control point of subdivided  *
* curve find the tangent as the difference of the 2 end points.		      *
******************************************************************************/
CagdVecStruct *BzrCrvTangent(CagdCrvStruct *Crv, CagdRType t)
{
    static CagdVecStruct P2;
    CagdVecStruct P1, *T;
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, j, l,
	k = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType
	t1 = 1.0 - t;
    CagdCrvStruct *RCrv;

    if (APX_EQ(t, 0.0)) {
	/* Use Crv starting tangent direction. */
	CagdCoerceToE3(P1.Vec, Crv -> Points, 0, Crv -> PType);
	CagdCoerceToE3(P2.Vec, Crv -> Points, 1, Crv -> PType);
    }
    else if (APX_EQ(t, 1.0)) {
	/* Use Crv ending tangent direction. */
	CagdCoerceToE3(P1.Vec, Crv -> Points, k - 2, Crv -> PType);
	CagdCoerceToE3(P2.Vec, Crv -> Points, k - 1, Crv -> PType);
    }
    else {
	if (t < 0.0 || t > 1.0)
	    FATAL_ERROR(CAGD_ERR_T_NOT_IN_CRV);

	RCrv = BzrCrvNew(k, Crv -> PType);

	/* Copy Crv into RCrv, so we can apply the recursive algo. to it. */
	for (i = 0; i < k; i++)
	    for (j = IsNotRational; j <= MaxCoord; j++)
		RCrv -> Points[j][i] = Crv -> Points[j][i];

	/* Apply the recursive algorithm to RCrv. */
	for (i = 1; i < k; i++)
	    for (l = 0; l < k - i; l++)
		for (j = IsNotRational; j <= MaxCoord; j++)
		    RCrv -> Points[j][l] = RCrv -> Points[j][l] * t1 +
					   RCrv -> Points[j][l+1] * t;

	CagdCoerceToE3(P1.Vec, RCrv -> Points, 0, RCrv -> PType);
	CagdCoerceToE3(P2.Vec, RCrv -> Points, 1, RCrv -> PType);

	CagdCrvFree(RCrv);
    }

    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 = BzrCrvTangent(Crv, t < 0.5 ? 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: pseudo subdivide Crv at t and using 3 consecutive control point  *
* (p1, p2, p3) of subdivided curve find the bi-normal as the cross product of *
* the difference (p1 - p2) and (p2 - p3).				      *
*   Since a curve may have not BiNormal at inflection points or if the 3      *
* points are colinear, NULL will be returned at such cases.		      *
******************************************************************************/
CagdVecStruct *BzrCrvBiNormal(CagdCrvStruct *Crv, CagdRType t)
{
    static CagdVecStruct P3;
    CagdVecStruct P1, P2;
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, j, l,
	k = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType
	t1 = 1.0 - t;
    CagdCrvStruct *RCrv;

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

    if (APX_EQ(t, 0.0)) {
	/* Use Crv starting tangent direction. */
	CagdCoerceToE3(P1.Vec, Crv -> Points, 0, Crv -> PType);
	CagdCoerceToE3(P2.Vec, Crv -> Points, 1, Crv -> PType);
	CagdCoerceToE3(P3.Vec, Crv -> Points, 2, Crv -> PType);
    }
    else if (APX_EQ(t, 1.0)) {
	/* Use Crv ending tangent direction. */
	CagdCoerceToE3(P1.Vec, Crv -> Points, k - 3, Crv -> PType);
	CagdCoerceToE3(P2.Vec, Crv -> Points, k - 2, Crv -> PType);
	CagdCoerceToE3(P3.Vec, Crv -> Points, k - 1, Crv -> PType);
    }
    else {
	if (t < 0.0 || t > 1.0)
	    FATAL_ERROR(CAGD_ERR_T_NOT_IN_CRV);

	RCrv = BzrCrvNew(k, Crv -> PType);

	/* Copy Crv into RCrv, so we can apply the recursive algo. to it. */
	for (i = 0; i < k; i++)
	    for (j = IsNotRational; j <= MaxCoord; j++)
		RCrv -> Points[j][i] = Crv -> Points[j][i];

	/* Apply the recursive algorithm to RCrv. */
	for (i = 1; i < k; i++)
	    for (l = 0; l < k - i; l++)
		for (j = IsNotRational; j <= MaxCoord; j++)
		    RCrv -> Points[j][l] = RCrv -> Points[j][l] * t1 +
					   RCrv -> Points[j][l+1] * t;

	CagdCoerceToE3(P1.Vec, RCrv -> Points, 0, RCrv -> PType);
	CagdCoerceToE3(P2.Vec, RCrv -> Points, 1, RCrv -> PType);
	CagdCoerceToE3(P3.Vec, RCrv -> Points, 2, RCrv -> PType);

	CagdCrvFree(RCrv);
    }

    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 *BzrCrvNormal(CagdCrvStruct *Crv, CagdRType t)
{
    static CagdVecStruct N, *T, *B;

    T = BzrCrvTangent(Crv, t);
    B = BzrCrvBiNormal(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), i = 0 to k-2.				      *
******************************************************************************/
CagdCrvStruct *BzrCrvDerive(CagdCrvStruct *Crv)
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, j,
	k = Crv -> Length,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdCrvStruct *DerivedCrv;

    if (!IsNotRational)
	return BzrCrvDeriveRational(Crv);

    DerivedCrv = BzrCrvNew(MAX(1, k - 1), Crv -> PType);

    if (k >= 2) {
	for (i = 0; i < k - 1; i++)
	    for (j = IsNotRational; j <= MaxCoord; j++)
		DerivedCrv -> Points[j][i] =
		    (k - 1) * (Crv -> Points[j][i+1] - Crv -> Points[j][i]);
    }
    else {
	for (j = IsNotRational; j <= MaxCoord; j++)
	    DerivedCrv -> Points[j][0] = 0.0;
    }

    return DerivedCrv;
}

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

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

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

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

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

    return IntCrv;
}

/******************************************************************************
* Convert a Bezier curve into Bspline curve by adding an open knot vector.    *
******************************************************************************/
CagdCrvStruct *CnvrtBezier2BsplineCrv(CagdCrvStruct *Crv)
{
    CagdCrvStruct *BspCrv;

    if (Crv -> GType != CAGD_CBEZIER_TYPE) {
	FATAL_ERROR(CAGD_ERR_WRONG_CRV);
	return NULL;
    }

    BspCrv = CagdCrvCopy(Crv);

    BspCrv -> Order = BspCrv -> Length;
    BspCrv -> KnotVector = BspKnotUniformOpen(BspCrv -> Length,
					           BspCrv -> Order, NULL);
    BspCrv -> GType = CAGD_CBSPLINE_TYPE;
    return BspCrv;
}

/******************************************************************************
* Convert a Bspline curve into a set of Bezier curves by subdividing the      *
* Bspline curve at all its internal knots.				      *
*   Returned is a list of Bezier curves.				      *
******************************************************************************/
CagdCrvStruct *CnvrtBspline2BezierCrv(CagdCrvStruct *Crv)
{
    int i,
	Order = Crv -> Order,
	Length = Crv -> Length;
    CagdRType LastT,
	*KnotVector = Crv -> KnotVector;
    CagdCrvStruct
	*BezierCrvs = NULL,
	*OrigCrv = Crv;

    if (Crv -> GType != CAGD_CBSPLINE_TYPE) {
	FATAL_ERROR(CAGD_ERR_WRONG_CRV);
	return NULL;
    }

    for (i = Length - 1, LastT = KnotVector[Length]; i >= Order; i--) {
    	CagdRType
    	    t = KnotVector[i];
    	    
	if (!APX_EQ(LastT, t)) {
    	    CagdCrvStruct
		*Crvs = BspCrvSubdivAtParam(Crv, t);

    	    if (Crv != OrigCrv)
    	        CagdCrvFree(Crv);

    	    Crvs -> Pnext -> Pnext = BezierCrvs;
    	    BezierCrvs = Crvs -> Pnext;

    	    Crv = Crvs;
    	    Crv -> Pnext = NULL;

	    LastT = t;
    	}
    }

    if (Crv == OrigCrv) {
	/* No interior knots in this curve - just copy it: */
	BezierCrvs = CagdCrvCopy(Crv);
    }
    else {
    	Crv -> Pnext = BezierCrvs;
    	BezierCrvs = Crv;
    }

    for (Crv = BezierCrvs; Crv != NULL; Crv = Crv -> Pnext) {
    	Crv -> GType = CAGD_CBEZIER_TYPE;
	Crv -> Length = Crv -> Order;
	IritFree((VoidPtr) Crv -> KnotVector);
	Crv -> KnotVector = NULL;
    }

    return BezierCrvs;
}

