/******************************************************************************
* CagdSRev.c - Surface of revolution out of a given profile.		      *
*******************************************************************************
* Written by Gershon Elber, Mar. 91.					      *
******************************************************************************/

#include "cagd_loc.h"

static int CircKnotVector[12] = { 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4 };

/******************************************************************************
* Constructs a surface of revolution around the Z axis of the given profile   *
* curve. Resulting surface will be a Bspline surface, while input may be      *
* either a Bspline or a Bezier curve. Curve must be 3d (I.e. P3 or E3).	      *
******************************************************************************/
CagdSrfStruct *CagdSurfaceRev(CagdCrvStruct *Crv)
{
    int i, j, i9,
	Len = Crv -> Length;
    CagdPointType
	PType = Crv -> PType;
    CagdRType **SrfPoints,
	sin45 = sin(M_PI / 4.0),
	**CrvPoints = Crv -> Points;
    CagdSrfStruct
	*Srf = BspSrfNew(9, Len, 3, Crv -> Order, CAGD_PT_P3_TYPE);

    for (i = 0; i < 12; i++)
	Srf -> UKnotVector[i] = (CagdRType) CircKnotVector[i];

    switch (Crv -> GType) {
	case CAGD_CBEZIER_TYPE:
	    BspKnotUniformOpen(Len, Crv -> Order, Srf -> VKnotVector);
	    break;
	case CAGD_CBSPLINE_TYPE:
	    CAGD_GEN_COPY(Srf -> VKnotVector, Crv -> KnotVector,
			  sizeof(CagdRType) * (Len + Crv -> Order));
	    break;
	case CAGD_CPOWER_TYPE:
	    FATAL_ERROR(CAGD_ERR_POWER_NO_SUPPORT);
	    return NULL;
	default:
	    FATAL_ERROR(CAGD_ERR_UNDEF_CRV);
	    return NULL;
    }

    SrfPoints = Srf -> Points;

    /* For each control point in the original curve - generate 9 points that */
    /* Form a circle perpendicular to the Z axis.			     */
    for (i = i9 = 0; i < Len; i++, i9 += 9) {
	SrfPoints[W][i9] = 1.0;
	switch (PType) {
	    case CAGD_PT_P3_TYPE:
		SrfPoints[W][i9] = CrvPoints[W][i];
	    case CAGD_PT_E3_TYPE:
		SrfPoints[X][i9] = CrvPoints[X][i];
		SrfPoints[Y][i9] = CrvPoints[Y][i];
		SrfPoints[Z][i9] = CrvPoints[Z][i];
		break;
	    default:
		FATAL_ERROR(CAGD_ERR_UNSUPPORT_PT);
		break;
	}

	/* Last point is exactly the same as first one in circle - copy it.  */
	for (j = W; j <= Z; j++)
	    SrfPoints[j][i9 + 8] = SrfPoints[j][i9];

	/* The Z components are identical in all circle, while the XY        */
	/* components are rotated 45 degrees at a time:			     */
	for (j = 1; j < 8; j++) {
	    SrfPoints[W][i9 + j] = SrfPoints[W][i9];
	    SrfPoints[X][i9 + j] = SrfPoints[X][i9 + j - 1] * sin45 -
				   SrfPoints[Y][i9 + j - 1] * sin45;
	    SrfPoints[Y][i9 + j] = SrfPoints[X][i9 + j - 1] * sin45 +
				   SrfPoints[Y][i9 + j - 1] * sin45;
	    SrfPoints[Z][i9 + j] = SrfPoints[Z][i9];
	}

	/* And finally we need to compensate for the W's on every second pt. */
	for (j = 1; j < 8; j += 2) {
	    SrfPoints[W][i9 + j] *= sin45;
	    SrfPoints[Z][i9 + j] *= sin45;
	}
    }

    return Srf;
}
