/******************************************************************************
* AFD_Cube.c - Cubic Adaptive forward Differencing code.		      *
*									      *
* This file's code is based on the following cubic polynomial basis function  *
*									      *
*		      C2  2    C3  3					      *
* C(t) = C0 + C1 t + --- t  + --- t	,   Ci are the coefficients.	      *
*		      2	       3					      *
*									      *
* For more, see:							      *
*									      *
* 1. S. Chang, M. Shantz and R.Rocchetti. Rendering Cubic Curves and	      *
*    Surfaces with Integer Adaptive Forward Differencing. Computer Graphics,  *
*    Vol. 23, Num. 3, pp. 157-166, Siggraph Jul. 1989.			      *
* 2. M. Shantz and S. L. Lien. Shading Bicubic Patches. Computer Graphics,    *
*    Vol. 21, Num. 4, pp. 189-196, Siggraph Jul. 1987.			      *
* 3. M. Shantz and S. Chang. Rendering Trimmed NURBS with Adaptive Forward    *
*    Differencing. Computer Graphics, Vol. 22, Num. 4, pp. 189-198,	      *
*    Siggraph Aug. 1988.						      *
*******************************************************************************
* Written by Gershon Elber, Jan. 93.					      *
******************************************************************************/

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

/******************************************************************************
* Given four coefficents of a cubic Bezier curve, computes the four	      *
* coefficients of the cubic afd basis functions, in place.		      *
******************************************************************************/
void AfdCnvrtCubicBzrToAfd(CagdRType Coef[4])
{
    CagdRType AfdCoef[4];

    AfdCoef[0] = Coef[0];
    AfdCoef[1] = Coef[3] - Coef[0];
    AfdCoef[2] = 6.0 * Coef[3] - 12.0 * Coef[2] + 6.0 * Coef[1];
    AfdCoef[3] = 6.0 * Coef[3] - 18.0 * Coef[2] + 18.0 * Coef[1] - 6.0 * Coef[0];
    CAGD_GEN_COPY(Coef, AfdCoef, sizeof(CagdRType) * 4);
}

/******************************************************************************
* Given four coefficents of a cubic afd polynomial, apply the L ( half the    *
* step size ) n times to them, in place.				      *
* We basically precomputed L^n and apply it here once. Every instance of L    *
* half the domain and so L^n divides the domain by 2^n.			      *
******************************************************************************/
void AfdApplyLn(CagdRType Coef[4], int n)
{
    CagdRType AfdCoef[4];

    switch (n) {
	case 1:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 2.0 - Coef[2] / 8.0 + Coef[3] / 16.0;
	    AfdCoef[2] = Coef[2] / 4.0 - Coef[3] / 8.0;
	    AfdCoef[3] = Coef[3];
	    break;
	case 2:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 4.0 - Coef[2] * 3.0 / 32.0 +
						Coef[3] * 7.0 / 128.0;
	    AfdCoef[2] = Coef[2] / 16.0 - Coef[3] * 3.0 / 64.0;
	    AfdCoef[3] = Coef[3] / 64.0;
	    break;
	case 3:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 8.0 - Coef[2] * 7.0 / 128.0 +
						Coef[3] * 35.0 / 1024.0;
	    AfdCoef[2] = Coef[2] / 64 - Coef[3] * 7.0 / 512.0;
	    AfdCoef[3] = Coef[3] / 512.0;
	    break;
	case 4:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 16.0 - Coef[2] * 15.0 / 512.0 +
						Coef[3] * 155.0 / 8192.0;
	    AfdCoef[2] = Coef[2] / 256 - Coef[3] * 15.0 / 4096.0;
	    AfdCoef[3] = Coef[3] / 4096.0;
	    break;
	case 5:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 32.0 - Coef[2] * 31.0 / 2048.0 +
						Coef[3] * 651.0 / 65536.0;
	    AfdCoef[2] = Coef[2] / 1024 - Coef[3] * 31.0 / 32768.0;
	    AfdCoef[3] = Coef[3] / 262144.0;
	    break;
	case 6:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 64.0 - Coef[2] * 63.0 / 8192.0 +
						Coef[3] * 2667.0 / 524288.0;
	    AfdCoef[2] = Coef[2] / 4096.0 - Coef[3] * 63.0 / 262144.0;
	    AfdCoef[3] = Coef[3] / 262144.0;
	    break;
	case 7:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 128.0 - Coef[2] * 127.0 / 32768.0 +
						Coef[3] * 10795.0 / 4194304.0;
	    AfdCoef[2] = Coef[2] / 16384.0 - Coef[3] * 127.0 / 2097152.0;
	    AfdCoef[3] = Coef[3] / 2097152.0;
	    break;
	case 8:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 256.0 - Coef[2] * 255.0 / 131072.0 +
						Coef[3] * 43435.0 / 33554432.0;
	    AfdCoef[2] = Coef[2] / 65536.0 - Coef[3] * 255.0 / 16777216.0;
	    AfdCoef[3] = Coef[3] / 16777216.0;
	    break;
	case 9:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 512.0 - Coef[2] * 511.0 / 524288.0 +
					Coef[3] * 174251.0 / 268435456.0;
	    AfdCoef[2] = Coef[2] / 262144.0 - Coef[3] * 511.0 / 134217728.0;
	    AfdCoef[3] = Coef[3] / 134217728.0;
	    break;
	case 10:
	    AfdCoef[0] = Coef[0];
	    AfdCoef[1] = Coef[1] / 1024.0 - Coef[2] * 1023.0 / 2097152.0 +
					Coef[3] * 698027.0 / 2147483648.0;
	    AfdCoef[2] = Coef[2] / 1048576.0 - Coef[3] * 1023.0 / 1073741824.0;
	    AfdCoef[3] = Coef[3] / 1073741824.0;
	    break;
	default:
	    FATAL_ERROR(CAGD_ERR_OUT_OF_RANGE);
	    return;
    }
    CAGD_GEN_COPY(Coef, AfdCoef, sizeof(CagdRType) * 4);
}

/******************************************************************************
* Given four coefficents of a cubic afd polynomial, apply the E ( step by 1 ) *
* in place.								      *
******************************************************************************/
void AfdApplyEStep(CagdRType Coef[4])
{
    Coef[0] += Coef[1];
    Coef[1] += Coef[2];
    Coef[2] += Coef[3];
}

/******************************************************************************
* Given four coefficents of a cubic Bezier curve, computes the four	      *
* coefficients of the cubic afd basis functions and step along them to create *
* a piecewise polynomial approximating the curve.			      *
*   if NonAdaptive is TRUE then 2^Log2Step constant steps are taken, creating *
* 2^Log2Step + 1 points along the curve.				      *
*   Otherwise the full blown adaptive algorithm is used.		      *
******************************************************************************/
void AfdComputePolyline(CagdRType Coef[4], CagdRType *Poly, int Log2Step,
							CagdBType NonAdaptive)
{
    int i;
    int n = 1 << Log2Step;

    AfdCnvrtCubicBzrToAfd(Coef);
    AfdApplyLn(Coef, Log2Step);

    if (NonAdaptive) {
	for (i = 0; i <= n; i++) {
	    Poly[i] = Coef[0];
	    AfdApplyEStep(Coef);
	}
    }
    else
    {
	FATAL_ERROR(CAGD_ERR_AFD_NO_SUPPORT);
    }
}

/******************************************************************************
* Samples the curves at FineNess location equally spaced in the Bezier        *
* parametric domain [0..1]. If Cache is enabled, and FineNess is power of     *
* two, upto or equal to CacheFineNess, the cache is used, otherwise the       *
* points are evaluated manually for each of the samples.		      *
* Data is saved at the Points array of vectors (according to Curve PType),    *
* each vector is assumed to be allocated for FineNess CagdRType points.	      *
* Bezier curve must be cubic.						      *
******************************************************************************/
void AfdBzrCrvEvalToPolyline(CagdCrvStruct *Crv, int FineNess,
							CagdRType *Points[])
{
    CagdBType
	IsNotRational = !CAGD_IS_RATIONAL_CRV(Crv);
    int i, j,
	MaxCoord = CAGD_NUM_OF_PT_COORD(Crv -> PType);
    CagdRType
	**CtlPoints = Crv -> Points;

    if (Crv -> Order != 4)
	FATAL_ERROR(CAGD_ERR_CUBIC_EXPECTED);

    for (i = IsNotRational; i <= MaxCoord; i++) {
	CagdRType Coef[4];

	for (j = 0; j < 4; j++)
	    Coef[j] = CtlPoints[i][j];

	AfdComputePolyline(Coef, Points[j], FineNess, TRUE);
    }
}

#ifdef DEBUG_AFD

/* If this section is defined, this file can be compiled stand alone. */

void CagdFatalError(CagdFatalErrorType ErrID)
{
}

void main(int argc, char **argv)
{
    CagdRType
	Poly[1025],
	Coef[4] = { 0.0, 1.0, 2.0, 3.0 };
    int i,
	Log2Step = 6;

    while (argc >= 2) {
	if (argc >= 2 && strcmp( argv[1], "-s" ) == 0) {
	    Log2Step = atoi( argv[2] );
	    argc -= 2;
	    argv += 2;
	}
	else if (argc >= 2 && strcmp( argv[1], "-c" ) == 0) {
	    sscanf(argv[2], "%lf", &Coef[0]);
	    sscanf(argv[3], "%lf", &Coef[1]);
	    sscanf(argv[4], "%lf", &Coef[2]);
	    sscanf(argv[5], "%lf", &Coef[3]);
	    argc -= 5;
	    argv += 5;
	}
	else {
	    fprintf(stderr, "Wrong command line");
	    exit(1);
	}
    }

    printf("Steps = %d, Coef = %lf %lf %lf %lf\n",
	   Log2Step, Coef[0], Coef[1], Coef[2], Coef[3]);

    AfdComputePolyline(Coef, Poly, Log2Step, TRUE);
    for (i = 0; i <= (1 << Log2Step); i++)
	printf("%d = %lg\n", i, (double) Poly[i]);
}

#endif /* DEBUG_AFD */
